Noundry.Authnz

Complete OAuth 2.0 authentication and authorization for ASP.NET Core with multi-provider support, PKCE security, and built-in tag helpers.

v1.3.0 .NET 8.0 / 9.0 / 10.0 MIT License Stateless Architecture
# Install via NuGet
$ dotnet add package Noundry.Authnz

Quick Start

Get OAuth authentication running in your ASP.NET Core app in three steps.

1. Configure Program.cs

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();

2. Configure Providers in appsettings.json

{
 "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"
 }
 }
 }
}

3. Add Login Buttons

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>

Authentication Flow

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

Core API

IOAuthService

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);
}

OAuthUserInfo

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; }
}

Built-in Endpoints

The library automatically registers these HTTP endpoints via OAuthController.

GET /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.

GET /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.

GET /oauth/logout

Signs out the user and clears the authentication cookie. Accepts an optional redirectUri query parameter for post-logout redirect.

Supported Providers

Pre-configured defaults for major OAuth providers. Only ClientId and ClientSecret are required -- endpoints and scopes are auto-filled.

Google

Scopes: openid, profile, email

Callback: /oauth/callback/google

Microsoft

Scopes: openid, profile, email

Callback: /oauth/callback/microsoft

GitHub

Scopes: user:email

Callback: /oauth/callback/github

Apple

Scopes: name, email

Callback: /oauth/callback/apple

Facebook

Scopes: email, public_profile

Callback: /oauth/callback/facebook

Custom Providers

Any OAuth 2.0 / OpenID Connect provider (Okta, Auth0, etc.)

Full endpoint configuration required

OAuth Success Handling

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>();

Tag Helpers

Three built-in tag helpers for login, status display, and logout.

<noundry-oauth-login>

AttributeTypeDescription
providerstringProvider name (required unless show-all)
show-allboolShow buttons for all configured providers
button-textstringCustom button text
button-classstringCSS classes for button styling
redirect-uristringPost-login redirect destination

<noundry-oauth-status>

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>

<noundry-oauth-logout
 button-text="Sign Out"
 redirect-uri="/">
</noundry-oauth-logout>

Custom Provider Configuration

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
 );
});

Configuration Reference

PropertyDefaultDescription
DefaultRedirectUri/Post-login redirect
LoginPath/LoginLogin page path
LogoutPath/oauth/logoutLogout endpoint
AccessDeniedPath/AccessDeniedAccess denied page
RequireHttpsMetadatatrueEnforce HTTPS

Important Notes

Stateless Architecture

Do not use session state for authentication. Always use claims from cookie authentication. Access user data via User.FindFirst("claim_name")?.Value.

Login URL Pattern

The correct URL is /oauth/login/{provider}. Do not use /auth/ or /login/ prefixes.

GitHub Email Permission

For GitHub OAuth, you must enable email permissions in the GitHub OAuth App settings: Account permissions > Email addresses > Read-only.

Callback URLs

Register exact callback URLs with each provider: https://yourdomain.com/oauth/callback/{provider}

Related Packages