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.
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
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",
});Link a provider to the current user
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:
| Option | Type | Default | Description |
|---|---|---|---|
provider_id | str | — | Required. Unique id used in client calls and route paths. |
client_id | str | — | Required. OAuth client id. |
client_secret | str | — | Required. OAuth client secret. |
discovery_url | str | None | OIDC discovery doc. Required unless authorization_url + token_url are set. |
authorization_url | str | None | Authorization endpoint (if not using discovery). |
token_url | str | None | Token endpoint (if not using discovery). |
user_info_url | str | None | Userinfo endpoint. |
scopes | tuple[str, ...] | () | Scopes to request. |
pkce | bool | False | Enable PKCE for the authorization-code flow. |
issuer | str | None | Expected issuer for OIDC validation. |
require_issuer_validation | bool | False | Reject tokens whose issuer does not match. |
authentication | str | "post" | Client auth at the token endpoint: "post" or "basic". |
map_profile_to_user | (profile) -> dict | None | Map provider claims onto the Kernia user. |
disable_sign_up | bool | False | Reject sign-ins for users with no existing account. |
override_user_info | bool | False | Refresh 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.