Skip to Content

React Integration

⚡ 12 min read

Vite + React SPA with redirect-based Transcodes auth — CDN script, transcodes.redirectToSignIn(), callback exchange, client-side route guards.

Canonical walkthrough: Signin · API details: Redirect API

Browser only. The SDK uses IndexedDB and WebAuthn. Do not import or call transcodes during SSR. If you share components with Next.js, mark them 'use client'.


Before you start

RequirementWhere
Project IDConsole → Authentication Kit → Installation Guide
CDN script snippetSame panel — paste into index.html
transcodes.d.tsConsole → Project Setup → download TypeScript definitions
SDK Redirect OriginsConsole → Configuration → Domains — required when sign-in returns to your origin with ?sid=
At least one memberConsole → RBAC → Users — end users sign in as members

If redirectUri / callback URL is not registered, MFA succeeds but the return redirect fails. Match Step 1.


Load the SDK (CDN)

Paste the Console snippet into index.html before your app bundle:

index.html
<script src="https://cdn.transcodes.link/%VITE_TRANSCODES_PROJECT_ID%/webworker.js" defer ></script>
.env
VITE_TRANSCODES_PROJECT_ID=proj_abc123xyz

Vite replaces %VITE_*% in index.html at build time. After load, window.transcodes exposes redirectToSignIn, handleSignInCallback, token, on, …

Add transcodes.d.ts to your project (e.g. src/types/) and include it in tsconfig.json.


Sign-in flow

1. Start sign-in (button)

src/components/SignInButton.tsx
export function SignInButton() { return ( <button type='button' onClick={() => transcodes.redirectToSignIn()}> Sign in </button> ); }

Optional explicit callback:

transcodes.redirectToSignIn({ redirectUri: `${window.location.origin}/auth/callback`, });

2. Handle callback on every app load

Safe to run on every page — no session change when ?sid= is absent (success: false, AUTH_CANCELLED).

src/App.tsx
import { useEffect } from 'react'; export function App() { useEffect(() => { void transcodes.handleSignInCallback().then((result) => { if (result?.success) { // JWT stored — AUTH_STATE_CHANGED fires } }); }, []); return <Routes />; }

3. Subscribe to auth state

useEffect(() => { const off = transcodes.on( 'AUTH_STATE_CHANGED', ({ isAuthenticated, member }) => { setAuthed(isAuthenticated); setMember(member ?? null); } ); return () => off(); }, []);

transcodes.token.isAuthenticated() is async — use it for initial render, not sync if (transcodes.token.hasToken()) alone.

Details: Step 3: Redirect · Step 4: Events


Auth provider (optional)

src/context/AuthContext.tsx
import { createContext, useContext, useEffect, useState, type ReactNode, } from 'react'; type AuthContextValue = { isAuthenticated: boolean; isLoading: boolean; signIn: () => void; signOut: () => Promise<void>; }; const AuthContext = createContext<AuthContextValue | null>(null); export function AuthProvider({ children }: { children: ReactNode }) { const [isAuthenticated, setIsAuthenticated] = useState(false); const [isLoading, setIsLoading] = useState(true); useEffect(() => { void transcodes.handleSignInCallback(); void transcodes.token.isAuthenticated().then((v) => { setIsAuthenticated(v); setIsLoading(false); }); return transcodes.on( 'AUTH_STATE_CHANGED', ({ isAuthenticated: authed }) => { setIsAuthenticated(authed); } ); }, []); return ( <AuthContext.Provider value={{ isAuthenticated, isLoading, signIn: () => transcodes.redirectToSignIn(), signOut: () => transcodes.token.signOut(), }} > {children} </AuthContext.Provider> ); } export function useAuth() { const ctx = useContext(AuthContext); if (!ctx) throw new Error('useAuth must be used within AuthProvider'); return ctx; }

Protected routes (React Router)

import { Navigate } from 'react-router-dom'; import { useAuth } from '@/context/AuthContext'; function ProtectedRoute({ children }: { children: React.ReactNode }) { const { isAuthenticated, isLoading } = useAuth(); if (isLoading) return null; if (!isAuthenticated) return <Navigate to='/login' replace />; return children; }

Step-up and Console

Member must already be signed in (SDK session / Bearer token).

const res = await transcodes.redirectToStepUp({ resource: 'documents', action: 'delete', }); const gate = res.payload[0]; const ok = res.success && (gate?.decision === 'allow' || (gate?.decision === 'stepup' && gate?.status === 'verified')); if (ok) { // run sensitive action } transcodes.redirectToConsole();

Step-up Auth · Console


Call your backend with the member JWT

const token = await transcodes.token.getAccessToken(); await fetch('/api/me', { headers: { Authorization: `Bearer ${token}` }, });

Verify on the server with ES256 + project JWK — Step 5: Server-side JWT.


Common mistakes

MistakeFix
if (transcodes.token.isAuthenticated()) without awaitAlways await transcodes.token.isAuthenticated()
SDK in Server Components / SSRClient only — useEffect or 'use client'
Callback URL not in SDK Redirect OriginsRegister origin in Console
Step-up before sign-inredirectToStepUp requires active session
Calling transcodes before script loadsWait for DOMContentLoaded or user gesture

Other frameworks

Last updated on