OAuth Proxy
Keep OAuth client secrets on the server during SPA and preview-deploy flows.
OAuth proxy lets a browser app start a provider sign-in without ever holding the client secret. Kernia stores the provider config, signs the state, exchanges the authorization code server-side, and issues the session. It also rewrites redirect URLs so a single registered callback works across preview deployments.
Installation
Add the plugin to your server
Pass oauth_proxy() to your Kernia config. The optional SPA helper flow takes a
providers map and a redirect_uri that exactly matches what you registered at
the provider.
import os
from kernia import KerniaOptions
from kernia.auth import init
from kernia.plugins.oauth_proxy import OAuthProxyOptions, oauth_proxy
from .db import adapter
from .providers import google_provider
auth = init(KerniaOptions(
database=adapter,
secret=os.environ["KERNIA_SECRET"],
base_url=os.environ["KERNIA_BASE_URL"],
plugins=[
oauth_proxy(OAuthProxyOptions(
providers={"google": google_provider},
redirect_uri="https://api.example.com/api/auth/oauth-proxy/callback",
)),
],
))Add the client plugin
import { createAuthClient } from "better-auth/client";
import { oAuthProxyClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
baseURL: "/api/auth",
plugins: [oAuthProxyClient()],
});Usage
Once installed, OAuth proxy works transparently behind your normal social sign-in calls: the client requests an authorization URL, Kernia returns it with signed state, and the secret-bearing code exchange happens server-side.
await authClient.signIn.social({
provider: "google",
callbackURL: "/dashboard",
});The browser is redirected to the provider and back through
/api/auth/oauth-proxy/callback, where Kernia validates the signed state,
exchanges the code, creates the user and session, and redirects to your app.
Options
Set on OAuthProxyOptions:
| Option | Type | Default | Description |
|---|---|---|---|
providers | dict[str, OAuthProvider] | None | Providers exposed through the SPA helper flow. |
redirect_uri | str | None | Callback URL registered at the provider. Must match exactly. |
success_callback_url | str | None | Default app URL to return to after sign-in. |
trusted_providers | tuple[str, ...] | () | Providers allowed to skip extra origin checks. |
disable_sign_up | bool | False | Reject proxy sign-ins for users with no existing account. |
max_age | int | 600 | Max age (seconds) of an encrypted passthrough payload. |
secret | str | None | Dedicated encryption secret for proxy payloads. Falls back to the global secret. |
Schema
No new tables. Provider accounts are written to the core account table.
The redirect_uri must be registered at the provider character-for-character.
A trailing slash or scheme mismatch is the most common cause of failed callbacks.