Changelog
Modules

External OAuth

Separate preview package for GitHub and Google OAuth login with Identity cookie or JWT completion.

External OAuth (GitHub, Google) ships as a separate NuGet package: AuthEndpoints.External.OAuth. It is compose-only (not wired into the AddAuthEndpoints facade). Default completion issues an Identity application cookie; replace the completer for JWT.

Preview. Independent versioning from the core AuthEndpoints package — see the changelog. This package always references both GitHub and Google OAuth handler packages; per-provider NuGet splits may come later.

Install

dotnet add package AuthEndpoints --version 3.0.0-rc.3
dotnet add package AuthEndpoints.External.OAuth --version 3.0.0-preview.2

Requires ASP.NET Core Identity (UserManager / SignInManager) already configured — typically via AddAuthEndpoints. It does not call Identity management HTTP APIs; those remain optional alongside External.

AuthEndpoints.External.OAuth 3.0.0-preview.2+ requires AuthEndpoints 3.0.0-rc.3+ (public AddLoginRateLimiting used by OAuth DI).

Hosts must call UseRateLimiter() (included in UseAuthEndpoints). AddExternalAuthEndpoints registers the login rate-limit policy.

DI

using AuthEndpoints.External.OAuth;
using AuthEndpoints.External.OAuth.GitHub;
using AuthEndpoints.External.OAuth.Google;

builder.Services.AddExternalAuthEndpoints<AppUser>(o =>
{
    o.RequireVerifiedEmail = true;   // default
    o.AutoLinkByEmail = true;        // default; only when email is verified
    o.DefaultReturnUrl = "/";        // must be relative
    o.ErrorPath = "/auth/external/error";
    // o.AllowedReturnUrlOrigins.Add("https://app.example.com"); // optional absolute allowlist
})
.AddGitHub(o =>
{
    o.ClientId = builder.Configuration["Authentication:GitHub:ClientId"]!;
    o.ClientSecret = builder.Configuration["Authentication:GitHub:ClientSecret"]!;
})
.AddGoogle(o =>
{
    o.ClientId = builder.Configuration["Authentication:Google:ClientId"]!;
    o.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"]!;
});

ClientId/Secret are validated on startup. Missing values fail fast.

AddExternalAuthBuilder methods:

  • AddGitHub / AddGoogle — OAuth handlers (SignInScheme = Identity.External) + provider metadata
  • AddCompleter<T> — replace cookie completion (e.g. JwtExternalLoginCompleter<AppUser>)
  • AddProvider / AddProvider<T> — custom providers (see Custom providers)

JWT completion

Requires JWT services registered (AddJwtEndpoints):

builder.Services.AddExternalAuthEndpoints<AppUser>()
    .AddCompleter<JwtExternalLoginCompleter<AppUser>>()
    .AddGitHub(...);

After OAuth, a refresh cookie is written and the browser redirects to returnUrl. The SPA should obtain an access token via the JWT refresh flow (CSRF + refresh cookie) — the access token is not placed in the redirect URL.

Map

var external = app.MapGroup("/auth/external").WithTags("External");
external.MapGitHubAuthEndpoints<AppUser>();
external.MapGoogleAuthEndpoints<AppUser>();
external.MapExternalAccountEndpoints<AppUser>(); // link / unlink while signed in

Or map every registered sign-in provider:

app.MapGroup("/auth/external").MapExternalAuthEndpoints<AppUser>();

Routes

Relative to the group prefix (example /auth/external):

MethodPathAuthNotes
GET/login/githubChallenge; rate-limited; returnUrl
GET/login/github/callbackProvision + completer; errors redirect to ErrorPath
GET/login/googleChallenge; rate-limited
GET/login/google/callbackProvision + completer
GET/loginsyesList linked external logins
DELETE/logins/{loginProvider}/{providerKey}yesUnlink
GET/link/{scheme}yesStart link challenge
GET/link/{scheme}/callbackyesComplete link

IdP callback paths

ProviderMiddleware path
GitHub/signin-github
Google/signin-google

Security behavior

  • Verified email (default): create/link requires email_verified=true (Google/OIDC). GitHub marks email verified when the handler surfaces an email via user:email (verified primary).
  • Auto-link by email (default on): only when the email is verified; disable with AutoLinkByEmail = false.
  • EmailConfirmed on new users matches whether the provider email was verified.
  • External cookie is signed out after successful app sign-in / link.
  • returnUrl: relative local paths only by default; absolute URLs need AllowedReturnUrlOrigins.
  • Errors / cancel: browser redirects to ErrorPath?error=&error_description=; clients that prefer application/json over text/html get Problem details instead.

Options

PropertyDefaultNotes
SignInSchemenullCookie completer scheme override
IsPersistenttrueApplication cookie persistence
DefaultReturnUrl/Relative local path
RequireVerifiedEmailtrueBlock unverified provider emails
AutoLinkByEmailtrueLink existing user by verified email
AllowedReturnUrlOriginsemptyAbsolute http(s) origins allowlist
ErrorPath/auth/external/errorRelative path for error redirects

Custom providers

  1. Register an OAuth handler with SignInScheme = IdentityConstants.ExternalScheme.
  2. Implement IExternalAuthProvider (scheme + login/callback paths + endpoint name).
  3. builder.AddProvider<MyProvider>() then MapExternalAuthProvider<TUser>("MyScheme") or MapExternalAuthEndpoints.

Ensure the provider principal includes email + email_verified when RequireVerifiedEmail is true.

Configuration (Demo)

VariableProvider
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRETGitHub
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRETGoogle

Gotchas

  • Pipeline: authentication + UseRateLimiter() (via UseAuthEndpoints).
  • Host an error page at ErrorPath (or change the option).
  • Package split for GitHub-only / Google-only NuGets is deferred; both handler packages ship with this preview.