Tutorial: Integrate TideCloak into an Existing Next.js App
Add TideCloak auth to an existing Next.js app - provider setup, redirect, middleware, API verification, and E2EE.
1) Prepare TideCloak
- Ensure your realm is set up (you can use the dev container in the other tutorial).
- In the Admin Console, go to Clients → myclient → Action → Download adaptor configs (format:
keycloak-oidc-keycloak-json). - Save the JSON in your app (e.g.,
/tidecloakAdapter.json).
2) Install the SDK
- NPM
- Yarn
npm install @tidecloak/nextjs
yarn add @tidecloak/nextjs
Create public/silent-check-sso.html (required for silent SSO):
<html><body><script>parent.postMessage(location.href, location.origin)</script></body></html>
3) Wrap your app with <TideCloakProvider>
App Router
import React from 'react';import { TideCloakProvider } from '@tidecloak/nextjs';import adapter from '../tidecloakAdapter.json';export default function RootLayout({ children }: { children: React.ReactNode }) {return (<html lang="en"><body><TideCloakProvider config={{ ...adapter }}>{children}</TideCloakProvider></body></html>);}
Pages Router
import React from 'react';import { TideCloakProvider } from '@tidecloak/nextjs';import adapter from '../tidecloakAdapter.json';function MyApp({ Component, pageProps }) {return (<TideCloakProvider config={adapter}><Component {...pageProps} /></TideCloakProvider>);}export default MyApp;
4) Redirect URI
If not specified, the SDK assumes:
`${window.location.origin}/auth/redirect`
Create a page at that route (App Router example):
'use client';import { useEffect } from 'react';import { useRouter } from 'next/navigation';import { useTideCloak } from '@tidecloak/nextjs';export default function RedirectPage() {const { authenticated, isInitializing, logout } = useTideCloak();const router = useRouter();useEffect(() => {const params = new URLSearchParams(window.location.search);if (params.get("auth") === "failed") {sessionStorage.setItem("tokenExpired", "true");logout();}}, []);useEffect(() => {if (!isInitializing) {router.push(authenticated ? '/home' : '/');}}, [authenticated, isInitializing, router]);return <p style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>Waiting for authentication...</p>;}
Or pass an explicit redirectUri in the provider config.
5) Use the hook & guard components
'use client';import { useTideCloak, Authenticated, Unauthenticated } from '@tidecloak/nextjs';function Header() {const { authenticated, login, logout, tokenExp } = useTideCloak();return (<header>{authenticated ? (<><span>Logged in (exp {new Date(tokenExp * 1000).toLocaleTimeString()})</span><button onClick={logout}>Log Out</button></>) : <button onClick={login}>Log In</button>}</header>);}
6) Protect routes with Edge middleware
import { NextResponse } from 'next/server';import tidecloakConfig from './tidecloakAdapter.json';import { createTideCloakMiddleware } from '@tidecloak/nextjs/server/tidecloakMiddleware';export default createTideCloakMiddleware({config: tidecloakConfig,protectedRoutes: {'/admin/*': ['admin'],'/api/private/*': ['user'],},onFailure: ({ token }, req) => NextResponse.redirect(new URL('/login', req.url)),onError: (err, req) => NextResponse.rewrite(new URL('/error', req.url)),});export const config = {matcher: ['/((?!_next|[^?]*\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico)).*)','/api/(.*)',],runtime: 'edge',};
7) Verify tokens in API routes
Pages Router
import type { NextApiRequest, NextApiResponse } from 'next';import { verifyTideCloakToken } from '@tidecloak/nextjs/server';import config from '../../tidecloakAdapter.json';export default async function handler(req: NextApiRequest, res: NextApiResponse) {const token = req.cookies.kcToken || req.headers.authorization?.split(' ')[1] || '';const payload = await verifyTideCloakToken(config, token, ['user']);if (!payload) return res.status(401).json({ error: 'Unauthorized' });res.status(200).json({ data: 'Secure data response' });}
App Router
import { NextRequest, NextResponse } from 'next/server';import { verifyTideCloakToken } from '@tidecloak/nextjs/server';import config from '../../../tidecloakAdapter.json';export async function GET(req: NextRequest) {const token = req.cookies.get('kcToken')?.value || '';const payload = await verifyTideCloakToken(config, token, ['user']);if (!payload) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });return NextResponse.json({ data: 'Secure data response' });}
8) Optional - E2EE with tag-based encryption
// Encrypt:const encryptedArray = await doEncrypt([{ data: "10 Smith Street", tags: ["street"] },]);// Decrypt:const decryptedArray = await doDecrypt([{ encrypted: encryptedArray[0], tags: ["street"] },]);
- Permissions:
_tide_<tag>.selfencryptto encrypt;_tide_<tag>.selfdecryptto decrypt. - Data types:
datamust be a string or Uint8Array (objects will fail).
9) Try it
- Run your app, login, hit a protected API, and test SSO logout.