Substantive.AspNetCore.Authentication.OpenIdConnect 9.17.24-build20260204r1

Substantive.AspNetCore.Authentication.OpenIdConnect is a reusable .NET library that provides secure-by-default authentication building blocks for Razor Pages applications and API application.

It simplifies configuring:

  • External OAuth / OpenID Connect identity providers (Google, GitHub, GitLab, LINE, Auth0, Okta, Keycloak, Azure AD, Azure AD B2C)
  • Secure defaults (Authorization Code flow, PKCE, no token persistence)
  • Strongly-typed options with validation and source-generated option validators
  • API token validation using OpenIddict (OAuth 2.0 introspection)

Installation

1. Add configuration to the appsettings.json

Add the required authentication configuration to appsettings.json.

  • AuthenticationOpenIdConnect is used for interactive sign-in (Razor Pages / web apps)
  • OpenIddictValidation is used for API token validation (recommended default for APIs)
{
  // API applications typically DO NOT need interactive sign-in
  // AuthenticationOpenIdConnect can be omitted unless the API also hosts UI endpoints
  "OpenIddictValidation": {
    "IssuerBaseAddress": "https://auth.example.com/",
    "Audiences": "api",
    "ClientId": "api-client",
    "ClientSecret": "api-client-secret"
  }
}

2. Load configuration and add dependencies to the program.cs

var builder = WebApplication.CreateBuilder(args);

// API-focused authentication using token validation
builder.Services
       .AddApiAudienceAuthentication(options =>
          builder.Configuration
                 .GetSection("OpenIddictValidation")
                 .Bind(options));

builder.Services.AddAuthorization();

builder.Services.AddControllers();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

Usage

Sample 1: API Application (Token-based authentication)

This is the recommended setup for API-only applications. The API validates access tokens issued by an external authorization server using OpenIddict.

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddApiAudienceAuthentication(options =>
        builder.Configuration
            .GetSection("OpenIddictValidation")
            .Bind(options));

builder.Services.AddAuthorization();
builder.Services.AddControllers();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

Controller example

[ApiController]
[Route("api/[controller]")]
public sealed class ValuesController : ControllerBase
{
    [HttpGet]
    [Authorize]
    public IActionResult Get()
        => Ok(new[] { "value1", "value2" });
}

Sample 2: Blazor Application (Interactive sign-in)

This setup is suitable for Blazor Server applications (or hybrid apps) that require interactive user sign-in using external identity providers.

Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddAuthenticationOpenIdConnect(options =>
        builder.Configuration
            .GetSection("AuthenticationOpenIdConnect")
            .Bind(options));

builder.Services.AddAuthorization();

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

Example configuration using presets

builder.Services.AddAuthenticationOpenIdConnect(options =>
{
    options.AuthenticationOptions.DefaultScheme = "Cookies";
    options.AuthenticationOptions.DefaultChallengeScheme = "Google";

    // Interactive sign-in using Google
    options.Google = OAuthOptionsPresets.Google();

    // Optional: additional external providers
    options.Providers =
    [
        OAuthOptionsPresets.GitHub(),
        OAuthOptionsPresets.Okta("dev-123456.okta.com")
    ];
});

Advanced Sample: Combine OAuth presets with loaded configuration

In real-world applications, it is common to combine secure OAuth presets with configuration-bound secrets and environment-specific values. This approach provides:

  • Secure, opinionated defaults from OAuthOptionsPresets
  • Environment-specific values (ClientId, ClientSecret, scopes) from configuration
  • Clean separation between what the provider is and how it is configured per environment

appsettings.json

{
  "AuthenticationOpenIdConnect": {
    "AuthenticationOptions": {
      "DefaultScheme": "Cookies",
      "DefaultChallengeScheme": "Google"
    },
    "Providers": {
      "Google": {
        "ClientId": "<google-client-id>",
        "ClientSecret": "<google-client-secret>",
        "Scope": [ "openid", "profile", "email" ]
      },
      "GitHub": {
        "ClientId": "<github-client-id>",
        "ClientSecret": "<github-client-secret>",
        "Scope": [ "read:user", "user:email" ]
      }
    }
  }
}

Program.cs

builder.Services.AddAuthenticationOpenIdConnect(options =>
{
    // Bind shared authentication options
    builder.Configuration
        .GetSection("AuthenticationOpenIdConnect")
        .Bind(options);

    // Start from secure presets
    var google = OAuthOptionsPresets.GoogleOAuth();
    var gitHub = OAuthOptionsPresets.GitHub();

    // Overlay environment-specific configuration
    builder.Configuration
        .GetSection("AuthenticationOpenIdConnect:Providers:Google")
        .Bind(google.OAuthOptions);

    builder.Configuration
        .GetSection("AuthenticationOpenIdConnect:Providers:GitHub")
        .Bind(gitHub.OAuthOptions);

    options.Providers =
    [
        google,
        gitHub
    ];
});

Why this pattern works well

  • ✅ Presets define secure defaults (PKCE, callbacks, endpoints)
  • ✅ Configuration controls secrets and scopes per environment
  • ✅ No secrets are embedded in source code
  • ✅ Easy to add/remove providers without rewriting logic
  • ✅ Works cleanly with ValidateOnStart

FAQs and Troubleshoots

Q: Why does the application fail on startup if a provider is misconfigured?

A: This library uses ValidateOnStart and explicit validation to ensure authentication is correctly configured. Failing fast prevents insecure or partially configured authentication from reaching production.

Q: Should I use AuthenticationOpenIdConnect in an API-only application?

A: No. API-only applications should use AddApiAudienceAuthentication with OpenIddict token validation. AuthenticationOpenIdConnect is intended for interactive sign-in scenarios such as Razor Pages or hybrid API + UI applications.

Q: Where should ClientId and ClientSecret values be stored?

A: Always store secrets in configuration sources such as environment variables, secret managers, or user-secrets. They are intentionally never included in presets or source code.

Showing the top 20 packages that depend on Substantive.AspNetCore.Authentication.OpenIdConnect.

Packages Downloads
Substantive.Foundation.Backends
A metapackage contains all Substantive backend building blocks.
4
Substantive.Foundation.Backends
A metapackage contains all Substantive backend building blocks.
6
Substantive.Foundation.Backends
A metapackage contains all Substantive backend building blocks.
7
Substantive.Foundation.Backends
A metapackage contains all Substantive backend building blocks.
8
Substantive.Foundation.Backends
A metapackage contains all Substantive backend building blocks.
9
Substantive.Foundation.Backends
A metapackage contains all Substantive backend building blocks.
14
Substantive.Foundation.Backends
A metapackage contains all Substantive backend building blocks.
16
Substantive.Metapackage.AspNetCoreServer
A metapackage contains all Substantive ASP.NET Core server-side packages.
4
Substantive.Metapackage.Backends
A metapackage contains all Substantive backend building blocks.
4

Version Downloads Last updated
9.17.28-build20260331R1 4 03/31/2026
9.17.27-build20260306R1 7 03/06/2026
9.17.26-build20260211R1 5 02/11/2026
9.17.25-build20260210R1 2 02/10/2026
9.17.24-build20260209R1 2 02/09/2026
9.17.24-build20260204R1 4 02/04/2026
9.17.23-build20260204R1 1 02/04/2026
9.17.22-build20260204R1 1 02/04/2026
9.17.21-build20260204R2 1 02/04/2026
9.17.21-build20260204R1 2 02/04/2026
9.17.20-build20260203R1 4 02/03/2026
9.17.19-build20260130R1 8 01/30/2026
9.17.18-build20260129R1 20 01/29/2026
9.17.17-build20260129R1 23 01/29/2026
9.17.17-build20260128R1 21 01/28/2026
9.17.16-build20260127R2 23 01/27/2026
9.17.16-build20260123R1 5 01/23/2026
9.17.15-build20260122R1 7 01/22/2026
9.17.14-build20260122R2 7 01/22/2026
9.17.14-build20260122R1 3 01/22/2026
9.17.14-build20260121R1 3 01/21/2026
9.17.13-build20260121R1 3 01/21/2026
9.17.12-build20260120R1 7 01/20/2026
9.17.11-build20260120R1 6 01/20/2026
9.17.10-build20260120R1 5 01/20/2026
9.17.9-build20260119R1 4 01/19/2026
9.17.8-build20260119R1 3 01/19/2026
9.17.7-build20260115R1 2 01/15/2026
9.17.6-build20260115R1 2 01/15/2026
9.17.5-build20260119R1 3 01/19/2026
9.17.5-build20260115R1 2 01/15/2026
9.17.4-build20260115R1 5 01/15/2026
9.17.3-build20260115R1 2 01/15/2026
9.17.2-build20260115R1 1 01/15/2026
9.17.2-build20260113R1 6 01/13/2026
9.17.1-build20260113R2 2 01/13/2026
9.17.1-build20251231R2 2 12/31/2025
9.17.1-build20251230R2 3 12/30/2025
9.17.1-build20251230R1 2 12/30/2025
9.17.0-build20251229R2 2 12/29/2025
9.17.0-build20251229R1 2 12/29/2025
9.17.0-build20251226R1 2 12/26/2025
9.17.0-build20251225R1 2 12/25/2025
9.16.0-build20251224R1 4 12/24/2025
9.15.3-build20251224R1 2 12/24/2025
9.15.2-build20251224R1 2 12/24/2025
9.15.1-build20251224R1 2 12/24/2025
9.15.0-build20251224R1 2 12/24/2025
9.14.4-build20251224R1 2 12/24/2025
9.14.3-build20251224R4 2 12/24/2025
9.14.2-build20251224R1 2 12/24/2025
9.14.1-build20251224R1 2 12/24/2025
9.14.0-build20251223R1 2 12/23/2025
9.3.0-build20251223R4 2 12/23/2025
9.3.0-build20251223R3 1 12/23/2025
9.3.0-build20251223R2 2 12/23/2025
9.3.0-build20251223R1 1 12/23/2025
9.3.0-build20251222R2 2 12/22/2025
9.3.0-build20251222R1 2 12/22/2025
9.2.0-build20251222R2 2 12/22/2025
9.2.0-build20251222R1 2 12/22/2025
9.1.0-build20251218R1 2 12/18/2025
9.1.0-build20251217R2 2 12/17/2025
9.1.0-build20251217R1 3 12/17/2025
9.1.0-build20251216R2 3 12/16/2025
9.1.0-build20251216R1 2 12/16/2025
9.1.0-build20251215R1 2 12/15/2025
9.1.0-build20251212R1 2 12/12/2025
9.1.0-build20251211R1 2 12/11/2025
9.1.0-build20251210R1 2 12/10/2025
9.1.0-build20251202R2 3 12/02/2025
9.1.0-build20251202R1 1 12/02/2025
9.0.5-build20251117R1 2 11/17/2025
9.0.5-build20251110R1 6 11/10/2025
9.0.5-build20251103R1 3 11/03/2025
9.0.5-build20251030R1 4 10/30/2025
9.0.5-build20251028R1 2 10/28/2025
9.0.5-build20251016R1 1 10/16/2025
9.0.5-build20250925R2 5 09/25/2025
9.0.5-build20250925R1 3 09/25/2025
9.0.5-build20250924R1 3 09/24/2025
9.0.5-build20250910R1 1 09/10/2025
9.0.5-build20250908R1 8 09/08/2025
9.0.5-build20250905R2 3 09/05/2025
9.0.5-build20250905R1 3 09/05/2025
9.0.5-build20250827R1 3 08/27/2025
9.0.5-build20250825R1 2 08/25/2025
9.0.5-build20250822R1 4 08/22/2025
9.0.5-build20250821R1 6 08/21/2025
9.0.4-build20250821R1 3 08/21/2025
9.0.3-build20250821R1 3 08/21/2025
9.0.2-build20250821R1 3 08/21/2025
9.0.1-build20250821R1 3 08/21/2025
9.0.0 3 08/16/2025
9.0.0-build20250821R3 3 08/21/2025
9.0.0-build20250821R2 3 08/21/2025
9.0.0-build20250821R1 3 08/21/2025
9.0.0-build20250818R2 3 08/18/2025
9.0.0-build20250818R1 3 08/18/2025
9.0.0-build20250815R1 3 08/15/2025
9.0.0-build20250814R1 3 08/14/2025
9.0.0-build20250722R3 5 07/22/2025
9.0.0-build20250714R3 8 07/14/2025
9.0.0-build20250714R2 4 07/14/2025
9.0.0-build20250714R1 2 07/14/2025
9.0.0-build20250710R1 5 07/10/2025
9.0.0-build20250707R2 11 07/07/2025
9.0.0-build20250707R1 4 07/07/2025
9.0.0-build20250629R1 9 06/29/2025
9.0.0-build20250626R2 9 06/26/2025
9.0.0-build20250626R1 4 06/26/2025
9.0.0-build20250623R1 10 06/23/2025
9.0.0-build20250621R1 14 06/21/2025
1.1.0-build20251224R1 2 12/24/2025