Session Strategies

Last updated

The logging package uses session strategies to identify users and group their API actions. Multiple strategies are available to support different authentication patterns.

Session Strategy Options

builder.Services.AddApiLogging(options =>
{
    // Choose primary strategy
    options.SessionStrategy = SessionStrategy.Hybrid;  // Default
    
    // Or use one of:
    // SessionStrategy.Jwt
    // SessionStrategy.HttpContext
    // SessionStrategy.Manual
});

Available Strategies

1. JWT Claims Strategy

Extracts user identity from JWT token claims. Best for APIs with token-based authentication.

options.SessionStrategy = SessionStrategy.Jwt;

// Configure which claims to look for
options.UserIdClaimTypes = new List<string>
{
    ClaimTypes.NameIdentifier,  // Standard .NET claim
    "sub",                       // OpenID Connect subject
    "user_id",                   // Custom claim
    ClaimTypes.Name              // Fallback
};

// Session ID from claims (optional)
options.SessionIdClaimTypes = new List<string> { "sid" };

2. HttpContext Strategy

Looks for user/session identity in headers, cookies, or HttpContext.Items. Good for API key authentication or custom identity schemes.

options.SessionStrategy = SessionStrategy.HttpContext;

// Allow identity from headers
options.AllowHeaderIdentity = true;
options.UserIdHeaderNames = new List<string> { "X-User-Id", "X-Api-Key" };
options.SessionIdHeaderNames = new List<string> { "X-Session-Id" };

// Allow identity from cookies
options.AllowCookieIdentity = true;
options.UserIdCookieNames = new List<string> { "UserId" };
options.SessionIdCookieNames = new List<string> { "SessionId" };

// Tenant from headers/cookies
options.TenantIdHeaderNames = new List<string> { "X-Tenant-Id" };
options.TenantIdCookieNames = new List<string> { "TenantId" };

3. Manual Strategy

Reads identity from HttpContext.Items. Use this when you set the identity programmatically.

options.SessionStrategy = SessionStrategy.Manual;

// In middleware or controller:
public class IdentityMiddleware
{
    public async Task InvokeAsync(HttpContext context)
    {
        var userId = await GetUserIdFromSomewhere();
        var sessionId = await GetSessionIdFromSomewhere();
        
        context.Items["ApiLogging_UserId"] = userId;
        context.Items["ApiLogging_SessionId"] = sessionId;
        context.Items["ApiLogging_TenantId"] = "tenant-123";
        
        await _next(context);
    }
}

4. Hybrid Strategy (Default)

Tries multiple strategies in order until one succeeds. This is the most flexible option.

options.SessionStrategy = SessionStrategy.Hybrid;

// Default order: JWT โ†’ HttpContext โ†’ Manual
// Customize the order:
options.SessionIdentificationOrder = new[]
{
    SessionIdentifierType.JwtClaims,
    SessionIdentifierType.HttpContext,
    SessionIdentifierType.Manual
};

Anonymous Sessions

Enable anonymous sessions for unauthenticated users:

options.AllowAnonymousSessions = true;
options.AnonymousUserIdPrefix = "anon-";

// Anonymous users get auto-generated IDs like:
// "anon-abc123def456"

Client-Provided Session IDs

Allow clients to provide their own session IDs via headers:

options.AllowClientProvidedSessionId = true;
options.SessionIdHeaderNames = new List<string> { "X-Session-Id" };

// Client sends: X-Session-Id: my-session-123

Session Validation

// Require MongoDB ObjectId format for session IDs
options.RequireObjectIdSessionIds = true;

// Allow session ID without user ID (headless scenarios)
options.AllowSessionIdWithoutUserId = true;

Session Timeouts

// Session becomes inactive after 30 minutes of no activity
options.SessionInactivityTimeoutMinutes = 30;

// Background service cleans up inactive sessions every hour
options.SessionCleanupIntervalMinutes = 60;

Session Caching

Session lookups are cached to reduce database queries:

options.SessionLookupCacheEnabled = true;    // Default: true
options.SessionLookupCacheMinutes = 2;       // Cache duration
options.SessionLookupCacheSizeLimit = 1000;  // 0 = unlimited

Multi-Tenant Configuration

// Configure tenant identification
options.TenantIdClaimType = "tenantId";  // JWT claim
options.TenantIdHeaderNames = new List<string> { "X-Tenant-Id" };
options.TenantIdCookieNames = new List<string> { "TenantId" };

Complete Configuration Example

builder.Services.AddApiLogging(options =>
{
    // Connection
    options.ConnectionString = "mongodb://localhost:27017";
    options.DatabaseName = "ApiLogs";
    
    // Session strategy
    options.SessionStrategy = SessionStrategy.Hybrid;
    options.SessionIdentificationOrder = new[]
    {
        SessionIdentifierType.JwtClaims,
        SessionIdentifierType.HttpContext
    };
    
    // JWT claims
    options.UserIdClaimTypes = new List<string> { "sub", "user_id" };
    options.SessionIdClaimTypes = new List<string> { "sid" };
    
    // Headers/cookies fallback
    options.AllowHeaderIdentity = true;
    options.UserIdHeaderNames = new List<string> { "X-User-Id" };
    options.SessionIdHeaderNames = new List<string> { "X-Session-Id" };
    
    // Anonymous support
    options.AllowAnonymousSessions = true;
    options.AnonymousUserIdPrefix = "guest-";
    
    // Timeouts
    options.SessionInactivityTimeoutMinutes = 60;
    options.SessionCleanupIntervalMinutes = 30;
    
    // Caching
    options.SessionLookupCacheEnabled = true;
    options.SessionLookupCacheMinutes = 5;
});

SessionIdentity Model

public class SessionIdentity
{
    public string? UserId { get; set; }
    public string? SessionId { get; set; }
    public string? TenantId { get; set; }
    public SessionIdentifierType Source { get; set; }
}

public enum SessionIdentifierType
{
    JwtClaims,
    HttpContext,
    Manual
}

Accessing Session Identity

The resolved session identity is stored in HttpContext.Items:

[HttpGet]
public IActionResult GetCurrentSession()
{
    if (HttpContext.Items.TryGetValue(
        ApiLoggingService.SessionIdentityItemKey, 
        out var obj) && obj is SessionIdentity identity)
    {
        return Ok(new
        {
            identity.UserId,
            identity.SessionId,
            identity.TenantId,
            Source = identity.Source.ToString()
        });
    }
    
    return NotFound();
}