MillerByte.Logging.Api

Session Strategies

Last updated: 1/22/2026

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();
}