Skip to content

Identity.Pro

The Identity.Pro module extends the base identity system with advanced enterprise features: session management, admin impersonation, and OAuth2/OIDC integration.

User Entity

The framework's User entity supports the full identity lifecycle:

csharp
public class User : FullAuditedEntity<Guid>
{
    public Guid? TenantId { get; }
    public string UserName { get; }
    public string Email { get; }
    public bool EmailConfirmed { get; }
    public bool TwoFactorEnabled { get; }
    public bool LockoutEnabled { get; }
    public int FailedLoginAttempts { get; }
    public DateTime? LockoutEndUtc { get; }
    public bool IsActive { get; }
    public string? FirstName { get; }
    public string? LastName { get; }
    public string FullName { get; }

    // Navigation properties
    public ICollection<UserRole> UserRoles { get; }
    public ICollection<UserClaim> UserClaims { get; }
    public ICollection<UserLogin> UserLogins { get; }    // External logins
    public ICollection<UserToken> UserTokens { get; }
}

Session Management

Track and manage active user sessions:

csharp
public interface ISessionManager
{
    Task<UserSession> CreateSessionAsync(Guid userId, string ipAddress, string userAgent, CancellationToken ct = default);
    Task<IReadOnlyList<UserSession>> GetActiveSessionsAsync(Guid userId, CancellationToken ct = default);
    Task RevokeSessionAsync(Guid sessionId, CancellationToken ct = default);
    Task RevokeAllSessionsAsync(Guid userId, CancellationToken ct = default);
}

Listing active sessions

csharp
[HttpGet("sessions")]
[Authorize]
public async Task<IActionResult> GetMySessions(CancellationToken ct)
{
    var userId = User.GetUserId();
    var sessions = await _sessionManager.GetActiveSessionsAsync(userId, ct);
    return Ok(sessions);
}

[HttpDelete("sessions/{sessionId}")]
[Authorize]
public async Task<IActionResult> RevokeSession(Guid sessionId, CancellationToken ct)
{
    await _sessionManager.RevokeSessionAsync(sessionId, ct);
    return NoContent();
}

// Revoke all other sessions (keep current one)
[HttpPost("sessions/revoke-others")]
[Authorize]
public async Task<IActionResult> RevokeOtherSessions(CancellationToken ct)
{
    var userId = User.GetUserId();
    await _sessionManager.RevokeAllSessionsAsync(userId, ct);
    return Ok(new { message = "All other sessions have been revoked." });
}

Admin Impersonation

Allows administrators to impersonate other users for debugging and support:

csharp
public interface IImpersonationService
{
    Task<AuthResponseDto> ImpersonateAsync(Guid adminUserId, Guid targetUserId, CancellationToken ct = default);
    Task StopImpersonationAsync(CancellationToken ct = default);
    bool IsImpersonating { get; }
    Guid? OriginalUserId { get; }
}

Impersonation flow

csharp
[HttpPost("impersonate/{userId}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Impersonate(Guid userId, CancellationToken ct)
{
    var adminId = User.GetUserId();
    var response = await _impersonationService.ImpersonateAsync(adminId, userId, ct);

    // Returns a new JWT with the target user's claims
    // plus an "OriginalUserId" claim for audit purposes
    return Ok(response);
}

[HttpPost("stop-impersonation")]
[Authorize]
public async Task<IActionResult> StopImpersonation(CancellationToken ct)
{
    await _impersonationService.StopImpersonationAsync(ct);
    return Ok();
}

Impersonation is fully audited -- all actions taken while impersonating are logged with both the admin and target user IDs.

OAuth2/OIDC Integration

Configure external identity providers:

Azure AD

csharp
builder.Services.AddAuthentication()
    .AddOpenIdConnect("AzureAD", options =>
    {
        options.Authority = "https://login.microsoftonline.com/{tenant-id}/v2.0";
        options.ClientId = builder.Configuration["AzureAd:ClientId"];
        options.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
        options.ResponseType = "code";
        options.Scope.Add("openid");
        options.Scope.Add("profile");
        options.Scope.Add("email");
    });

Keycloak

csharp
builder.Services.AddAuthentication()
    .AddOpenIdConnect("Keycloak", options =>
    {
        options.Authority = "https://keycloak.example.com/realms/rivora";
        options.ClientId = "rivora-api";
        options.ClientSecret = builder.Configuration["Keycloak:ClientSecret"];
        options.ResponseType = "code";
        options.RequireHttpsMetadata = true;
    });

Claims Transformation

Map external claims to RIVORA Framework claims:

csharp
builder.Services.AddRvrClaimsTransformation(options =>
{
    options.MapClaim("preferred_username", ClaimTypes.Name);
    options.MapClaim("email", ClaimTypes.Email);
    options.MapClaim("groups", "Role");
    options.MapClaim("tenant_id", "TenantId");
});

Account Lockout (Anti Brute-Force)

The User entity includes built-in brute-force protection:

csharp
// Automatic lockout after failed attempts
user.IncrementFailedLogins();

if (user.FailedLoginAttempts >= maxAttempts)
{
    user.LockUntil(DateTime.UtcNow.AddMinutes(lockoutDurationMinutes));
}

// Check lockout
if (user.IsLockedOut())
{
    return Unauthorized("Account is locked. Try again later.");
}

// Reset on successful login
user.ResetFailedLogins();

Registration

csharp
builder.Services.AddRvrIdentityPro(options =>
{
    options.EnableSessionManagement = true;
    options.MaxConcurrentSessions = 5;
    options.EnableImpersonation = true;
    options.ImpersonationAuditLevel = AuditLevel.Full;
    options.LockoutMaxAttempts = 5;
    options.LockoutDurationMinutes = 15;
});

Released under the MIT License.