Portal Community

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