Secure a React App
There are two common ways to connect a browser app to AuthGate.
| Path | Best for | Browser stores access token? |
|---|---|---|
| BFF Gateway | Most beginner and ecommerce-style browser apps. | No |
| Direct OAuth2 PKCE | Learning OAuth2 libraries or building a token-based SPA. | The OAuth2 library manages tokens |
If you are not sure which one to choose, use the BFF Gateway path first. It is easier to reason about because your frontend calls same-origin URLs like /bff/me and /api/products.
Option 1: Use a BFF Gateway
This is the recommended path for a normal React or Next.js website.
Your frontend does not need an OAuth2 library. AuthGate owns the login redirect, callback, token exchange, and session cookie.
1. Create the OAuth2 client
In IAM > Clients, create or choose an OAuth2 client for the browser app.
The client must allow the BFF callback URL:
https://your-gateway.authgate.site/bff/callback2. Create a BFF gateway
In API Gateway > Create Gateway:
| Field | Value |
|---|---|
| Gateway type | BFF |
| Auth type | OAUTH2 |
| OAuth2 client | Your IAM client ID |
3. Register the frontend app
Open the gateway, then go to Frontend Apps.
Add the deployed frontend origin:
https://your-react-app.example.comUsers should open the gateway URL, not the raw frontend origin:
https://your-gateway.authgate.site4. Use BFF endpoints from React
Login button:
export function SignInButton() {
return (
<button onClick={() => {
window.location.href = '/bff/login?return_to=/'
}}>
Sign in
</button>
)
}Session check:
type BffSession = {
authenticated: boolean
tenantId?: string
gatewayName?: string
oauthClientId?: string
scope?: string
}
export async function getSession(): Promise<BffSession> {
const response = await fetch('/bff/me', {
credentials: 'include'
})
if (!response.ok) {
return { authenticated: false }
}
return response.json()
}Logout:
await fetch('/bff/logout', {
method: 'POST',
credentials: 'include'
})5. Call API routes
Public route:
const products = await fetch('/api/products', {
credentials: 'include'
}).then((response) => response.json())Secure route:
const profile = await fetch('/api/profile', {
credentials: 'include'
}).then((response) => response.json())If /api/profile is SECURE and the user is not signed in, AuthGate should return an unauthorized response.
Do not save AuthGate access tokens in localStorage when using the BFF pattern. The browser should use the BFF session cookie only.
Option 2: Use direct OAuth2 PKCE
Use this path if you are intentionally learning OAuth2/OIDC libraries.
Install a client library:
npm install react-oidc-context oidc-client-tsConfigure the provider:
import { AuthProvider } from 'react-oidc-context'
const oidcConfig = {
authority: 'https://iam.authgate.site',
client_id: '<your_client_id>',
redirect_uri: `${window.location.origin}/auth/callback`,
response_type: 'code',
scope: 'openid profile email'
}
export function App() {
return (
<AuthProvider {...oidcConfig}>
<YourRoutes />
</AuthProvider>
)
}Trigger login:
import { useAuth } from 'react-oidc-context'
export function LoginButton() {
const auth = useAuth()
if (auth.isAuthenticated) {
return <p>Signed in as {auth.user?.profile.sub}</p>
}
return (
<button onClick={() => void auth.signinRedirect()}>
Sign in
</button>
)
}Call an OAuth2 gateway route:
const token = auth.user?.access_token
const response = await fetch('https://your-api.authgate.site/api/orders', {
headers: {
Authorization: `Bearer ${token}`
}
})Which option should I use?
BFF Gateway
Choose BFF if you want a normal website where login is handled by AuthGate and browser code does not touch access tokens.