Plugins

OIDC Provider

Turn Kernia into an OpenID Connect / OAuth 2.1 identity provider for your own apps.

The OIDC provider turns your Kernia instance into an identity provider: other applications register as clients, your users authorize them, and Kernia issues ID, access, and refresh tokens. It implements OAuth 2.1 with PKCE, refresh-token rotation, RFC 7662 introspection, RFC 7009 revocation, and OIDC discovery.

The in-tree kernia.plugins.oidc_provider import is a deprecated shim. New code should install and import the standalone kernia_oauth_provider package shown below — both expose the same OIDC issuer.

Installation

Add the plugin to your server

Install kernia-oauth-provider and pass oauth_provider() your issuer URL.

auth.py
import os

from kernia import KerniaOptions
from kernia.auth import init
from kernia_oauth_provider import OAuthProviderOptions, oauth_provider

from .db import adapter

auth = init(KerniaOptions(
    database=adapter,
    secret=os.environ["KERNIA_SECRET"],
    base_url=os.environ["KERNIA_BASE_URL"],
    plugins=[
        oauth_provider(OAuthProviderOptions(
            issuer=os.environ["KERNIA_BASE_URL"],
            supported_scopes=("openid", "profile", "email", "offline_access"),
        )),
    ],
))

Add the client plugin

Relying-party apps that talk to your issuer use the OIDC client plugin.

auth-client.ts
import { createAuthClient } from "better-auth/client";
import { oidcClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  baseURL: "/api/auth",
  plugins: [oidcClient()],
});

Usage

Discover the issuer

Clients read the OIDC metadata to find the authorization, token, JWKS, and userinfo endpoints:

GET /api/auth/.well-known/openid-configuration

The authorization-code flow then runs against /api/auth/oauth2/authorize and /api/auth/oauth2/token, with claims available at /api/auth/oauth2/userinfo.

When a client requests scopes a user hasn't granted, Kernia shows your consent page. Submit the user's decision with:

await authClient.oauth2.consent({ accept: true });

Register a client dynamically

With enable_dynamic_registration=True, apps can self-register (RFC 7591):

await authClient.oauth2.register({
  client_name: "My App",
  redirect_uris: ["https://app.example.com/callback"],
  grant_types: ["authorization_code"],
  scope: "openid profile email",
});

Options

Set on OAuthProviderOptions:

OptionTypeDefaultDescription
issuerstrRequired. Issuer URL advertised in tokens and metadata.
supported_scopestuple[str, ...]("openid", "profile", "email", "offline_access")Scopes the issuer offers.
access_token_ttlint3600Access-token lifetime in seconds.
refresh_token_ttlint2592000Refresh-token lifetime in seconds.
code_ttlint600Authorization-code lifetime in seconds.
enable_dynamic_registrationboolFalseAllow RFC 7591 self-registration of clients.
require_pkce_for_publicboolTrueRequire PKCE for public clients.
jwt_access_tokenboolTrueIssue self-contained JWT access tokens vs. opaque reference tokens.
store_client_secretstr"hashed"How client secrets are kept at rest: "hashed" or "plain".
store_tokensstr"hashed"How refresh tokens are kept at rest: "hashed" or "plain".
pairwise_secretstrNoneSecret (min 32 chars) for pairwise sub identifiers.
grant_typestuple[str, ...]authorization_code, client_credentials, refresh_tokenGrant types the token endpoint accepts.

Schema

Adds the OAuth provider tables (registered clients, authorization codes, access and refresh tokens, consent grants). Generate migrations from the kernia_oauth_provider package after installing it.

Keep enable_dynamic_registration off unless your product genuinely needs open client registration — it lets any caller create a client.