OAuth Provider
Add an OAuth 2.1 authorization consent screen to Solid and Zaidan.
The OAuth Provider plugin adds the user-facing consent step for Better Auth's OAuth 2.1 Provider. It identifies the requesting application, explains the requested scopes, shows the signed-in account, and lets the user allow or cancel the request.
It contributes:
- A copied
<OAuthConsent />view at/auth/consent - Public OAuth client metadata loading
- Allow and cancel actions through
oauthConsentOptions - Default labels for
openid,profile,email, andoffline_access - Custom metadata for application-specific scopes
Setup
Configure the Better Auth server
Install the provider package:
bun add @better-auth/oauth-providerAdd the JWT and OAuth Provider plugins. Point consentPage at the route that renders the consent view:
import { oauthProvider } from "@better-auth/oauth-provider"
import { betterAuth } from "better-auth"
import { jwt } from "better-auth/plugins"
export const auth = betterAuth({
disabledPaths: ["/token"],
plugins: [
jwt(),
oauthProvider({
loginPage: "/auth/sign-in",
consentPage: "/auth/consent"
})
]
})Generate or migrate your Better Auth schema after enabling the server plugin:
bunx auth@latest migrateConfigure the Solid client
Add oauthProviderClient() to the auth client. It preserves Better Auth's signed authorization query when the user responds:
import { oauthProviderClient } from "@better-auth/oauth-provider/client"
import { createAuthClient } from "@better-auth-ui/solid"
export const authClient = createAuthClient({
plugins: [oauthProviderClient()]
})Install the Solid and Zaidan view
bunx shadcn@latest add https://better-auth-ui.com/r/solid/oauth-provider.jsonThis installs:
src/lib/auth/oauth-provider-plugin.tssrc/components/auth/oauth-provider/oauth-consent.tsxsrc/components/auth/user/user-avatar.tsx
Register the UI plugin
import { AuthProvider } from "@/components/auth/auth-provider"
import { oauthProviderPlugin } from "@/lib/auth/oauth-provider-plugin"
<AuthProvider
authClient={authClient}
plugins={[oauthProviderPlugin()]}
>
{children}
</AuthProvider>Allow the consent route
Include the plugin path in the route that renders <Auth />:
import { viewPaths } from "@better-auth-ui/core"
import { oauthProviderPlugin } from "@/lib/auth/oauth-provider-plugin"
const validAuthPathSegments = new Set([
...Object.values(viewPaths.auth),
oauthProviderPlugin().viewPaths.auth.oauthConsent
])Keep this path aligned with the server's consentPage.
Custom scopes
Add labels and short descriptions for application-specific scopes:
oauthProviderPlugin({
scopeMetadata: {
calendar: {
label: "View your calendar",
description: "Read your calendar events and availability."
}
},
localization: {
allow: "Authorize"
}
})Unknown scopes remain visible using their raw scope value.
Consent behavior
This first consent view accepts or denies the complete requested scope set. It does not render per-scope controls. Omitting scope from the consent mutation tells Better Auth to accept the scopes from the original signed request.
Keep the authorization query string on the consent page. Do not strip it, rebuild it from redirect_uri, or navigate to the requested redirect yourself. oauthProviderClient() forwards the signed query to Better Auth, and Better Auth validates and completes the redirect.
The public client endpoint requires a signed-in session. Direct visits with missing request data, no session, or an unknown client render an invalid-request state.
Login reuses the existing signIn view and resumes automatically when Better Auth creates the session. Optional prompt=create, account-selection, and post-login screens require application-specific continuation logic and are not registered by this base plugin.
Solid APIs
Use usePublicOAuthClient to load application metadata and oauthConsentOptions to submit the user's decision.
Last updated on