Skip to main content

How‑to Guide: Common Tasks with TideCloak in React

Copy‑paste snippets for the tasks you'll use most in a React SPA.


🧰 Install or scaffold

New React + Vite app (TypeScript):

npm create vite@latest my-app -- --template react-ts
cd my-app
npm install @tidecloak/react

Required file for silent SSO: public/silent-check-sso.html

<html>
<body>
<script>parent.postMessage(location.href, location.origin)</script>
</body>
</html>

🧩 Initialize the Provider (React Router)

src/main.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { TideCloakContextProvider } from '@tidecloak/react';
import adapter from '../tidecloakAdapter.json';
import Home from './pages/Home';
import RedirectPage from './pages/auth/RedirectPage';
import Dashboard from './pages/Dashboard';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<TideCloakContextProvider config={adapter}>
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/auth/redirect" element={<RedirectPage />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</BrowserRouter>
</TideCloakContextProvider>
</React.StrictMode>
);

🔗 Customize redirect URI

Default (if omitted):

`${window.location.origin}/auth/redirect`

Override it:

<TideCloakContextProvider config={{ ...adapter, redirectUri: 'https://yourapp.com/auth/callback' }}>
{/* app */}
</TideCloakContextProvider>

Ensure the target route exists in your router.

Example redirect handler src/pages/auth/RedirectPage.tsx

import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTideCloak } from '@tidecloak/react';

export default function RedirectPage() {
const { authenticated, isInitializing, logout } = useTideCloak();
const navigate = useNavigate();

useEffect(() => {
const params = new URLSearchParams(window.location.search);
if (params.get('auth') === 'failed') {
sessionStorage.setItem('tokenExpired', 'true');
logout();
}
}, []);

useEffect(() => {
if (!isInitializing) navigate(authenticated ? '/dashboard' : '/');
}, [authenticated, isInitializing, navigate]);

return (
<div style={{ minHeight:'100vh', display:'flex', alignItems:'center', justifyContent:'center' }}>
<p>Waiting for authentication...</p>
</div>
);
}

🗝️ Read token claims

import { useTideCloak } from '@tidecloak/react';

const { getValueFromToken, getValueFromIdToken } = useTideCloak();

const email = getValueFromToken('email');
const username = getValueFromIdToken('preferred_username');

📌 Check roles

import { useTideCloak } from '@tidecloak/react';

const { hasRealmRole, hasClientRole } = useTideCloak();

const isAdmin = hasRealmRole('admin');
const isEditor = hasClientRole('editor'); // defaults to your app client
const isBillingOnBackoffice = hasClientRole('billing:writer', 'backoffice-client');

♻️ Manually refresh token

import { useTideCloak } from '@tidecloak/react';

const { refreshToken } = useTideCloak();
const ok = await refreshToken();
console.log(ok ? 'Token refreshed!' : 'Refresh failed.');

(Tokens also refresh automatically as they expire.)


🔐 Encrypt data before sending

import { useTideCloak } from '@tidecloak/react';

const { doEncrypt } = useTideCloak();

const encrypted = await doEncrypt([
{ data: 'Sensitive Information', tags: ['private'] },
]);

Data type: data must be string or Uint8Array. Permissions: require _tide_<tag>.selfencrypt on the access token.


🔓 Decrypt received data

import { useTideCloak } from '@tidecloak/react';

const { doDecrypt } = useTideCloak();

const decrypted = await doDecrypt([
{ encrypted: receivedEncryptedData, tags: ['private'] },
]);

Permissions: require _tide_<tag>.selfdecrypt. Order guarantee: output preserves input order.


🧱 Route guards and gated UI

Simple gated UI:

import { Authenticated, Unauthenticated } from '@tidecloak/react';

export function Header() {
return (
<>
<Authenticated><button>Sign out</button></Authenticated>
<Unauthenticated><button>Sign in</button></Unauthenticated>
</>
);
}

Lightweight route guard:

import { useTideCloak } from '@tidecloak/react';
import { Navigate } from 'react-router-dom';

export function RequireAuth({ children }: { children: JSX.Element }) {
const { authenticated, isInitializing } = useTideCloak();
if (isInitializing) return null;
return authenticated ? children : <Navigate to="/" replace />;
}

Use it:

<Route path="/dashboard" element={<RequireAuth><Dashboard/></RequireAuth>} />

🛡️ Verify tokens on the backend (Node Express)

import express from 'express';
import type { Request, Response, NextFunction } from 'express';
import { verifyTideCloakToken } from '@tidecloak/react/server';
import adapter from '../tidecloakAdapter.json';

const app = express();

async function auth(requiredRoles: string[] = []) {
return async (req: Request, res: Response, next: NextFunction) => {
const authz = req.headers.authorization || '';
const token = authz.startsWith('Bearer ') ? authz.slice(7) : '';
const payload = await verifyTideCloakToken(adapter, token, requiredRoles);
if (!payload) return res.status(401).json({ error: 'Unauthorized' });
(req as any).user = payload;
next();
};
}

app.get('/api/secure', await auth(['user']), (req, res) => {
res.json({ data: 'Secure data response' });
});

app.listen(3001, () => console.log('API on :3001'));

⚠️ Handle auth errors

import { useEffect } from 'react';
import { useTideCloak } from '@tidecloak/react';

export function AuthErrorToast() {
const { initError } = useTideCloak();

useEffect(() => {
if (initError) {
console.error('Authentication error:', initError);
alert('Authentication failed. Please log in again.');
}
}, [initError]);

return null;
}

🧪 Local TideCloak (dev)

If you don't have a server yet, start the dev container which auto‑imports a sample realm:

sudo docker run -d -v .:/opt/keycloak/data/h2 -p 8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=password --name tidecloak tideorg/tidecloak-dev:latest

Activate a license (dev/free)

Go to your realm's Tide IdP SettingsManage LicenseRequest License and finish the flow.


✅ Quick checklist

  • public/silent-check-sso.html exists
  • <TideCloakContextProvider config={adapter}> wraps your app
  • Redirect route exists (default /auth/redirect or your custom redirectUri)
  • Roles are created and assigned in TideCloak
  • Backend verifies tokens before serving secure data