Substantive.AspNetCore.Authentication.OpenIdConnect 9.17.17-build20260128r1

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 = OAuthPresets.Google();

    // Optional: additional external providers
    options.Providers =
    [
        OAuthPresets.GitHub(),
        OAuthPresets.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 OAuthPresets
  • 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 = OAuthPresets.GoogleOAuth();
    var gitHub = OAuthPresets.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.
3
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.
11
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.
3
Substantive.Metapackage.Backends
A metapackage contains all Substantive backend building blocks.
3

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