Scaffold a TideCloak-ready Next.js app with optional IAM setup and working auth - start building instantly with a live example
npm install @tidecloak/create-nextjsbash
sudo apt update && sudo apt install -y curl jq
npm init @tidecloak/nextjs@latest my-app
`
#### 2.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.json
`
$3
`npm run dev`
Here it is - localhost:3000
🎉
---
Expanding from the template
$3
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.
Set up encrypt/decrypt roles
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).
$3
`ts
// 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_ for each tag.
* Decryption requires access token roles _tide_ for each tag.
---
References
This bundle provides:
* - application-level context
* useTideCloak() hook - access tokens and auth actions
* verifyTideCloakToken() - server-side JWT verification
* / - UI guards
* doEncrypt() / doDecrypt() - tag-based encryption/decryption
* createTideCloakMiddleware() - Edge middleware for route protection (supports both Pages & App routers)
$3
Use this hook anywhere to manage auth:
`tsx
'use client'
import { useTideCloak } from '@tidecloak/nextjs';
function Header() {
const {
authenticated,
login,
logout,
token,
tokenExp,
refreshToken,
getValueFromToken,
getValueFromIdToken,
hasRealmRole,
hasClientRole,
doEncrypt,
doDecrypt,
} = useTideCloak();
return (
{authenticated ? (
<>
Logged in
>
) : (
)}
{token && (
Expires at {new Date(tokenExp * 1000).toLocaleTimeString()}
)}
);
}
`
| 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 | 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 | Encrypt or decrypt payloads via TideCloak’s built-in service. |
$3
Use out-of-the-box components to show or hide content:
`tsx
'use client'
import { Authenticated, Unauthenticated } from '@tidecloak/nextjs';
function Dashboard() {
return (
<>
Dashboard
{/ Protected widgets /}
Please log in to access the dashboard.
>
);
}
`
* : renders children only when authenticated === true
* : renders children only when authenticated === false
$3
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 | void
Hook before auth logic; can short-circuit by returning a NextResponse.
* onSuccess
(ctx: { payload: Record
Hook after successful auth & role checks; override the response by returning one.
* onFailure
(ctx: { token: string | null }, req: NextRequest) => NextResponse | void
Hook when auth or role check fails; return a NextResponse to override.
* onError
(err: any, req: NextRequest) => NextResponse
Hook for unexpected errors in middleware logic.
#### Next.js 16+ (proxy.ts)
Create proxy.ts at your project root. Proxy runs on Node.js runtime.
`ts
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 config to 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.
`ts
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:
1. Bypass any publicRoutes
2. Read the kcToken cookie
3. Invoke onRequest hook (if provided)
4. Match path against protectedRoutes patterns
5. Verify signature, issuer, and roles via verifyTideCloakToken()
6. On success: onSuccess hook or NextResponse.next()
7. On failure: onFailure hook or default 403 response
8. On unexpected errors: onError hook
#### Server‑Side Token Verification
You can verify TideCloak-issued JWTs on your server or API routes using verifyTideCloakToken:
`ts
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:
`ts
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