Tutorial: Integrate TideCloak into an Existing React.js app
This tutorial walks you through setting up the TideCloak SDK in a React application to handle secure authentication.
1) Prerequisites
Before you begin, ensure you have the following:
- 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)
2) Install @tidecloak/react
Add the TideCloak React SDK to your project:
- NPM
- Yarn
npm install @tidecloak/react
yarn add @tidecloak/react
This bundle provides:
<TideCloakContextProvider>- application-level contextuseTideCloak()hook - access tokens and auth actions<Authenticated>/<Unauthenticated>- UI guardsdoEncrypt()/doDecrypt()- tag-based encryption/decryption
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
Wrap your app's root with <TideCloakContextProvider> to enable authentication context throughout the component tree.
If you're using React Router, your setup might look like this:
import React from 'react';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';export default function App() {return (<TideCloakContextProvider config={adapter}><BrowserRouter><Routes><Route path="/" element={<Home />} /><Route path="/auth/redirect" element={<RedirectPage />} />{/* Add additional routes here */}</Routes></BrowserRouter></TideCloakContextProvider>);}
⚠️ If you don't define a route at
/auth/redirect, and you're using the defaultredirectUri, your app will break after login/logout. Either create this route or overrideredirectUriin the provider config.If you override the
redirectUri, you must ensure that the custom path exists in your router. Otherwise, the app will redirect to a non-existent route and fail.
4) Redirect URI Handling
TideCloak supports an optional redirectUri config field. This defines where the user is sent after login/logout.
If omitted, it defaults to:
`${window.location.origin}/auth/redirect`
Example: If your app runs at
http://localhost:3000, then by default the redirect path ishttp://localhost:3000/auth/redirect.
You must create this route if you use the default, or explicitly override it:
<TideCloakContextProvider config={{ ...adapter, redirectUri: 'https://yourapp.com/auth/callback' }}><YourApp /></TideCloakContextProvider>
⚠️ If you override the
redirectUri, make sure the specified path exists in your app. Missing this route will cause failed redirects.
Example: Redirect Handling Page
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 ? '/home' : '/');}}, [authenticated, isInitializing, navigate]);return (<div style={{minHeight: '100vh',display: 'flex',alignItems: 'center',justifyContent: 'center',fontSize: '1rem',color: '#555',}}><p>Waiting for authentication...</p></div>);}
Description: This page helps finalize the login or logout flow, and also reacts to token expiration events that may have triggered a redirect. It's required if you're using the default redirectUri. If you override the redirect URI, the file is optional-but the route for the redirect must still exist in your app.
5) Using the useTideCloak Hook
Use this hook anywhere in your component tree to manage authentication:
import { useTideCloak } from '@tidecloak/react';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. |
6) Guard Components
Use these components to show or hide content based on authentication state:
import { Authenticated, Unauthenticated } from '@tidecloak/react';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
7) Encrypting & Decrypting Data
Protect sensitive payloads using tag-based encryption/decryption:
// Encrypt:const encryptedArray = await doEncrypt([{ data: { email: 'user@example.com' }, tags: ['email'] },]);// Decrypt: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 encryptionconst 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.
8) Advanced & Best Practices
- Auto-Refresh: built-in, no manual timer setup required
- Error Handling: check
initErrorfromuseTideCloak - Custom Claims: access token fields with
getValueFromToken()/getValueFromIdToken() - Role-Based UI: combine
hasRealmRole,hasClientRole, and guard components - Lazy Initialization: optionally wrap
<TideCloakContextProvider>around only protected routes
✅ Run & Test
npm start
Your React app is now connected with TideCloak authentication.