Skip to content
Slicekit

Backend guides

Adding an OAuth provider

Register an OAuth provider for sign-in, account linking and re-authentication.

View .md
On this page

OAuth providers share the generic sign-in, linking and re-authentication endpoints. Start with the live IOAuthProvider interface and implementations in api/src/Slicekit.Api/Auth/OAuth/: GitHub is the existing non-OIDC example, and Google includes ID-token verification. Use those files rather than copying an incomplete provider skeleton.

Settings and discovery

Add the provider to api/src/Slicekit.Core/Settings/OAuthSettings.cs using OAuthProviderSettings, with validation for enabled providers. Committed settings contain placeholders; deployment supplies the credentials and redirect URI. Follow the settings pattern for environment overrides.

Register the implementation in api/src/Slicekit.Api/Configuration/Auth.cs under the global OAuth flag and the provider’s own flag. Add its lowercase ID to FeaturesEndpoint in api/src/Slicekit.Api/Endpoints/v1/Auth/ as well. Registration in DI alone does not advertise a new provider to the frontend.

For a Discord settings property, the environment prefix is OAuth__Discord__, followed by Enabled, ClientId, ClientSecret or RedirectUri. Document these overrides in .env.prod.example and supply them to the API in your deployment configuration.

Identity contracts

The canonical provider name is persisted with external logins. Do not rename it after use. The factory accepts route IDs without regard to case, but the stored identity must remain stable. Use the provider’s opaque or numeric account ID as ExternalId, rather than an email address that can change.

OIDC providers must validate ID tokens, including the expected subject and email checks, through OidcIdTokenValidator as the Google provider does. Keep the generic endpoints’ state and PKCE handling when adding a provider; do not introduce another callback or session mechanism.

Frontend entry and icon

Add the lowercase ID and display label to frontend/src/features/oauth/types.ts. Add a local brand icon to frontend/src/features/oauth/components/OAuthButtons.tsx, following GithubIcon and the inline Google icon. The map uses React’s ComponentType<{ className?: string }>; another icon package is not required. Keep the icon decorative with aria-hidden and label the button with the provider name.

Sign-in and account-linking choices are filtered by the enabled-provider response. Keep that response, the frontend ID and the provider factory lookup consistent.

Callback and registration policy

Allowlist the exact frontend callback URI in the provider’s application settings. A local Discord example is http://localhost:3003/auth/oauth/discord/callback. The provider redirects there with a GET; the SPA then posts code and state to the API. An API callback accepting only POST cannot serve as the browser redirect destination.

The same SPA callback completes account linking and step-up re-authentication. Preserve the pending operation so each redirect returns to the intended flow.

An OAuth sign-in for an unknown email provisions an account and must respect Auth:RegistrationEnabled. Existing accounts can still sign in when registration is disabled, and configured bootstrap admins can establish the first admin account. Enabling a provider must not bypass an invite-only product’s policy.

Verify

  • Build the API; run frontend type checking, lint and tests.
  • Verify discovery and startup validation with both flags enabled, either flag disabled, and credentials missing.
  • Exercise sign-in, linking and OAuth step-up through the same callback.
  • Disable registration and confirm unknown accounts are refused while existing accounts can still sign in.

See Authentication for session behavior and Getting started for running the API and SPA together.

Live demo

Explore the running app

No sign-up. Pick an account to open the demo with its credentials prefilled, or open it and sign in yourself.

Open without signing in

Credentials are prefilled. Shared password Slicekit-Demo-2026!.