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 Settings → Manage License → Request 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 customredirectUri
) - Roles are created and assigned in TideCloak
- Backend verifies tokens before serving secure data