mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-19 23:53:17 -05:00
Co-authored-by: Alex Yang <himself65@outlook.com> Co-authored-by: Paola Estefanía de Campos <84341268+Paola3stefania@users.noreply.github.com> Co-authored-by: Tim Keller <36288711+tjkeller-xyz@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Alex Yang <himself65@users.noreply.github.com> Co-authored-by: Taesu <166604494+bytaesu@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
81 lines
3.4 KiB
Plaintext
81 lines
3.4 KiB
Plaintext
---
|
||
title: OAuth Proxy
|
||
description: OAuth Proxy plugin for Better Auth
|
||
---
|
||
|
||
A proxy plugin, that allows you to proxy OAuth requests. Useful for development and preview deployments where the redirect URL can't be known in advance to add to the OAuth provider.
|
||
|
||
## Installation
|
||
|
||
<Steps>
|
||
<Step>
|
||
### Add the plugin to your **auth** config
|
||
```ts title="auth.ts"
|
||
import { betterAuth } from "better-auth"
|
||
import { oAuthProxy } from "better-auth/plugins" // [!code highlight]
|
||
|
||
export const auth = betterAuth({
|
||
plugins: [
|
||
oAuthProxy({ // [!code highlight]
|
||
productionURL: "https://my-main-app.com", // Optional - if the URL isn't inferred correctly // [!code highlight]
|
||
currentURL: "http://localhost:3000", // Optional - if the URL isn't inferred correctly // [!code highlight]
|
||
}), // [!code highlight]
|
||
]
|
||
})
|
||
```
|
||
</Step>
|
||
<Step>
|
||
### Add redirect URL to your OAuth provider
|
||
|
||
For the proxy server to work properly, you’ll need to pass the redirect URL of your main production app registered with the OAuth provider in your social provider config. This needs to be done for each social provider you want to proxy requests for.
|
||
|
||
```ts
|
||
export const auth = betterAuth({
|
||
plugins: [
|
||
oAuthProxy(),
|
||
],
|
||
socialProviders: {
|
||
github: {
|
||
clientId: "your-client-id",
|
||
clientSecret: "your-client-secret",
|
||
redirectURI: "https://my-main-app.com/api/auth/callback/github" // [!code highlight]
|
||
}
|
||
}
|
||
})
|
||
```
|
||
</Step>
|
||
</Steps>
|
||
|
||
## How it works
|
||
|
||
The plugin allows you to use a single OAuth client (registered with your production URL) across multiple environments like preview deployments or local development.
|
||
|
||
1. Preview server initiates OAuth, redirecting to the OAuth provider with production's redirect URI
|
||
2. OAuth provider callbacks to production server
|
||
3. Production server exchanges the code for tokens and fetches user info
|
||
4. Production server encrypts the profile data and redirects to preview server (**no database write on production**)
|
||
5. Preview server decrypts the profile, creates user/session in its own database, and sets the session cookie
|
||
|
||
```ts
|
||
import { authClient } from "@/lib/auth-client"
|
||
|
||
await authClient.signIn.social({
|
||
provider: "github",
|
||
callbackURL: "/dashboard"
|
||
})
|
||
```
|
||
|
||
The encrypted profile data is passed via URL query parameters and can only be decrypted by servers sharing the same secret. This also allows preview deployments to use separate databases from production if needed.
|
||
|
||
<Callout type="info">
|
||
This plugin is intended for development and preview environments. If `baseURL` and `productionURL` are the same, the plugin will not proxy the request.
|
||
</Callout>
|
||
|
||
## Options
|
||
|
||
**productionURL**: If this value matches the `baseURL` in your auth config, requests will not be proxied. Defaults to the `BETTER_AUTH_URL` environment variable.
|
||
|
||
**currentURL**: The application's current URL is automatically determined by the plugin. It first checks for the request URL if invoked by a client, then it checks the base URL from popular hosting providers, and finally falls back to the `baseURL` in your auth config. If the URL isn't inferred correctly, you can specify it manually here.
|
||
|
||
**maxAge**: Maximum age in seconds for encrypted profile payloads. Payloads older than this will be rejected to prevent replay attacks. Keep this value short (e.g., 30-60 seconds) to minimize the window for potential replay attacks while still allowing normal OAuth flows. Defaults to `60` seconds.
|