React / Next.js Integration
On this page
Install Dependencies
# Next.js (recommended)
npm install next-auth
# React SPA
npm install oidc-client-ts react-oidc-contextNext.js with next-auth
app/api/auth/[...nextauth]/route.tstypescript
import NextAuth from 'next-auth';
const handler = NextAuth({
providers: [
{
id: 'opengate',
name: 'OpenGate IAM',
type: 'oauth',
wellKnown: 'http://localhost:8080/realms/master/.well-known/openid-configuration',
clientId: process.env.OPENGATE_CLIENT_ID!,
clientSecret: process.env.OPENGATE_CLIENT_SECRET!,
authorization: { params: { scope: 'openid profile email' } },
profile(profile) {
return {
id: profile.sub,
name: `${profile.given_name} ${profile.family_name}`,
email: profile.email,
roles: profile.roles ?? [],
};
},
},
],
callbacks: {
async jwt({ token, account, profile }) {
if (account) {
token.accessToken = account.access_token;
token.roles = (profile as any)?.roles ?? [];
}
return token;
},
async session({ session, token }) {
session.accessToken = token.accessToken as string;
(session.user as any).roles = token.roles;
return session;
},
},
});
export { handler as GET, handler as POST };.env.localbash
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-random-secret-32-chars
OPENGATE_CLIENT_ID=my-nextjs-app
OPENGATE_CLIENT_SECRET=your-client-secretReact SPA (PKCE)
For React apps without a backend, use PKCE with a public client (no client secret):
src/auth/oidcConfig.tstypescript
import { WebStorageStateStore } from 'oidc-client-ts';
export const oidcConfig = {
authority: 'http://localhost:8080/realms/master',
client_id: 'my-spa',
redirect_uri: window.location.origin + '/callback',
response_type: 'code',
scope: 'openid profile email',
post_logout_redirect_uri: window.location.origin,
userStore: new WebStorageStateStore({ store: window.sessionStorage }),
};src/main.tsxtsx
import { AuthProvider } from 'react-oidc-context';
import { oidcConfig } from './auth/oidcConfig';
export default function App() {
return (
<AuthProvider {...oidcConfig}>
<YourApp />
</AuthProvider>
);
}Protecting Routes
src/components/ProtectedRoute.tsxtsx
import { useAuth } from 'react-oidc-context';
export function ProtectedRoute({ children }: { children: React.ReactNode }) {
const auth = useAuth();
if (auth.isLoading) return <div>Loading...</div>;
if (!auth.isAuthenticated) {
auth.signinRedirect();
return null;
}
return <>{children}</>;
}Next.js App Router middleware:
middleware.tstypescript
export { default } from 'next-auth/middleware';
export const config = {
matcher: ['/dashboard/:path*', '/admin/:path*'],
};Calling the API
src/lib/api.tstypescript
import { getSession } from 'next-auth/react';
export async function apiFetch(path: string, init?: RequestInit) {
const session = await getSession();
return fetch(`http://localhost:8080${path}`, {
...init,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${session?.accessToken}`,
...init?.headers,
},
});
}
// Usage
const res = await apiFetch('/api/users?realm=master');
const { content } = await res.json();Token refresh
next-auth handles token refresh automatically using the refresh_token grant. For React SPA, react-oidc-context auto-renews tokens before expiry using silent refresh.