Skip to main content

Advanced Configuration

This guide covers complex scenarios such as custom user stores, high availability, and manual credential validation.

1. Class-Based User Store

For complex applications, you may prefer to implement IPrimusAuthUserStore as a class rather than using the inline Lambda configuration.

using PrimusSaaS.Identity.Broker;
using System.Security.Claims;

public class AppUserStore : IPrimusAuthUserStore
{
private readonly AppDbContext _db;

public AppUserStore(AppDbContext db)
{
_db = db;
}

public async Task<PrimusAuthUser?> FindByEmailAsync(string email, CancellationToken ct = default)
{
var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == email, ct);
if (user == null) return null;

return new PrimusAuthUser
{
Id = user.Id.ToString(),
Email = user.Email,
Role = user.Role
};
}

// Optional: Only needed for SSO (Azure/Google) provisioning
public async Task<PrimusAuthUser?> AutoProvisionUserAsync(string email, string provider, ClaimsPrincipal principal, CancellationToken ct = default)
{
var newUser = new User { Email = email, Source = provider };
_db.Users.Add(newUser);
await _db.SaveChangesAsync(ct);

return new PrimusAuthUser { Id = newUser.Id.ToString(), Email = newUser.Email };
}

public Task<bool> IsLockedOutAsync(string email, CancellationToken ct) => Task.FromResult(false);
public Task RecordLoginFailureAsync(string email, CancellationToken ct) => Task.CompletedTask;
public Task ResetLoginFailureAsync(string email, CancellationToken ct) => Task.CompletedTask;
}

Registration:

builder.Services.AddScoped<IPrimusAuthUserStore, AppUserStore>();
builder.Services.AddPrimusAuthBroker(...);

2. Custom Credential Validator

The default IPrimusAuthCredentialValidator is required only if you want to use the /api/auth/login endpoint (Username/Password). If you only use Azure AD/Okta, you can skip this.

public class AppCredentialValidator : IPrimusAuthCredentialValidator
{
private readonly AppDbContext _db;
public AppCredentialValidator(AppDbContext db) => _db = db;

public async Task<PrimusAuthUser?> ValidateCredentialsAsync(string email, string password, CancellationToken ct)
{
// Your custom hashing logic here
var user = await _db.Users.FirstOrDefaultAsync(u => u.Email == email);
if (user != null && VerifyHash(password, user.PasswordHash))
{
return new PrimusAuthUser { Id = user.Id, Email = user.Email };
}
return null;
}
}

3. High Availability (Cluster/Kubernetes)

If you run multiple instances of your API, the session cookie encrypted by Server A must be readable by Server B.

Solution: Share the Data Protection Keys.

// Store keys in a shared folder (Azure Files, AWS S3, or Redis)
builder.Services.AddPrimusBrokerDataProtection("PrimusApp", new DirectoryInfo(@"/mnt/shared/keys"));