Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/bitwarden/server/llms.txt

Use this file to discover all available pages before exploring further.

The Events service collects and persists user and organization events for audit logging, compliance reporting, and security monitoring.

Overview

The Events service provides:
  • Event Collection: Client-side event submission endpoint
  • Event Storage: Persisting events to database and Azure Table Storage
  • Event Types: User actions, cipher operations, organization activities
  • Compliance Support: Audit trails for regulatory requirements
  • Security Monitoring: Track suspicious activities and access patterns

Architecture

Configuration

From src/Events/Startup.cs:26:
Service Configuration
public void ConfigureServices(IServiceCollection services)
{
    // Settings
    var globalSettings = services.AddGlobalSettingsServices(Configuration, Environment);
    
    // Data Protection
    services.AddCustomDataProtectionServices(Environment, globalSettings);
    
    // Repositories
    services.AddDatabaseRepositories(globalSettings);
    
    // Context
    services.AddScoped<ICurrentContext, CurrentContext>();
    
    // Authentication
    services.AddIdentityAuthenticationServices(globalSettings, Environment, config =>
    {
        config.AddPolicy("Application", policy =>
        {
            policy.RequireAuthenticatedUser();
            policy.RequireClaim(JwtClaimTypes.AuthenticationMethod, "Application", "external");
            policy.RequireClaim(JwtClaimTypes.Scope, ApiScopes.Api);
        });
    });
    
    // Event Services
    var usingServiceBusAppCache = CoreHelpers.SettingHasValue(
        globalSettings.ServiceBus.ConnectionString) &&
        CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ApplicationCacheTopicName);
    
    services.AddScoped<IApplicationCacheService, FeatureRoutedCacheService>();
    
    if (usingServiceBusAppCache)
    {
        services.AddSingleton<IVCurrentInMemoryApplicationCacheService, 
            InMemoryServiceBusApplicationCacheService>();
    }
    else
    {
        services.AddSingleton<IVCurrentInMemoryApplicationCacheService, 
            InMemoryApplicationCacheService>();
    }
    
    services.AddEventWriteServices(globalSettings);
    services.AddScoped<IEventService, EventService>();
    
    // Event integrations
    services.AddDistributedCache(globalSettings);
    services.AddRabbitMqListeners(globalSettings);
}

Event Collection

Collect Endpoint

From src/Events/Controllers/CollectController.cs:16:
Collect Controller
[Route("collect")]
[Authorize("Application")]
public class CollectController : Controller
{
    [HttpPost]
    public async Task<IActionResult> Post([FromBody] IEnumerable<EventModel> model)
    {
        if (model == null || !model.Any())
        {
            return new BadRequestResult();
        }
        
        foreach (var eventModel in model)
        {
            switch (eventModel.Type)
            {
                case EventType.User_ClientExportedVault:
                    await _eventService.LogUserEventAsync(
                        _currentContext.UserId.Value, 
                        eventModel.Type, 
                        eventModel.Date);
                    break;
                    
                case EventType.Cipher_ClientViewed:
                case EventType.Cipher_ClientAutofilled:
                case EventType.Cipher_ClientCopiedPassword:
                    // Validate cipher access and log event
                    var cipher = await _cipherRepository.GetByIdAsync(
                        eventModel.CipherId.Value,
                        _currentContext.UserId.Value);
                    
                    if (cipher != null)
                    {
                        cipherEvents.Add(new Tuple<Cipher, EventType, DateTime?>(
                            cipher, eventModel.Type, eventModel.Date));
                    }
                    break;
                    
                case EventType.Organization_ClientExportedVault:
                    var organization = await _organizationRepository.GetByIdAsync(
                        eventModel.OrganizationId.Value);
                    
                    if (organization != null)
                    {
                        await _eventService.LogOrganizationEventAsync(
                            organization, eventModel.Type, eventModel.Date);
                    }
                    break;
            }
        }
        
        // Batch process cipher events
        if (cipherEvents.Any())
        {
            foreach (var eventsBatch in cipherEvents.Chunk(50))
            {
                await _eventService.LogCipherEventsAsync(eventsBatch);
            }
        }
        
        return new OkResult();
    }
}
Endpoint: POST /collect Authentication: Required (Bearer token) Request Body:
[
  {
    "type": 1000,
    "cipherId": "guid",
    "date": "2024-03-10T12:00:00Z"
  },
  {
    "type": 1100,
    "organizationId": "guid",
    "date": "2024-03-10T12:01:00Z"
  }
]

Event Types

User Events

Login/Logout

User authentication events

Vault Export

Vault data export operations

Settings Changes

User profile and settings modifications

2FA Events

Two-factor authentication changes

Cipher Events

From src/Events/Controllers/CollectController.cs:80:
Cipher Event Types
EventType.Cipher_ClientViewed
EventType.Cipher_ClientAutofilled
EventType.Cipher_ClientCopiedPassword
EventType.Cipher_ClientCopiedHiddenField
EventType.Cipher_ClientCopiedCardCode
EventType.Cipher_ClientToggledPasswordVisible
EventType.Cipher_ClientToggledCardCodeVisible
EventType.Cipher_ClientToggledHiddenFieldVisible
These events track vault item interactions:
  • Viewing credentials
  • Auto-filling forms
  • Copying sensitive data
  • Revealing hidden fields

Organization Events

Organization Event Types
EventType.Organization_ClientExportedVault
EventType.Organization_ItemOrganization_Accepted
EventType.Organization_ItemOrganization_Declined
EventType.Organization_AutoConfirmEnabled_Admin
EventType.Organization_AutoConfirmDisabled_Admin

Administrative Events

  • User invitations
  • Policy changes
  • Collection modifications
  • Group management
  • Permission changes

Event Validation

The service validates event submissions:
1

Authentication

Verify user is authenticated via Bearer token
2

Authorization

Check user has access to referenced resources (ciphers, organizations)
3

Batching

Group cipher events for efficient processing (50 per batch)
4

Storage

Persist events to database and Azure Table Storage

Application Cache

From src/Events/Startup.cs:56: The Events service uses application cache for organization abilities:
Cache Configuration
services.AddScoped<IApplicationCacheService, FeatureRoutedCacheService>();

if (usingServiceBusAppCache)
{
    services.AddSingleton<IVCurrentInMemoryApplicationCacheService, 
        InMemoryServiceBusApplicationCacheService>();
}
else
{
    services.AddSingleton<IVCurrentInMemoryApplicationCacheService, 
        InMemoryApplicationCacheService>();
}
Cache synchronization via:
  • Azure Service Bus: Multi-instance deployments
  • In-Memory: Single instance deployments
From src/Events/Startup.cs:80:
Cache Hosted Service
if (usingServiceBusAppCache)
{
    services.AddHostedService<Core.HostedServices.ApplicationCacheHostedService>();
}

Event Integrations

The service supports event-driven integrations via RabbitMQ: From src/Events/Startup.cs:86:
Event Integrations
services.AddDistributedCache(globalSettings);
services.AddRabbitMqListeners(globalSettings);
Integrations include:
  • Slack: Organization event notifications
  • Microsoft Teams: Webhook notifications
  • Custom Webhooks: User-defined integrations

Middleware Pipeline

From src/Events/Startup.cs:90:
Request Pipeline
public void Configure(IApplicationBuilder app)
{
    // Security headers
    app.UseMiddleware<SecurityHeadersMiddleware>();
    
    // Forwarded headers (self-hosted)
    if (globalSettings.SelfHosted)
    {
        app.UseForwardedHeaders(globalSettings);
    }
    
    // Default middleware
    app.UseDefaultMiddleware(env, globalSettings);
    
    // Routing
    app.UseRouting();
    
    // CORS
    app.UseCors(policy => policy
        .SetIsOriginAllowed(o => CoreHelpers.IsCorsOriginAllowed(o, globalSettings))
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials());
    
    // Authentication & Authorization
    app.UseAuthentication();
    app.UseAuthorization();
    
    // Current context
    app.UseMiddleware<CurrentContextMiddleware>();
    
    // Controllers
    app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
}

Event Storage

Events are stored in two locations:

SQL Database

Primary event storage with relational queries:
Event Table
CREATE TABLE [dbo].[Event] (
    [Id] UNIQUEIDENTIFIER NOT NULL,
    [Type] INT NOT NULL,
    [UserId] UNIQUEIDENTIFIER,
    [OrganizationId] UNIQUEIDENTIFIER,
    [CipherId] UNIQUEIDENTIFIER,
    [CollectionId] UNIQUEIDENTIFIER,
    [PolicyId] UNIQUEIDENTIFIER,
    [GroupId] UNIQUEIDENTIFIER,
    [OrganizationUserId] UNIQUEIDENTIFIER,
    [ActingUserId] UNIQUEIDENTIFIER,
    [DeviceType] SMALLINT,
    [IpAddress] VARCHAR(50),
    [Date] DATETIME2(7) NOT NULL,
    CONSTRAINT [PK_Event] PRIMARY KEY CLUSTERED ([Id] ASC)
);

Azure Table Storage

Scalable event storage for high-volume events:
Table Storage
PartitionKey: OrganizationId or UserId
RowKey: {Date:yyyyMMddHHmmss}_{EventId}
Benefits:
  • High throughput
  • Cost-effective storage
  • Efficient time-range queries

Client Integration

JavaScript Example

const events = [
  {
    type: 1000, // Cipher_ClientViewed
    cipherId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
    date: new Date().toISOString()
  },
  {
    type: 1003, // Cipher_ClientAutofilled
    cipherId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
    date: new Date().toISOString()
  }
];

await fetch('https://events.bitwarden.com/collect', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(events)
});

Mobile Example (C#)

var events = new List<EventModel>
{
    new EventModel
    {
        Type = EventType.Cipher_ClientViewed,
        CipherId = cipherId,
        Date = DateTime.UtcNow
    }
};

await _apiService.PostEventsCollectAsync(events);

Deployment

Environment Variables

GLOBALSETTINGS__SELFHOSTED=true
GLOBALSETTINGS__SQLSERVER__CONNECTIONSTRING=<connection>
GLOBALSETTINGS__EVENTS__CONNECTIONSTRING=<azure_table>
GLOBALSETTINGS__SERVICEBUS__CONNECTIONSTRING=<service_bus>
GLOBALSETTINGS__SERVICEBUS__APPLICATIONCACHETOPICNAME=<topic>

Docker

docker run -d \
  --name bitwarden-events \
  -p 5005:5000 \
  -e GLOBALSETTINGS__SelfHosted=true \
  -e GLOBALSETTINGS__SqlServer__ConnectionString="<connection>" \
  bitwarden/events:latest

Performance Considerations

Batch Processing

Cipher events processed in batches of 50 for efficiency

Async Operations

Non-blocking event persistence

Table Storage

Azure Table for high-volume event storage

Cache Sync

Service Bus for distributed cache invalidation

Security Considerations

Event data contains sensitive information. Ensure proper access controls and encryption.
  • Authentication: All requests require valid Bearer tokens
  • Authorization: Users can only log events for resources they access
  • Validation: Cipher and organization access validated before logging
  • IP Tracking: Client IP addresses captured for security analysis
  • Audit Trail: Events are immutable once stored

Compliance Support

The Events service supports compliance requirements:
  • GDPR: User activity tracking and data access logs
  • SOC 2: Audit trails for security controls
  • HIPAA: Access logging for protected health information
  • ISO 27001: Information security event management

Querying Events

Organization administrators can query events via the API service:
curl -X GET "https://api.bitwarden.com/organizations/{orgId}/events" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json"
Query parameters:
  • start: Start date (ISO 8601)
  • end: End date (ISO 8601)
  • actingUserId: Filter by user
  • itemId: Filter by resource (cipher, collection, etc.)