Integration Guides
⚡ 12 min readIntegrate Transcodes WebAuthn/Passkey authentication into your application. Choose your framework below.
Choose Your Framework
Framework Comparison
| Framework | Build Tool | TypeScript | SSR | State Management |
|---|---|---|---|---|
| React | Vite | Yes | No | Context API |
| Next.js | Next.js | Yes | Yes | Context API |
| Vue.js | Vite | Yes | No | Composables / Pinia |
| Vanilla JS | Vite | No | No | Class / Module |
Universal Setup
All integrations follow the same core steps:
1. Add SDK Script
Add the Transcodes SDK to your HTML. For Vite projects, use environment variable placeholders:
<script
type="module"
src="https://cdn.transcodes.link/%VITE_TRANSCODES_PROJECT_ID%/webworker.js"
></script>For Next.js, use the next/script component:
<Script
src={`https://cdn.transcodes.link/${process.env.NEXT_PUBLIC_TRANSCODES_PROJECT_ID}/webworker.js`}
strategy='beforeInteractive'
/>2. Set Environment Variables
Vite (React, Vue, Vanilla):
VITE_TRANSCODES_PROJECT_ID=proj_abc123xyzNext.js:
NEXT_PUBLIC_TRANSCODES_PROJECT_ID=proj_abc123xyz3. Open Login Modal
const result = await transcodes.openAuthLoginModal({
projectId: 'proj_abc123xyz',
});
if (result.success) {
const { token, user } = result.payload[0];
console.log('Logged in as:', user.email);
}4. Check Authentication Status
isAuthenticated() is an async method. Always use await.
// CORRECT: use await
const isAuth = await transcodes.token.isAuthenticated();
if (isAuth) {
// User is logged in
}5. Subscribe to Auth Events
// on() returns an unsubscribe function
const unsubscribe = transcodes.on('AUTH_STATE_CHANGED', (payload) => {
console.log('Auth state:', payload.isAuthenticated);
});
// Later: cleanup
unsubscribe();6. Sign Out
await transcodes.token.signOut();SDK API Overview
The Transcodes SDK exposes the following on the global transcodes object:
// Token Management
transcodes.token.getCurrentUser(): Promise<User | null>
transcodes.token.getAccessToken(): Promise<string | null>
transcodes.token.hasToken(): boolean
transcodes.token.hasPrivateKey(): Promise<boolean>
transcodes.token.validateToken(): boolean
transcodes.token.isAuthenticated(): Promise<boolean> // async!
transcodes.token.signOut(): Promise<void>
// User Information
transcodes.user.get(params): Promise<ApiResponse<User[]>>
// Authentication Modals
transcodes.openAuthLoginModal(params): Promise<ApiResponse<AuthResult[]>>
transcodes.openAuthModal(params): Promise<ApiResponse<null>>
transcodes.openAuthMfaModal(params): Promise<ApiResponse<AuthResult[]>>
// Event Subscription
transcodes.on(event, callback): () => void // returns unsubscribe
transcodes.off(event, callback): voidAvailable Events
| Event | Description |
|---|---|
AUTH_STATE_CHANGED | Authentication state changed (login/logout) |
TOKEN_REFRESHED | Access token was automatically refreshed |
TOKEN_EXPIRED | Token expired and cannot be refreshed |
ERROR | SDK error occurred |
TypeScript Support
All framework guides include complete TypeScript type definitions. Here’s a summary:
declare global {
const transcodes: {
token: {
getCurrentUser(): Promise<User | null>;
getAccessToken(): Promise<string | null>;
hasToken(): boolean;
hasPrivateKey(): Promise<boolean>;
validateToken(): boolean;
isAuthenticated(): Promise<boolean>;
signOut(): Promise<void>;
};
user: {
get(params: {
projectId?: string;
userId?: string;
email?: string;
fields?: string;
}): Promise<ApiResponse<User[]>>;
};
on<T extends TranscodesEventName>(
event: T,
callback: (payload: TranscodesEventMap[T]) => void
): () => void;
off<T extends TranscodesEventName>(
event: T,
callback: (payload: TranscodesEventMap[T]) => void
): void;
openAuthLoginModal(params: {
projectId?: string;
userId?: string;
}): Promise<ApiResponse<AuthResult[]>>;
openAuthModal(params: {
userId: string;
projectId?: string;
}): Promise<ApiResponse<null>>;
openAuthMfaModal(params: {
userId: string;
projectId?: string;
}): Promise<ApiResponse<AuthResult[]>>;
};
}
type TranscodesEventName =
| 'AUTH_STATE_CHANGED'
| 'TOKEN_REFRESHED'
| 'TOKEN_EXPIRED'
| 'ERROR';See individual framework guides for complete type definitions.
Common Patterns
Protected Route Pattern
All frameworks implement a similar protected route pattern:
- Check
isAuthenticated()(async) - If not authenticated, show login modal
- If login cancelled, redirect to home
- If authenticated, render protected content
React/Next.js: <ProtectedRoute> component with useEffect
Vue: <AuthGuard> component with watch
Vanilla JS: init() function with async/await
Auth State Management
| Framework | Pattern | File |
|---|---|---|
| React | Context + use() hook | AuthContext.tsx |
| Next.js | Context + 'use client' | AuthProvider.tsx |
| Vue | Composable / Pinia | useAuth.ts / auth.ts |
| Vanilla JS | Class / Module | auth.js |
Common Mistakes
// WRONG: isAuthenticated() is async
if (transcodes.token.isAuthenticated()) {
// This always runs! (Promise is truthy)
}
// CORRECT: use await
if (await transcodes.token.isAuthenticated()) {
// This correctly checks auth status
}// WRONG: Method name
await transcodes.token.logout();
// CORRECT: use signOut
await transcodes.token.signOut();Troubleshooting
SDK Not Loading
// Check if SDK is loaded
if (typeof transcodes === 'undefined') {
console.error('Transcodes SDK not loaded');
}CORS Errors
Ensure your domain is added to the allowed list in the Transcodes Dashboard :
localhost:5173(Vite dev server)localhost:3000(Next.js dev server)yourdomain.com(production)
CSP (Content Security Policy)
Add Transcodes domains to your CSP headers:
<meta
http-equiv="Content-Security-Policy"
content="
script-src 'self' https://cdn.transcodes.link;
connect-src 'self' https://api.transcodes.io;
frame-src 'self' https://auth.transcodes.io;
"
/>Next Steps
- React Integration - Vite React SPA guide
- Next.js Integration - Next.js 15 App Router guide
- Vue Integration - Vite Vue 3 guide
- Vanilla JS Integration - Pure JavaScript guide
- API Reference - Full API documentation