Astrolabe.OIDC
1.0.1
dotnet add package Astrolabe.OIDC --version 1.0.1
NuGet\Install-Package Astrolabe.OIDC -Version 1.0.1
<PackageReference Include="Astrolabe.OIDC" Version="1.0.1" />
<PackageVersion Include="Astrolabe.OIDC" Version="1.0.1" />
<PackageReference Include="Astrolabe.OIDC" />
paket add Astrolabe.OIDC --version 1.0.1
#r "nuget: Astrolabe.OIDC, 1.0.1"
#:package Astrolabe.OIDC@1.0.1
#addin nuget:?package=Astrolabe.OIDC&version=1.0.1
#tool nuget:?package=Astrolabe.OIDC&version=1.0.1
Astrolabe.OIDC
An OpenID Connect provider implementation for .NET 8+ applications using Minimal APIs. Part of the Astrolabe Apps library stack.
Overview
Astrolabe.OIDC allows your application to act as an OIDC identity provider. This enables SPAs using MSAL.js (or any OIDC-compliant client) to authenticate against your local user system instead of an external identity provider like Azure AD.
Installation
dotnet add package Astrolabe.OIDC
Features
- Authorization Code Flow with PKCE: Full OIDC provider compatible with MSAL.js
- Token Signing: RS256 JWT signing with configurable RSA keys
- Refresh Token Rotation: Automatic rotation on each refresh for security
- Pluggable Token Store: In-memory default, replaceable with database-backed stores
- Discovery & JWKS: Standard
.well-known/openid-configurationand JWKS endpoints
How It Works
The OIDC provider implements the Authorization Code flow with PKCE:
- The OIDC client (e.g. MSAL.js) redirects to
/authorize - The server validates the request and redirects to your SPA login page with an
oidc_request_idparameter - The user logs in via the existing local user login flow and obtains a JWT
- The SPA calls
/authorize/completewith the request ID and user JWT - The server returns a redirect URL containing an authorization code
- The OIDC client exchanges the code at
/tokenfor id_token, access_token, and refresh_token
Getting Started
Step 1: Generate an RSA Key
Generate an RSA private key for signing tokens:
openssl genrsa -out oidc-signing-key.pem 2048
Step 2: Create Your Endpoints Class
public class MyOidcEndpoints : OidcEndpoints
{
public MyOidcEndpoints(OidcProviderConfig config, OidcEndpointOptions? options = null)
: base(config, options) { }
}
Step 3: Implement IOidcUserClaimsProvider
This bridges your user system with OIDC claims. Given a user JWT from your existing login flow, return the OIDC claims:
public class MyClaimsProvider : IOidcUserClaimsProvider
{
private readonly AppDbContext _context;
public MyClaimsProvider(AppDbContext context)
{
_context = context;
}
public async Task<IEnumerable<Claim>?> GetClaimsFromUserToken(string userJwt)
{
// Decode and validate the JWT from your existing auth system
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadJwtToken(userJwt);
var userId = token.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
if (userId == null) return null;
var user = await _context.Users.FindAsync(Guid.Parse(userId));
if (user == null) return null;
return new[]
{
new Claim("sub", user.Id.ToString()),
new Claim("name", $"{user.FirstName} {user.LastName}"),
new Claim("email", user.Email),
new Claim("role", user.Role)
};
}
}
Step 4: Register in Program.cs
var oidcConfig = new OidcProviderConfig
{
Issuer = "https://myapp.example.com/oidc",
RsaKey = new OidcRsaKeyConfig { PemKey = builder.Configuration["Oidc:RsaPrivateKey"] },
Clients =
[
new OidcClientConfig
{
ClientId = "my-spa",
RedirectUris = ["https://myapp.example.com/auth/callback"],
PostLogoutRedirectUris = ["https://myapp.example.com/"]
}
],
LoginPageUrl = "/login"
};
builder.Services.AddOidcEndpoints<MyOidcEndpoints>(oidcConfig);
builder.Services.AddScoped<IOidcUserClaimsProvider, MyClaimsProvider>();
Step 5: Map the Endpoints
app.MapOidcEndpoints<MyOidcEndpoints>("/oidc");
Step 6: Configure the OIDC Client
For MSAL.js:
const msalConfig = {
auth: {
clientId: "my-spa",
authority: "https://myapp.example.com/oidc",
protocolMode: "OIDC",
},
};
Endpoints
All endpoints are mapped under the route group prefix (e.g. /oidc):
| Method | Path | Description |
|---|---|---|
| GET | /.well-known/openid-configuration |
OIDC discovery document |
| GET | /.well-known/keys |
JSON Web Key Set (JWKS) |
| GET | /authorize |
Authorization endpoint (redirects to login page) |
| POST | /authorize/complete |
Complete authorization after SPA login |
| POST | /token |
Token endpoint (auth code and refresh token grants) |
| GET | /logout |
End session (redirects to post-logout URI) |
Customization
Disabling Specific Endpoints
builder.Services.AddOidcEndpoints<MyOidcEndpoints>(oidcConfig, options =>
{
options.EnableEndSession = false;
});
Custom Token Store
By default, authorization codes, refresh tokens, and authorize requests are stored in memory using InMemoryOidcTokenStore. For production deployments with multiple server instances, implement IOidcTokenStore with a database-backed store:
public class DbOidcTokenStore : IOidcTokenStore
{
// Implement all methods using your database
}
// Register before AddOidcEndpoints (uses TryAddSingleton)
builder.Services.AddSingleton<IOidcTokenStore, DbOidcTokenStore>();
Configuration Options
OidcProviderConfig supports the following settings:
| Property | Default | Description |
|---|---|---|
Issuer |
(required) | The issuer URL, must match where endpoints are hosted |
RsaKey |
(required) | RSA key config (PemKey, Base64Key, optional KeyId) |
Clients |
[] |
List of registered OIDC clients |
LoginPageUrl |
"/login" |
SPA login page URL for the authorize redirect |
AccessTokenLifetimeSeconds |
3600 |
Access token lifetime (1 hour) |
IdTokenLifetimeSeconds |
3600 |
ID token lifetime (1 hour) |
AuthorizationCodeLifetimeSeconds |
300 |
Auth code lifetime (5 minutes) |
RefreshTokenLifetimeSeconds |
86400 |
Refresh token lifetime (24 hours) |
SPA Login Page Integration
Your SPA login page needs to handle the oidc_request_id query parameter. After a successful login:
// Detect OIDC flow
const params = new URLSearchParams(window.location.search);
const oidcRequestId = params.get("oidc_request_id");
if (oidcRequestId) {
// After successful login, complete the OIDC flow
const response = await fetch("/oidc/authorize/complete", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
oidcRequestId,
userToken: jwt, // JWT from the local user login
}),
});
const { redirectUrl } = await response.json();
window.location.href = redirectUrl;
}
Migration from Astrolabe.LocalUsers
If you were previously using the OIDC functionality from Astrolabe.LocalUsers, update your references:
- Add a reference to
Astrolabe.OIDC - Change
using Astrolabe.LocalUsers.Oidctousing Astrolabe.OIDC
License
MIT
Links
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 7.5.1)
- Microsoft.IdentityModel.Tokens (>= 7.5.1)
- System.IdentityModel.Tokens.Jwt (>= 7.5.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Astrolabe.OIDC:
| Package | Downloads |
|---|---|
|
Astrolabe.OIDC.EF
Entity Framework Core implementation of IOidcTokenStore for Astrolabe.OIDC. |
GitHub repositories
This package is not used by any popular GitHub repositories.