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, and connect/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 /mcp and 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. /mcp authenticates with the OpenIddict validation scheme; without AddCoreOAuth that scheme does not exist and /mcp requests fail. Presence of the OAuth config 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.cs in the Core repository.
  • The full flow is covered by the end-to-end integration test OAuthAuthorizationCodeFlowIntegrationTests (discovery → registration → consent → code → tokens → revocation).