OAuth Setup
Summary
AddCoreOAuth() adds the OAuth 2.1 authorization server to your API host. One configured secret derives all key material; everything else works out of the box. Hosts that expose MCP also call AddCoreMcpServer() / UseCoreMcp().
Syntax
builder.Services
.AddCoreApiEvents(/* ... */)
.AddCoreOAuth(o => builder.Configuration.GetSection("OAuth").Bind(o));
// MCP hosts also register the MCP server:
builder.Services.AddCoreMcpServer(options =>
options.AppBaseUrl = builder.Configuration.GetSection("Client:BaseUrls").Get<string[]>()?.FirstOrDefault());
var app = builder.Build();
app.UseCoreMcp(); // MCP hosts: maps /mcp + /.well-known/oauth-protected-resource
await app.UseCoreApi();
app.Run();
CoreOAuthOptions
| Property | Required | Description |
|---|---|---|
Secret |
Yes | The single deployment-global OAuth secret. Token signing, token encryption, and the client-id HMAC key are all derived from it. Use 32+ random bytes (hex or base64 string). Rotating it invalidates issued tokens and client ids — clients simply re-register and reconnect. |
PublicOrigin |
Behind a proxy/tunnel | The public URL the app is served on (e.g. https://app.example.com). Set it whenever a proxy terminates HTTPS and forwards plain HTTP — a load balancer, ingress, or dev tunnel — otherwise the OAuth issuer and discovery URLs advertise the internal address and publicly minted tokens are rejected internally. Leave unset only when the app is reached directly. |
AddCoreOAuth throws at startup when Secret is missing — there is no insecure default.
Configuration
{
"OAuth": {
"Secret": "<32+ random bytes>"
// "PublicOrigin": "https://app.example.com" // set when behind a proxy or tunnel
},
"Client": {
"BaseUrls": [ "http://localhost:5186" ] // first entry hosts your consent page
}
}
The same keys as environment variables (containers):
OAuth__Secret=<32+ random bytes>
OAuth__PublicOrigin=https://app.example.com
Client__BaseUrls__0=http://localhost:5186
Client:BaseUrls[0]must be the origin of the app that hosts your consent page. Core redirects unauthenticated authorize requests to{Client:BaseUrls[0]}/oauth/consent— see Consent page.
What AddCoreOAuth registers
- The OpenIddict server on
connect/authorize,connect/token, andconnect/revoke, allowing the authorization-code and refresh-token flows with PKCE required. - OpenIddict local token validation — the scheme (
OpenIddict.Validation.AspNetCore) that authenticates OAuth access tokens on/mcpand API endpoints. - The stateless client manager that reconstructs client registrations from self-encoded client ids.
- The short-lived consent cookie scheme (
__Host-BeneviaOAuth). - The connected-apps service backing
api/oauth/connected-apps. - Forwarded-headers support pinned to
PublicOrigin.
Regular API endpoints accept both token formats through one composite scheme: Core JWTs and OAuth access tokens are routed to the right handler by token shape, so existing sign-in keeps working unchanged.
Notes
- Register OAuth in every environment that serves MCP.
/mcpauthenticates with the OpenIddict validation scheme; withoutAddCoreOAuththat scheme does not exist and/mcprequests fail. Presence of theOAuthconfig section is what matters when the host registers it conditionally. - OAuth endpoints require HTTPS. For local testing use the HTTPS profile or a tunnel; plain-HTTP authorize/token requests are rejected.
- Reference host:
Tests/Benevia.Core.TestAPI/Program.csin the Core repository. - The full flow is covered by the end-to-end integration test
OAuthAuthorizationCodeFlowIntegrationTests(discovery → registration → consent → code → tokens → revocation).