How-to Guide: Common Tasks with TideCloak in Next.js
Common tasks you'll use most with TideCloak in a Next.js app - quick, copy-paste snippets.
How to: Install or Scaffold a Project
Scaffold a ready-to-go template:
sudo apt update && sudo apt install -y curl jqnpm init @tidecloak/nextjs@latest my-app
Install the SDK in an existing app:
- 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>
How to: Initialize the Provider (App & Pages Router)
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;
How to: Customize Redirect URI
Specify a custom redirect URI after login/logout:
<TideCloakProvider config={{ ...adapter, redirectUri: 'https://yourdomain.com/auth/callback' }}>{children}</TideCloakProvider>
Default (if omitted):
`${window.location.origin}/auth/redirect`
Example redirect page
'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 (<div style={{ minHeight:'100vh', display:'flex', alignItems:'center', justifyContent:'center' }}><p>Waiting for authentication...</p></div>);}
Ensure the path exists in your app to avoid redirect issues.
How to: Get User Info from Access/ID Tokens
Retrieve user information stored in tokens:
const { getValueFromToken, getValueFromIdToken } = useTideCloak();const userEmail = getValueFromToken('email');const preferredUsername = getValueFromIdToken('preferred_username');
How to: Check User Roles
Check realm and client roles:
const { hasRealmRole, hasClientRole } = useTideCloak();const isAdmin = hasRealmRole('admin');const isEditor = hasClientRole('editor'); // defaults to your app clientconst isBillingInOtherClient = hasClientRole('billing:writer', 'backoffice-client');
How to: Manually Refresh Token
Force a silent token refresh:
const { refreshToken } = useTideCloak();const refreshed = await refreshToken();console.log(refreshed ? 'Token refreshed!' : 'Refresh failed.');
Tokens also refresh automatically when they expire.
How to: Encrypt Data Before Sending
Encrypt sensitive data using tag-based encryption:
const { doEncrypt } = useTideCloak();const encrypted = await doEncrypt([{ data: "Sensitive Information", tags: ["private"] }]);
Note:
datamust be a string or Uint8Array. Permissions: require_tide_<tag>.selfencrypton the access token.
How to: Decrypt Received Data
Decrypt encrypted payloads:
const { doDecrypt } = useTideCloak();const decrypted = await doDecrypt([{ encrypted: receivedEncryptedData, tags: ["private"] }]);
Permissions: require
_tide_<tag>.selfdecrypton the access token. Order guarantee: returned array preserves the input order.
How to: Gate UI with Guard Components
Render UI conditionally based on auth state:
'use client';import { Authenticated, Unauthenticated } from '@tidecloak/nextjs';export default function Dashboard() {return (<><Authenticated><h1>Dashboard</h1>{/* Protected widgets */}</Authenticated><Unauthenticated><p>Please log in to access the dashboard.</p></Unauthenticated></>);}
🚧 How to: Protect Routes with Edge Middleware
Add a global middleware to protect pages and APIs:
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',};
How to: 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' });return 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' });}
How to: Handle Authentication Errors
Handle initialization errors from the provider:
'use client';import { useEffect } from 'react';import { useTideCloak } from '@tidecloak/nextjs';export default function ErrorBoundaryLite() {const { initError } = useTideCloak();useEffect(() => {if (initError) {console.error('Authentication error:', initError);alert('Authentication failed. Please log in again.');}}, [initError]);return null;}
How to: Run TideCloak locally (dev)
If you don't already have a TideCloak instance, run the dev container which auto-imports a sample realm.
sudo docker run -d -v .:/opt/keycloak/data/h2 -v ./nextjs-test-realm.json:/opt/keycloak/data/import/nextjs-test-realm.json --name tidecloak -p 8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=password tideorg/tidecloak-dev:latest
Admin console: http://localhost:8080
Your realm is automatically imported in the dev image
- Default realm
nextjs-testwith Tide IdP set as default. - Optional branding on the Tide login page.
- Simplified user-profile and registration off.
Link Tide Accountrequired action enabled.- Client myclient with the following settings:
- Redirect URIs:
http://localhost:3000/silent-check-sso.htmlandhttp://localhost:3000/auth/redirect - Web origins:
http://localhost:3000.
- Redirect URIs:
- JWT includes roles via dedicated scope.
- Realm roles
_tide_dob.selfdecryptand_tide_dob.selfencryptadded to defaults.
Activate a license (dev/free)
Go to the realm's Tide IdP Settings → Manage License → Request License and complete the flow. The host becomes licensed/activated (free dev tier available).
Play!
- Visit
http://localhost:3000 - Log in
- Hit the protected API route and try the E2EE example page
- Logout to test Single‑Sign‑Out