Plugins

Generic OAuth

Add any OAuth 2.0 or OIDC provider by URL, without writing a provider module.

Generic OAuth registers sign-in and callback routes for any provider you can describe with URLs and scopes. Point it at the provider's endpoints — or a single discovery URL — and Kernia handles the redirect, code exchange, and profile mapping.

Installation

Add the plugin to your server

Pass generic_oauth() a tuple of GenericOAuthConfig entries. Each needs a unique provider_id, credentials, and either a discovery_url or the explicit authorization_url + token_url pair.

auth.py
import os

from kernia import KerniaOptions
from kernia.auth import init
from kernia.plugins.generic_oauth import GenericOAuthConfig, generic_oauth

from .db import adapter

auth = init(KerniaOptions(
    database=adapter,
    secret=os.environ["KERNIA_SECRET"],
    base_url=os.environ["KERNIA_BASE_URL"],
    plugins=[
        generic_oauth((
            GenericOAuthConfig(
                provider_id="acme",
                client_id=os.environ["ACME_CLIENT_ID"],
                client_secret=os.environ["ACME_CLIENT_SECRET"],
                discovery_url="https://idp.example.com/.well-known/openid-configuration",
                scopes=("openid", "email", "profile"),
                pkce=True,
            ),
        )),
    ],
))

Add the client plugin

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

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

Usage

Sign in with a provider

Call signIn.oauth2 with the providerId you configured. The browser is redirected to the provider and back through /api/auth/oauth2/callback/:providerId, where Kernia exchanges the code and sets the session cookie.

await authClient.signIn.oauth2({
  providerId: "acme",
  callbackURL: "/dashboard",
});

For an already signed-in user, link an additional provider account:

await authClient.oauth2.link({
  providerId: "acme",
  callbackURL: "/settings/connections",
});

Options

Set per provider on GenericOAuthConfig:

OptionTypeDefaultDescription
provider_idstrRequired. Unique id used in client calls and route paths.
client_idstrRequired. OAuth client id.
client_secretstrRequired. OAuth client secret.
discovery_urlstrNoneOIDC discovery doc. Required unless authorization_url + token_url are set.
authorization_urlstrNoneAuthorization endpoint (if not using discovery).
token_urlstrNoneToken endpoint (if not using discovery).
user_info_urlstrNoneUserinfo endpoint.
scopestuple[str, ...]()Scopes to request.
pkceboolFalseEnable PKCE for the authorization-code flow.
issuerstrNoneExpected issuer for OIDC validation.
require_issuer_validationboolFalseReject tokens whose issuer does not match.
authenticationstr"post"Client auth at the token endpoint: "post" or "basic".
map_profile_to_user(profile) -> dictNoneMap provider claims onto the Kernia user.
disable_sign_upboolFalseReject sign-ins for users with no existing account.
override_user_infoboolFalseRefresh stored user fields from the provider on each sign-in.

Several providers ship as ready-made config factories — auth0, keycloak, okta, microsoft_entra_id, hubspot, patreon, gumroad, slack_generic, and line_generic — importable from kernia.plugins.generic_oauth.

Schema

No new tables. Linked provider accounts are written to the core account table.