Skip to main content

React

Official

Hooks and components to drop ThunderID into a React + Vite app in minutes. The provider restores an existing session on mount, handles the redirect callback, and refreshes tokens in the background, so your components only ever read state.

@thunderid/react
Apache 2.0 licence
API referenceQuickstartSource

Using a Backend for Frontend (BFF)

~15 min

A Backend for Frontend (BFF) is a small server that belongs to your app. It signs users in and keeps the tokens, so the refresh token never reaches the browser, and it receives back-channel logout. Use this setup when you want to keep your React app but need single logout or safer token storage.

How it works

The browser talks only to the BFF, through an HttpOnly session cookie. The BFF holds the tokens and the client secret, adds the access token to API calls, and receives the back-channel logout ThunderID sends when the user's session ends elsewhere.

Compared with React only

React onlyReact + BFF
Where tokens liveBrowser sessionStorageThe BFF. The browser gets an access token only if the app asks for one
Single logoutNot supportedSupported
Client typePublic, no secretConfidential, with a client secret

Set up the BFF

1

Register a confidential application

In the Console, create an application with a client secret. Open it, go to Advanced Settings → OAuth2 Configuration, and set these URIs. The redirect URIs use the React app's origin. The back-channel logout URI points at the BFF itself, because ThunderID calls it directly, server to server.

OAuth2 Configuration
text
Authorized redirect URI http://localhost:5173/api/auth/callback
Post-logout redirect URI http://localhost:5173
Back-channel logout URI http://localhost:3001/api/auth/backchannel-logout
2

Install the Express SDK

bash
npm install express cookie-parser @thunderid/express
3

Add the environment variables

Keep the client secret on the server. APP_URL is the origin the browser opens the React app on.

server/.env
bash
THUNDERID_BASE_URL=https://localhost:8090
THUNDERID_CLIENT_ID=<your-client-id>
THUNDERID_CLIENT_SECRET=<your-client-secret>
APP_URL=http://localhost:5173
4

Mount the SDK and receive back-channel logout

Mount thunderID() under /api/auth and add the back-channel logout route. The SDK stores the tokens on the server and sets the HttpOnly session cookie. Start the server with node --env-file=.env index.mjs.

server/index.mjs
js
import express from 'express'
import cookieParser from 'cookie-parser'
import { thunderID, handleBackchannelLogout } from '@thunderid/express'
const appUrl = process.env.APP_URL
const app = express()
app.use(cookieParser())
const auth = express.Router()
app.use('/api/auth', auth)
auth.use(
thunderID({
baseUrl: process.env.THUNDERID_BASE_URL,
clientId: process.env.THUNDERID_CLIENT_ID,
clientSecret: process.env.THUNDERID_CLIENT_SECRET,
afterSignInUrl: `${appUrl}/api/auth/callback`,
afterSignOutUrl: appUrl,
sessionCookie: { httpOnly: true, sameSite: 'lax' },
}),
)
// ThunderID posts a logout token here when the user's session ends elsewhere.
auth.post('/backchannel-logout', handleBackchannelLogout())
// Add the endpoints the React SDK calls. See "Endpoints the React SDK calls".
app.listen(3001)

Connect the React app

1

Serve the BFF on the app's origin

Forward /api/auth to the BFF, so the session cookie is first party and you need no CORS setup. In production, serve the React build and the BFF from the same origin, for example behind one reverse proxy.

vite.config.js
js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/api/auth': { target: 'http://localhost:3001' },
},
},
})
2

Switch the provider to BFF mode

Set endpoints.backend. The app needs no client ID or base URL, because every endpoint defaults to a path under /api/auth on the app's origin. session.checkInterval sets how often, in seconds, the SDK checks that the session is still live.

src/main.jsx
jsx
<ThunderIDProvider
endpoints={{ backend: {} }}
session={{ checkInterval: 30 }}
>
<App />
</ThunderIDProvider>
Note

Hooks and components work as before. Calls through the SDK's http client go to /api/auth/proxy/*, and the BFF adds the access token.

Endpoints the React SDK calls

The BFF serves these under /api/auth. The SDK sends the header X-Requested-With: XMLHttpRequest on every request it makes with fetch. Reject requests without it, except the sign-in redirects, so a page on another site cannot use the session cookie.

EndpointWhat it does
GET /signinStarts sign-in. The browser navigates here
GET /callbackThe redirect URI. Exchanges the code and sets the session cookie
GET /sessionReturns the signed-in user
GET /checksessionTells the app whether the session is still live. Must not refresh the session
POST /refreshRefreshes the access token on the server
POST /signoutEnds the session and returns the end-session URL
POST /tokenReturns an access token for APIs the app calls directly. Leave it out if the app sends every call through the proxy
ALL /proxy/*Forwards API calls with the access token

Try single logout

1

Allow a local back-channel logout URI

ThunderID rejects localhost and private network addresses as back-channel logout URIs by default. For local testing only, allow them in deployment.yaml and restart the server.

deployment.yaml
yaml
oauth:
logout:
backchannel:
reject_private_addresses: false
2

Add single sign-on to the sign-in flow

The flow needs the Check SSO Session and Save / Load Session nodes. Without them ThunderID records no session, so it sends no logout.

3

Sign out from another app

Sign in to this app and to a second app that uses the same ThunderID, in the same browser. Sign out of the second app. Within 30 seconds, or as soon as you switch back to this app's tab, it signs you out too.

Beyond this guide

React and Express BFF sample

Express SDK

Back-channel logout

Next.js SDK

ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© Copyright Linux Foundation Europe.For web site terms of use, trademark policy and other project policies please see https://linuxfoundation.eu/en/policies.