@tidecloak/create-nextjs
v0.13.27
Published
Scaffold a TideCloak-ready Next.js app with optional IAM setup and working auth - start building instantly with a live example
Readme
TideCloak NextJS SDK
Jump to:
Quickstart
Secure your Next.js app with TideCloak: authentication, session management, data encryption, and edge-middleware integration all in minutes.
1. Prerequisites
Before you begin, ensure you have:
- A running TideCloak server you have admin control over. No need to set anything up - just start the server.
As well as
- Next.js 13.5.7 or later
- React 18 or later
- Node.js ≥18.17.0
2. Initialize the template project
[!NOTE] The initialization will prompt you to create the realm and clients on your Tidecloak server. The script will also include Tide Realm Admin creation and IGA enablement.
sudo apt update && sudo apt install -y curl jq
npm init @tidecloak/nextjs@latest my-app2.a Project structure
my-app/
├── app
| └── api/
| │ └── protected/
| │ └── route.js <- A protected API on your NextJS server that verifies the user's access token
| ├── auth/
| │ └── redirect/
| │ └── page.jsx <- A dedicated page to redirect the user back to once authentication is complete
| ├── home/
| | └── page.jsx <- Your home page the user goes to once autenticated
| ├── public/
│ | └── silent-check-sso.html
| ├── layout.jsx <- Entry point of your app before the user sees any actual pages
| └── page.jsx <- Your login page the user is brought to when they need to authenticate
|
├── tidecloak.json <- Where your Tidecloak configuration sits
├── next.config.json
├── middleware.js <- Run on each page navigation - this is where the Tideccloak token is verified
└── package.json3. Test your app!
npm run dev
Here it is - localhost:3000 🎉
Expanding from the template
Implementing encryption/decryption
You will first need to create the required realm roles that enable each user to encrypt/decrypt their own date of births.
[!NOTE] You have already completed the pre-requisites asked for in the documentation to set up encrypt/decrypt roles AND also set up the required client.
TideCloak lets you protect sensitive fields with tag-based encryption. Pass in an array of { data, tags } objects and receive encrypted strings (or vice versa).
Syntax Overview
// Encrypt payloads:
const encryptedArray = await doEncrypt([
{ data: /* string */, tags: ['tag1', 'tag2'] },
// …
]);
// Decrypt blobs:
const decryptedArray = await doDecrypt([
{ encrypted: /* string from doEncrypt */, tags: ['tag1', 'tag2'] },
// …
]);Order guarantee: the returned array matches the input order.
- Encryption requires access token roles
_tide_<tag>.selfencryptfor each tag. - Decryption requires access token roles
_tide_<tag>.selfdecryptfor each tag.
References
This 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)
Using the useTideCloak Hook
Use this hook anywhere to manage auth:
'use client'
import { useTideCloak } from '@tidecloak/nextjs';
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. |
Guard Components
Use out-of-the-box components to show or hide content:
'use client'
import { Authenticated, Unauthenticated } from '@tidecloak/nextjs';
function Dashboard() {
return (
<>
<Authenticated>
<h1>Dashboard</h1>
{/* Protected widgets */}
</Authenticated>
<Unauthenticated>
<p>Please log in to access the dashboard.</p>
</Unauthenticated>
</>
);
}<Authenticated>: renders children only whenauthenticated === true<Unauthenticated>: renders children only whenauthenticated === false
Route Protection with TideCloak
TideCloak provides server-side route protection for both the Pages Router and the App Router in Next.js.
Options
config(TidecloakConfig): Your Tidecloak adapter JSON (downloaded from your TideCloak client settings).protectedRoutes(ProtectedRoutesMap): Map of path patterns to arrays of required roles.onRequest(ctx: { token: string | null }, req: NextRequest) => NextResponse | voidHook before auth logic; can short-circuit by returning aNextResponse.onSuccess(ctx: { payload: Record<string, any> }, req: NextRequest) => NextResponse | voidHook after successful auth & role checks; override the response by returning one.onFailure(ctx: { token: string | null }, req: NextRequest) => NextResponse | voidHook when auth or role check fails; return aNextResponseto override.onError(err: any, req: NextRequest) => NextResponseHook for unexpected errors in middleware logic.
Next.js 16+ (proxy.ts)
Create proxy.ts at your project root. Proxy runs on Node.js runtime.
import { NextResponse } from 'next/server';
import tidecloakConfig from './tidecloakAdapter.json';
import { createTideCloakProxy } from '@tidecloak/nextjs/server';
export const proxy = createTideCloakProxy({
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)),
});Important: Do NOT add
export const configto proxy.ts - it's not supported and will cause errors. Proxy files always run on Node.js runtime and don't need a matcher config.
Next.js 13-15 (middleware.ts)
Create middleware.ts at your project root. Middleware runs on Edge runtime.
import { NextResponse } from 'next/server';
import tidecloakConfig from './tidecloakAdapter.json';
import { createTideCloakMiddleware } from '@tidecloak/nextjs/server';
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/(.*)',
],
};Flow:
- Bypass any
publicRoutes - Read the
kcTokencookie - Invoke
onRequesthook (if provided) - Match path against
protectedRoutespatterns - Verify signature, issuer, and roles via
verifyTideCloakToken() - On success:
onSuccesshook orNextResponse.next() - On failure:
onFailurehook or default 403 response - On unexpected errors:
onErrorhook
Server‑Side Token Verification
You can verify TideCloak-issued JWTs on your server or API routes using verifyTideCloakToken:
import { verifyTideCloakToken } from '@tidecloak/nextjs/server';
// Returns the decoded payload if valid and roles pass, otherwise null
const payload = await verifyTideCloakToken(
config, // Your TideCloak adapter JSON
token, // Raw access token to verify
['admin', 'user'] // Optional roles; user must have at least one
);
if (!payload) {
// Invalid token or insufficient roles
}Under the hood, it uses jose for cryptographic verification and key management:
import { jwtVerify, createLocalJWKSet, createRemoteJWKSet } from 'jose';
export async function verifyTideCloakToken(config, token, allowedRoles = []) {
// Implementation checks token presence, issuer, signature,
// authorized party (azp), and at least one allowed role.
}Parameters:
config(object): Your TideCloak adapter JSON (parsed Tidecloak client adapter config).token(string): Access token string to verify.allowedRoles(string[], optional): Array of realm or client roles; user must have at least one.
Returns:
Promise<object | null>: Decoded JWT payload if valid and role check passes; otherwisenull.
Example: Protecting an API Route
Protect your server-side endpoints by verifying the JWT before proceeding.
Pages Router
// 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) {
// Extract token from cookie or Authorization header
const token = req.cookies.kcToken || req.headers.authorization?.split(' ')[1] || '';
// Verify signature, issuer, and roles (e.g., 'user')
const payload = await verifyTideCloakToken(config, token, ['user']);
if (!payload) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Proceed with secure logic
res.status(200).json({ data: 'Secure data response' });
}App Router
// 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) {
// Extract token from cookie
const token = req.cookies.get('kcToken')?.value || '';
// Verify signature, issuer, and roles (e.g., 'user')
const payload = await verifyTideCloakToken(config, token, ['user']);
if (!payload) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
// Proceed with secure logic
return NextResponse.json({ data: 'Secure data response' });
}Advanced & Best Practices
- Auto-Refresh: built into the provider-no manual timers.
- Error Handling: use the
initErrorvalue 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.

