OpenGate/ Docs

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-context

Next.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-secret

React 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.