@tidecloak/nextjs
v0.12.43
Published
TideCloak nextjs SDK
Maintainers
Readme
TideCloak NextJS SDK
Quick Start Template
If you're new to TideCloak, the fastest way to get started is with our official Next.js template:
@tidecloak/create-nextjs
It scaffolds a working project with authentication, middleware, and optional IAM setup - so you can start building right away.
Secure your Next.js app with TideCloak: authentication, session management, data encryption, and edge-middleware integration.
1. Prerequisites
Before you begin, ensure you have the following:
Next.js:
- App Router (recommended): Next.js 13.4 or later (for
layout.tsxsupport) - Pages Router (legacy): Next.js 12 or later (for
_app.tsxsupport)
- App Router (recommended): Next.js 13.4 or later (for
React 18 or later
Node.js ≥18.17.0
A running TideCloak server you have admin control over.
IGA enabled realm
A registered client in your realm with default user contexts approved and committed
A valid Tidecloak adapter JSON file (e.g.,
tidecloakAdapter.json)
Note: Choose either the App Router or the Pages Router for your project. You only need one routing system active.
2. Install @tidecloak/nextjs
Add @tidecloak/nextjs to your project:
npm install @tidecloak/nextjs
# or
yarn add @tidecloak/nextjsThis bundle provides:
<TideCloakProvider>- application-level contextuseTideCloak()hook - access tokens and auth actionsverifyTideCloakToken()- server-side JWT verification<Authenticated>/<Unauthenticated>- UI guardsdoEncrypt()/doDecrypt()- tag-based encryption/decryptioncreateTideCloakMiddleware()- Edge middleware for route protection (supports both Pages & App routers)
Note: Installing this package automatically adds a
silent-check-sso.htmlfile to yourpublicdirectory. This file is required for silent SSO checks; if it doesn’t exist, create it manually atpublic/silent-check-sso.htmlwith the following content, otherwise the app will break:<html> <body> <script>parent.postMessage(location.href, location.origin)</script> </body> </html>
3. Initialize the Provider
To begin using the SDK, wrap your application with <TideCloakProvider>.
This makes authentication state, token access, and authorization tools available throughout your app. You only need to wrap once-at the top level entry point depending on which routing system you're using.
App Router
File: /app/layout.tsx
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>
);
}Description: This layout.tsx is used by Next.js’s App Router. It defines the root HTML structure and wraps all nested pages and layouts with TideCloakProvider, making authentication context available everywhere in the /app directory.
Pages Router
File: /pages/_app.tsx
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;Description: The _app.tsx file is the entry point for the Pages Router. It wraps every page component in the /pages directory with TideCloakProvider, so that authentication state and methods are accessible across all your pages.
4. Redirect URI Handling
TideCloak supports an optional redirectUri parameter. This is the URL users are sent to after login or logout.
If omitted, it defaults to:
`${window.location.origin}/auth/redirect`If your app runs at
http://localhost:3000, then by default users will be redirected tohttp://localhost:3000/auth/redirectafter login or logout.If that route doesn't exist in your project, you must create it or explicitly define a different
redirectUriin your TideCloak config.
If you use the default, you must create a page at /auth/redirect in your app.
You can customize this URI in your provider config:
<TideCloakProvider config={{ ...adapter, redirectUri: 'https://yourapp.com/auth/callback' }}>
{children}
</TideCloakProvider>Example: /app/auth/redirect/page.tsx
'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',
fontSize: '1rem',
color: '#555',
}}>
<p>Waiting for authentication...</p>
</div>
);
}This page helps finalize the login or logout flow, and also reacts to token expiration events that may have triggered a redirect from the middleware.
4. Using the useTideCloak Hook
Use this hook anywhere in your React component tree to manage authentication:
'use client'
import React from 'react';
import { useTideCloak } from '@tidecloak/nextjs';
export default function Header() {
const {
authenticated,
login,
logout,
token,
tokenExp,
refreshToken,
getValueFromToken,
getValueFromIdToken,
hasRealmRole,
hasClientRole,
doEncrypt,
doDecrypt,
} = useTideCloak();
return (
<header>
{authenticated ? (
<>
<span>Logged in</span>
<button onClick={logout}>Log Out</button>
</>
) : (
<button onClick={login}>Log In</button>
)}
{token && (
<small>Expires at {new Date(tokenExp * 1000).toLocaleTimeString()}</small>
)}
</header>
);
}| Name | Type | Description |
| ------------------------------------- | -------------------------------------------- | ----------------------------------------------------------------------- |
| authenticated | boolean | Whether the user is logged in. |
| login() / logout() | () => void | Trigger the login or logout flows. |
| token, tokenExp | string, number | Access token and its expiration timestamp. |
| Automatic token refresh | built-in | Tokens refresh silently on expiration-no manual setup needed. |
| refreshToken() | () => Promise<boolean> | Force a silent token renewal. |
| getValueFromToken(key) | (key: string) => any | Read a custom claim from the access token. |
| getValueFromIdToken(key) | (key: string) => any | Read a custom claim from the ID token. |
| hasRealmRole(role) | (role: string) => boolean | Check a realm-level role. |
| hasClientRole(role, client?) | (role: string, client?: string) => boolean | Check a client-level role; defaults to your app’s client ID if omitted. |
| doEncrypt(data) / doDecrypt(data) | (data: any) => Promise<any> | Encrypt or decrypt payloads via TideCloak’s built-in service. |
5. Guard Components
Use these components to conditionally render UI based on auth state:
'use client'
import React from 'react';
import { Authenticated, Unauthenticated } from '@tidecloak/nextjs';
export default function Dashboard() {
return (
<>
<Authenticated>
<h1>Dashboard</h1>
{/* Protected widgets here */}
</Authenticated>
<Unauthenticated>
<p>Please log in to access the dashboard.</p>
</Unauthenticated>
</>
);
}<Authenticated>: renders children only whenauthenticated === true<Unauthenticated>: renders children only whenauthenticated === false
6. Encrypting & Decrypting Data
Protect sensitive payloads using tag-based encryption/decryption:
// Encrypt payloads:
const encryptedArray = await doEncrypt([
{ data: { email: '[email protected]' }, tags: ['email'] },
]);
// Decrypt blobs:
const decryptedArray = await doDecrypt([
{ encrypted: encryptedArray[0], tags: ['email'] },
]);Important: The
dataproperty must be either a string or aUint8Array(raw bytes).
When you encrypt a string, decryption returns a string.
When you encrypt aUint8Array, decryption returns aUint8Array.
Valid example:
// Before testing below, ensure you've set up the necessary roles: const multi_encrypted_addresses = await doEncrypt([ { data: "10 Smith Street", tags: ["street"] }, { data: "Southport", tags: ["suburb"] }, { data: "20 James Street - Burleigh Heads", tags: ["street", "suburb"] } ]);
Invalid (will fail):
// Prepare data for encryption const dataToEncrypt = { title: noteData.title, content: noteData.content }; // Encrypt the note data using TideCloak (this will error) const encryptedArray = await doEncrypt([{ data: dataToEncrypt, tags: ['note'] }]);
- Permissions: Encryption requires
_tide_<tag>.selfencrypt; decryption requires_tide_<tag>.selfdecrypt. - Order guarantee: Output preserves input order.
7. Edge Middleware with TideCloak
Place your middleware at the project root for both routers.
File: /middleware.ts
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',
};Description: The middleware.ts file runs at the Edge runtime before any page or API request. It applies authentication and role-based access control globally, using your adapter settings to verify tokens and redirect or rewrite responses as needed.
8. Server‑Side Token Verification
Pages Router
File: /pages/api/secure.ts
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
File: /app/api/secure/route.ts
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' });
}9. Advanced & Best Practices
- Auto-Refresh: built into the provider-no manual timers.
- Error Handling: use the
initErrorproperty fromuseTideCloak. - Custom Claims: read via
getValueFromToken()/getValueFromIdToken(). - Role-Based UI: combine hooks & guard components for fine-grained control.
- Lazy Initialization: wrap
<TideCloakProvider>around only protected sections in large apps.

