Passport
BizFirstGO SSO SDK
The Go.Essentials OIDC middleware — one method call sets up JWT validation, IDInfo resolution, token refresh, and Passport client integration for ASP.NET Core applications.
AddPassportAuthentication() Extension
The AddPassportAuthentication() extension is the primary integration point for ASP.NET Core applications. It wires up JWT bearer validation, JWKS auto-refresh, IDInfo population, and token refresh handling in a single call.
// Program.cs — minimal setup
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddPassportAuthentication(options =>
{
options.Authority = "https://passport.bizfirst.ai";
options.ClientId = builder.Configuration["Passport:ClientId"];
options.ClientSecret = builder.Configuration["Passport:ClientSecret"];
})
.AddPassportClient() // IPassportClient — for permission checks
.AddTokenRefresh(); // background refresh token management
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.Run();
PassportAuthenticationOptions
public class PassportAuthenticationOptions
{
/// <summary>Passport issuer URL. Used for OIDC discovery.</summary>
public required string Authority { get; set; }
/// <summary>Your OIDC client ID (registered in Passport Admin).</summary>
public required string ClientId { get; set; }
/// <summary>Client secret (null for public clients — use PKCE instead).</summary>
public string? ClientSecret { get; set; }
/// <summary>Scopes to request. Defaults: openid, profile, email, roles, tenant.</summary>
public string[] Scopes { get; set; } = ["openid", "profile", "email", "roles", "tenant"];
/// <summary>Clock skew tolerance for token expiry. Default: 60 seconds.</summary>
public TimeSpan ClockSkew { get; set; } = TimeSpan.FromSeconds(60);
/// <summary>Custom token validation parameters (merged with defaults).</summary>
public TokenValidationParameters? TokenValidationParameters { get; set; }
/// <summary>Interval for refreshing JWKS from Passport. Default: 10 minutes.</summary>
public TimeSpan JwksRefreshInterval { get; set; } = TimeSpan.FromMinutes(10);
/// <summary>
/// Called when token validation fails — return true to allow bypass (not recommended).
/// </summary>
public Func<TokenValidationFailedContext, Task<bool>>? OnTokenValidationFailed { get; set; }
}
IPassportClient
public interface IPassportClient
{
/// <summary>Check if a user has a permission (resource:action format).</summary>
Task<bool> CheckPermissionAsync(
string userId,
string permission,
string? resourceId = null,
CancellationToken ct = default);
/// <summary>Get all roles assigned to a user in the current tenant.</summary>
Task<IReadOnlyList<string>> GetUserRolesAsync(
string userId,
CancellationToken ct = default);
/// <summary>Exchange an authorization code for tokens.</summary>
Task<TokenResponse> ExchangeCodeAsync(
CodeExchangeRequest request,
CancellationToken ct = default);
/// <summary>Refresh an access token using a refresh token.</summary>
Task<TokenResponse> RefreshTokenAsync(
string refreshToken,
CancellationToken ct = default);
/// <summary>Revoke a token (access or refresh).</summary>
Task RevokeTokenAsync(
string token,
string tokenTypeHint = "refresh_token",
CancellationToken ct = default);
}
Full Configuration Example
// Program.cs — production configuration
builder.Services.AddPassportAuthentication(options =>
{
options.Authority = builder.Configuration["Passport:Authority"];
options.ClientId = builder.Configuration["Passport:ClientId"];
options.ClientSecret = builder.Configuration["Passport:ClientSecret"];
options.Scopes = ["openid", "profile", "email", "roles", "tenant"];
options.ClockSkew = TimeSpan.FromSeconds(60);
options.JwksRefreshInterval = TimeSpan.FromMinutes(10);
options.OnTokenValidationFailed = ctx =>
{
// Log but don't bypass
Log.Warning("Token validation failed: {Reason}", ctx.Exception.Message);
return Task.FromResult(false); // false = reject
};
});
// Register authorization policies driven by Passport roles
builder.Services.AddAuthorization(opts =>
{
opts.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireClaim("tenant_id")
.Build();
opts.AddPolicy("Admin", p => p.RequireClaim("roles", "admin"));
opts.AddPolicy("Manager", p => p.RequireClaim("roles", "admin", "manager"));
opts.AddPolicy("CanExecute", p => p.RequireClaim("roles", "admin", "manager", "executor"));
});
Middleware Pipeline Order
// Correct middleware order in Program.cs
var app = builder.Build();
app.UseHttpsRedirection();
// Security headers
app.UseHsts();
// Authentication MUST come before Authorization
app.UseAuthentication(); // validates JWT, populates ClaimsPrincipal
app.UseAuthorization(); // checks [Authorize] attributes
// IDInfo is available in all downstream middleware after here
app.MapControllers();
app.Run();
What AddPassportAuthentication() Sets Up For You
- JWT Bearer scheme with RS256 signature validation
- JWKS auto-fetch and refresh from the Passport discovery document
- Issuer and audience validation
- Clock skew tolerance
- IPassportTokenValidator registered in DI
- IPassportIdentityResolver registered in DI
- HttpContext.GetIDInfo() extension method
- IPassportClient HTTP client with auth header injection