Complete OAuth 2.0 authentication and authorization for ASP.NET Core with multi-provider support, PKCE security, and built-in tag helpers.
Get OAuth authentication running in your ASP.NET Core app in three steps.
using Noundry.Authnz.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllers();
builder.Services.AddNoundryOAuth(builder.Configuration);
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseNoundryOAuth(); // Must be after UseRouting
app.MapRazorPages();
app.MapControllers();
app.Run();
{
"OAuth": {
"DefaultRedirectUri": "/",
"LoginPath": "/Login",
"Providers": {
"google": {
"ClientId": "YOUR_CLIENT_ID.apps.googleusercontent.com",
"ClientSecret": "YOUR_CLIENT_SECRET"
},
"github": {
"ClientId": "YOUR_CLIENT_ID",
"ClientSecret": "YOUR_CLIENT_SECRET"
}
}
}
}
Register the tag helper in _ViewImports.cshtml:
@addTagHelper *, Noundry.Authnz
Then use the tag helper in your views:
<!-- Show all configured providers -->
<noundry-oauth-login show-all="true"></noundry-oauth-login>
<!-- Single provider -->
<noundry-oauth-login provider="google"></noundry-oauth-login>
Authnz uses a stateless, cookie-based authentication flow with PKCE for enhanced security.
User Click --> /oauth/login/{provider} --> Generate State --> Redirect to OAuth Provider
|
User Authorizes <-- OAuth Provider Authorization Page <-------------+
|
/oauth/callback/{provider} <-- OAuth Provider Redirect
|
Validate State --> Exchange Code for Token --> Fetch User Info --> Create Claims --> Set Cookie --> Redirect
The primary service for OAuth flow orchestration. Inject via dependency injection.
public interface IOAuthService
{
// Generate OAuth authorization URL for a provider
string GenerateAuthorizationUrl(string provider, string? state, string? redirectUri, string? codeVerifier);
// Handle the OAuth callback - exchanges code for token and fetches user info
Task<OAuthUserInfo?> HandleCallbackAsync(string provider, string code, string? codeVerifier);
// Exchange authorization code for access token
Task<string?> ExchangeCodeForTokenAsync(string provider, string code, string? codeVerifier);
// Fetch user information using access token
Task<OAuthUserInfo?> GetUserInfoAsync(string provider, string accessToken);
// Check if a provider is configured
bool IsProviderConfigured(string provider);
// Get all configured provider names
IEnumerable<string> GetConfiguredProviders();
// Generate PKCE code verifier
string? GenerateCodeVerifier(string provider);
}
Normalized user data returned after successful authentication.
public class OAuthUserInfo
{
public string Id { get; set; } // Provider-specific user ID
public string Email { get; set; } // User email
public string Name { get; set; } // Display name
public string FirstName { get; set; } // Given name
public string LastName { get; set; } // Family name
public string AvatarUrl { get; set; } // Profile picture URL
public string Provider { get; set; } // Provider name (e.g., "google")
public Dictionary<string, object> AdditionalClaims { get; set; }
}
The library automatically registers these HTTP endpoints via OAuthController.
/oauth/login/{provider}
Initiates the OAuth flow. Generates encrypted state, builds the authorization URL, and redirects the user to the OAuth provider. Accepts an optional redirectUri query parameter.
/oauth/callback/{provider}
Handles the OAuth provider callback. Validates state, exchanges the authorization code for a token, fetches user info, creates an authentication cookie with claims, and redirects to the stored URI.
/oauth/logout
Signs out the user and clears the authentication cookie. Accepts an optional redirectUri query parameter for post-logout redirect.
Pre-configured defaults for major OAuth providers. Only ClientId and ClientSecret are required -- endpoints and scopes are auto-filled.
Scopes: openid, profile, email
Callback: /oauth/callback/google
Scopes: openid, profile, email
Callback: /oauth/callback/microsoft
Scopes: user:email
Callback: /oauth/callback/github
Scopes: name, email
Callback: /oauth/callback/apple
Scopes: email, public_profile
Callback: /oauth/callback/facebook
Any OAuth 2.0 / OpenID Connect provider (Okta, Auth0, etc.)
Full endpoint configuration required
Implement IOAuthSuccessHandler to hook into the authentication flow for user management, adding custom claims, or controlling redirects.
public class MyOAuthHandler : IOAuthSuccessHandler
{
public async Task<OAuthSuccessResult?> HandleAsync(OAuthSuccessContext context)
{
var userService = context.GetRequiredService<IUserService>();
// Find or create user in your database
var user = await userService.FindOrCreateFromOAuthAsync(
providerId: context.UserInfo.Id,
provider: context.UserInfo.Provider,
email: context.UserInfo.Email,
name: context.UserInfo.Name
);
// Return additional claims for the cookie
return new OAuthSuccessResult
{
AdditionalClaims = new List<Claim>
{
new("app_user_id", user.Id.ToString()),
new(ClaimTypes.Role, user.Role)
},
OverrideRedirectUri = "/dashboard"
};
}
}
// Register in Program.cs
builder.Services.AddScoped<IOAuthSuccessHandler, MyOAuthHandler>();
Three built-in tag helpers for login, status display, and logout.
| Attribute | Type | Description |
|---|---|---|
| provider | string | Provider name (required unless show-all) |
| show-all | bool | Show buttons for all configured providers |
| button-text | string | Custom button text |
| button-class | string | CSS classes for button styling |
| redirect-uri | string | Post-login redirect destination |
Displays user avatar, name, and email when authenticated, or a "Not signed in" message for anonymous users.
<noundry-oauth-status
show-avatar="true"
show-name="true"
show-email="true">
</noundry-oauth-status>
<noundry-oauth-logout
button-text="Sign Out"
redirect-uri="/">
</noundry-oauth-logout>
Connect to any OAuth 2.0 provider by supplying full endpoint configuration.
builder.Services.AddNoundryOAuth(builder.Configuration, options =>
{
options.ConfigureCustomOAuthProvider(
providerName: "okta",
clientId: "your-client-id",
clientSecret: "your-client-secret",
authorizationEndpoint: "https://your-domain.okta.com/oauth2/v1/authorize",
tokenEndpoint: "https://your-domain.okta.com/oauth2/v1/token",
userInfoEndpoint: "https://your-domain.okta.com/oauth2/v1/userinfo",
scopes: new List<string> { "openid", "profile", "email" },
usePkce: true
);
});
| Property | Default | Description |
|---|---|---|
| DefaultRedirectUri | / | Post-login redirect |
| LoginPath | /Login | Login page path |
| LogoutPath | /oauth/logout | Logout endpoint |
| AccessDeniedPath | /AccessDenied | Access denied page |
| RequireHttpsMetadata | true | Enforce HTTPS |
Do not use session state for authentication. Always use claims from cookie authentication. Access user data via User.FindFirst("claim_name")?.Value.
The correct URL is /oauth/login/{provider}. Do not use /auth/ or /login/ prefixes.
For GitHub OAuth, you must enable email permissions in the GitHub OAuth App settings: Account permissions > Email addresses > Read-only.
Register exact callback URLs with each provider: https://yourdomain.com/oauth/callback/{provider}