# Transcodes — Full SDK Reference > Passkey-first B2B authentication service. Integrates via **HTML CDN** (`webworker.js`) or **`@bigstrider/transcodes-sdk`** (`init({ projectId })`). Pre-built modals for login, step-up MFA, member console, and admin panel. Issues JWTs. No backend auth code required. Base URL: https://transcodes.io Dashboard: https://app.transcodes.io CDN: https://cdn.transcodes.link/{PROJECT_ID}/webworker.js --- ## What is Transcodes? Transcodes is **Authentication Infrastructure as a Service (AaaS)** for B2B internal tools. It replaces DIY WebAuthn with: - **Passkey-first login** — biometric (Face ID, Touch ID, Windows Hello) - **Step-up MFA** — verify identity before sensitive actions (payments, deletes, admin) - **Member management** — RBAC, audit logs, backup - **Zero backend** — no auth endpoints, no credential DB, no token logic --- ## Two ways to integrate ### Option A — HTML / CDN (required for PWA) Add one ` ``` ```html ``` PWA note: download `sw.js` from the Console → Web App Cluster → Installation Guide and serve at `/sw.js`. After load, all APIs are available via `window.transcodes` (or just `transcodes`). ### Option B — @bigstrider/transcodes-sdk Install once, call `init` at your client entry point (never in SSR/server components). ```bash npm install @bigstrider/transcodes-sdk ``` ```typescript import { init } from '@bigstrider/transcodes-sdk'; // Call once at client entry (e.g. main.tsx, layout.tsx with 'use client') await init({ projectId: 'YOUR_PROJECT_ID' }); // Optional: await init({ projectId: '...', customUserId: 'uid_xxx', debug: true }); ``` Then use **named exports** — not `window.transcodes`: ```typescript import { init, // await init({ projectId, customUserId?, debug? }) openAuthLoginModal, // await openAuthLoginModal({ webhookNotification? }) openAuthConsoleModal, // await openAuthConsoleModal() openAuthAdminModal, // await openAuthAdminModal({ allowedRoles }) openAuthIdpModal, // await openAuthIdpModal({ resource, action }) isAuthenticated, // await isAuthenticated() → boolean (ASYNC!) getCurrentMember, // await getCurrentMember() → Member | null getAccessToken, // await getAccessToken() → string | null hasToken, // hasToken() → boolean (SYNC) signOut, // await signOut({ webhookNotification? }) getMember, // await getMember({ email?, memberId? }) → ApiResponse on, off, // on('AUTH_STATE_CHANGED', cb) → unsubscribeFn trackUserAction, // await trackUserAction({ tag, severity?, ... }, opts?) isPwaInstalled, // isPwaInstalled() → boolean (SYNC) } from '@bigstrider/transcodes-sdk'; ``` **CDN vs @bigstrider/transcodes-sdk naming:** | Action | CDN (window.transcodes) | @bigstrider/transcodes-sdk (named export) | | ---------------------- | ------------------------------------- | ------------------------ | | Init | (auto on script load) | `init({ projectId })` | | Login modal | `transcodes.openAuthLoginModal({})` | `openAuthLoginModal({})` | | Is authenticated? | `transcodes.token.isAuthenticated()` | `isAuthenticated()` | | Get current member | `transcodes.token.getCurrentMember()` | `getCurrentMember()` | | Get access token | `transcodes.token.getAccessToken()` | `getAccessToken()` | | Sign out | `transcodes.token.signOut()` | `signOut()` | | Lookup member | `transcodes.member.get({ email })` | `getMember({ email })` | | Subscribe to events | `transcodes.on('EVENT', cb)` | `on('EVENT', cb)` | --- ## TranscodesInitOptions (@bigstrider/transcodes-sdk only) ```typescript interface TranscodesInitOptions { projectId: string; // Required — from Transcodes Console customUserId?: string; // Optional — associate with your own user ID debug?: boolean; // Optional — enable debug logging } ``` --- ## Dynamic SDK utility methods (CDN and @bigstrider/transcodes-sdk) ```typescript // Check if SDK is already initialized (avoid double-init) transcodes.isInitialized(): boolean // Update config at runtime (e.g. set memberId after login) transcodes.setConfig({ memberId?: string }): void // Build info for debugging transcodes.getBuildInfo(): { buildTimestamp: string } ``` ```javascript // Guard against double-init if (!transcodes.isInitialized()) { await transcodes.init({ projectId: 'YOUR_PROJECT_ID' }); } // Link your own member ID after login transcodes.setConfig({ memberId: 'your_internal_user_id' }); ``` --- ## Modal APIs ### openAuthLoginModal Login + signup modal. Returns `ApiResponse`. ```typescript // CDN const result = await transcodes.openAuthLoginModal({ projectId?: string; // optional — usually inferred from script URL webhookNotification?: boolean; // send Slack webhook on login. Default: false }); // @bigstrider/transcodes-sdk import { openAuthLoginModal } from '@bigstrider/transcodes-sdk'; const result = await openAuthLoginModal({ webhookNotification: false }); ``` Success response: ```typescript if (result.success) { const { token, member } = result.payload[0]; // token: string (JWT) // member: Member console.log('Logged in:', member.email); } ``` ### openAuthConsoleModal Member account management (passkeys, profile, settings). ```typescript // CDN await transcodes.openAuthConsoleModal(); // params optional // @bigstrider/transcodes-sdk import { openAuthConsoleModal } from '@bigstrider/transcodes-sdk'; await openAuthConsoleModal(); ``` ### openAuthAdminModal Admin panel — only members with `allowedRoles` can pass through. ```typescript // CDN await transcodes.openAuthAdminModal({ allowedRoles: ['admin', 'moderator'], // Required projectId?: string; }); // @bigstrider/transcodes-sdk import { openAuthAdminModal } from '@bigstrider/transcodes-sdk'; await openAuthAdminModal({ allowedRoles: ['admin'] }); ``` ### openAuthIdpModal **Step-up MFA modal** — RBAC-based. Verify identity before sensitive actions. ```typescript // Params (IdpOpenParams) interface IdpOpenParams { resource: string; // e.g. 'users', 'revenue', 'settings' action: 'create' | 'read' | 'update' | 'delete'; // CRUD action forceStepUp?: boolean; // force MFA regardless of permission. Default: false webhookNotification?: boolean; // Slack webhook on result. Default: false projectId?: string; } // CDN const result = await transcodes.openAuthIdpModal({ resource: 'users', action: 'delete', }); // @bigstrider/transcodes-sdk import { openAuthIdpModal } from '@bigstrider/transcodes-sdk'; const result = await openAuthIdpModal({ resource: 'revenue', action: 'update' }); ``` Success response: ```typescript // IMPORTANT: check BOTH result.success AND result.payload[0]?.success if (result.success && result.payload[0]?.success) { const { sid, timestamp, action } = result.payload[0]; // sid: string — Step-up Session ID; send to your server to verify // timestamp: number — Unix ms await yourApi.deleteUser({ stepUpSid: sid }); } ``` Full `IdpAuthResponse`: ```typescript interface IdpAuthResponse { success: boolean; sid?: string; // step-up session ID (on success) error?: string; // error message (on failure) timestamp: number; // Unix timestamp (ms) action?: string; // the requested action } ``` --- ## Token API CDN: `transcodes.token.*` — `@bigstrider/transcodes-sdk`: named exports (`isAuthenticated`, `getCurrentMember`, `getAccessToken`, `hasToken`, `signOut`) ```typescript // Get current authenticated member (from JWT, no API call) // Returns null if not authenticated await transcodes.token.getCurrentMember(): Promise // Get valid access token (Memory → IndexedDB → new issuance) // Returns null if unavailable/expired await transcodes.token.getAccessToken(): Promise // SYNC: check if token is in memory (fast, no I/O) transcodes.token.hasToken(): boolean // ASYNC: full validity check (Memory → IndexedDB) — ALWAYS use await! await transcodes.token.isAuthenticated(): Promise // Sign out — clears session await transcodes.token.signOut({ webhookNotification?: boolean }): Promise ``` **Critical:** `isAuthenticated()` is async. A common mistake: ```javascript // WRONG — Promise is always truthy if (transcodes.token.isAuthenticated()) { ... } // CORRECT if (await transcodes.token.isAuthenticated()) { ... } ``` --- ## Member API CDN: `transcodes.member.*` — `@bigstrider/transcodes-sdk`: `getMember` ```typescript transcodes.member.get({ projectId?: string; memberId?: string; email?: string; fields?: string; // comma-separated field names to return }): Promise> ``` ```javascript // CDN const res = await transcodes.member.get({ email: 'user@example.com' }); // @bigstrider/transcodes-sdk import { getMember } from '@bigstrider/transcodes-sdk'; const res = await getMember({ email: 'user@example.com' }); if (res.success) console.log(res.payload[0].name); ``` --- ## Events API `on()` returns an unsubscribe function. ```typescript // CDN const unsub = transcodes.on('AUTH_STATE_CHANGED', (payload) => { if (payload.isAuthenticated) { console.log('Signed in as', payload.member?.email); console.log('Token expires at', new Date(payload.expiresAt!)); } }); unsub(); // unsubscribe // @bigstrider/transcodes-sdk import { on } from '@bigstrider/transcodes-sdk'; const unsub = on('AUTH_STATE_CHANGED', (payload) => { ... }); ``` ### Event: AUTH_STATE_CHANGED ```typescript interface AuthStateChangedPayload { isAuthenticated: boolean; accessToken: string | null; expiresAt: number | null; // Unix ms member: Member | null; } ``` ### Event: TOKEN_REFRESHED ```typescript interface TokenRefreshedPayload { accessToken: string; expiresAt: number; // Unix ms } ``` ### Event: TOKEN_EXPIRED ```typescript interface TokenExpiredPayload { expiredAt: number; // Unix ms } ``` ### Event: ERROR ```typescript interface ErrorPayload { code: string; message: string; context?: string; } ``` --- ## Audit API — trackUserAction Records user actions in Transcodes audit logs (visible in Console → Audit Logs). ```typescript // CDN await transcodes.trackUserAction( { tag: string; // Required. e.g. 'user:login', 'document:create' severity?: 'low' | 'medium' | 'high'; // Default: 'low' status?: boolean; // true = success, false = failure. Default: true error?: string; // Error message when status is false metadata?: Record; // Extra data, e.g. { amount: 99.99 } page?: string; // Page URL. Default: window.location.href }, { requireAuth?: boolean; // Open login modal if not authed. Default: false webhookNotification?: boolean; // Slack webhook. Default: false } ); // @bigstrider/transcodes-sdk — same signature import { trackUserAction } from '@bigstrider/transcodes-sdk'; await trackUserAction({ tag: 'payment:process', severity: 'high', status: true, metadata: { amount: 500 } }); ``` --- ## Type Definitions ```typescript interface Member { id?: string; projectId?: string; name?: string; email?: string; role?: string; metadata?: Record; createdAt?: Date | string; updatedAt?: Date | string; } interface AuthResult { token: string; // JWT access token member: Member; } interface ApiResponse { success: boolean; payload: T; error?: string; message?: string; status?: number; } interface IdpOpenParams { resource: string; action: 'create' | 'read' | 'update' | 'delete'; forceStepUp?: boolean; webhookNotification?: boolean; projectId?: string; } interface IdpAuthResponse { success: boolean; sid?: string; error?: string; timestamp: number; action?: string; } type TranscodesEventName = | 'AUTH_STATE_CHANGED' | 'TOKEN_REFRESHED' | 'TOKEN_EXPIRED' | 'ERROR'; ``` --- ## Framework setup (quick reference) ### React + Vite ```html ``` ```bash # .env VITE_TRANSCODES_PROJECT_ID=proj_abc123xyz ``` ### Next.js App Router (CDN path) ```tsx // app/layout.tsx import Script from 'next/script'; export default function RootLayout({ children }) { return (

Not signed in

``` --- ## CDN TypeScript setup When using the CDN approach, download `transcodes.d.ts` from the Transcodes Console and place it in `types/transcodes.d.ts`. Then update `tsconfig.json`: ```json { "compilerOptions": { "typeRoots": ["./node_modules/@types", "./types"] }, "include": ["src", "types"] } ``` When using `@bigstrider/transcodes-sdk`, TypeScript types ship with the package — no extra setup needed. --- ## React AuthContext pattern (recommended) The standard pattern for React apps — works with both CDN and `@bigstrider/transcodes-sdk`. ```tsx // src/context/AuthContext.tsx import { createContext, useEffect, useState, type ReactNode } from 'react'; import { isAuthenticated as sdkIsAuthenticated, openAuthLoginModal as sdkLogin, openAuthConsoleModal as sdkConsole, openAuthIdpModal as sdkIdp, signOut as sdkSignOut, on, } from '@bigstrider/transcodes-sdk'; interface AuthContextValue { isAuthenticated: boolean; isLoading: boolean; memberId: string | null; openAuthLoginModal: () => Promise; openAuthConsoleModal: () => Promise; openAuthIdpModal: (params: { resource: string; action: 'create' | 'read' | 'update' | 'delete'; }) => Promise; signOut: () => Promise; } export const AuthContext = createContext({ /* defaults */ } as AuthContextValue); export function AuthProvider({ children }: { children: ReactNode }) { const [isAuth, setIsAuth] = useState(false); const [isLoading, setIsLoading] = useState(true); const [memberId, setMemberId] = useState(null); useEffect(() => { sdkIsAuthenticated().then((auth) => { setIsAuth(auth); setIsLoading(false); }); const unsubscribe = on('AUTH_STATE_CHANGED', ({ isAuthenticated, member }) => { setIsAuth(isAuthenticated); setMemberId(member?.id ?? null); }); return () => unsubscribe(); }, []); return ( { const result = await sdkLogin({}); if (result.success) setMemberId(result.payload[0]?.member?.id ?? null); }, openAuthConsoleModal: async () => { await sdkConsole(); }, openAuthIdpModal: async (params) => { await sdkIdp(params); }, signOut: async () => { await sdkSignOut(); setIsAuth(false); setMemberId(null); }, }}> {children} ); } ``` Wrap your app in `` and consume with `use(AuthContext)` in components. --- ## Quick start example (@bigstrider/transcodes-sdk — React) ```tsx // src/App.tsx 'use client'; // Next.js import { openAuthLoginModal, on, getCurrentMember } from '@bigstrider/transcodes-sdk'; import { useEffect, useState } from 'react'; import type { Member } from '@bigstrider/transcodes-sdk'; export default function App() { const [member, setMember] = useState(null); useEffect(() => { getCurrentMember().then(setMember); const unsub = on('AUTH_STATE_CHANGED', (p) => setMember(p.member)); return unsub; }, []); const handleLogin = async () => { const result = await openAuthLoginModal({}); if (result.success) setMember(result.payload[0].member); }; return member ?

Hello, {member.email}

: ; } ``` --- ## Step-up MFA example Use before any sensitive action (delete account, payment, admin operation). ```typescript async function deleteUser(userId: string) { // 1. Request step-up verification const mfa = await transcodes.openAuthIdpModal({ resource: 'users', action: 'delete', }); if (!mfa.success) return; // User cancelled or failed const { sid } = mfa.payload[0]; // Step-up Session ID // 2. Call your API with the SID — verify it server-side await fetch(`/api/users/${userId}`, { method: 'DELETE', headers: { Authorization: `Bearer ${await transcodes.token.getAccessToken()}`, 'X-Step-Up-Sid': sid ?? '', }, }); // 3. Log the action await transcodes.trackUserAction({ tag: 'user:delete', severity: 'high', status: true, metadata: { userId }, }); } ``` --- ## Server-side JWT verification Transcodes uses **JWKS** (JSON Web Key Set) for token verification. Fetch the public keys from the JWKS endpoint — no static file needed. ``` JWKS endpoint: https://cdn.transcodes.link/{YOUR_PROJECT_ID}/jwks.json JWT issuer: Algorithm: RS256 ``` ```javascript // Node.js — recommended approach with jwks-rsa // npm install jsonwebtoken jwks-rsa const jwt = require('jsonwebtoken'); const jwksClient = require('jwks-rsa'); const client = jwksClient({ jwksUri: 'https://cdn.transcodes.link/YOUR_PROJECT_ID/jwks.json', cache: true, cacheMaxAge: 86400000, // 24 hours }); function getKey(header, callback) { client.getSigningKey(header.kid, (err, key) => { callback(err, key?.getPublicKey()); }); } async function verifyToken(token) { return new Promise((resolve, reject) => { jwt.verify(token, getKey, { algorithms: ['RS256'], issuer: '', }, (err, decoded) => { if (err) reject(err); else resolve(decoded); }); }); } // Express middleware app.use(async (req, res, next) => { const auth = req.headers.authorization; if (!auth) return res.status(401).json({ error: 'No token' }); try { const token = auth.replace('Bearer ', ''); req.member = await verifyToken(token); next(); } catch { res.status(401).json({ error: 'Invalid token' }); } }); ``` ```python # Python — pip install pyjwt cryptography requests import jwt, requests from functools import lru_cache @lru_cache(maxsize=1) def get_jwks(): return requests.get('https://cdn.transcodes.link/YOUR_PROJECT_ID/jwks.json').json() def verify_token(token: str) -> dict: header = jwt.get_unverified_header(token) jwks = get_jwks() key = next(k for k in jwks['keys'] if k['kid'] == header['kid']) public_key = jwt.algorithms.RSAAlgorithm.from_jwk(key) return jwt.decode(token, public_key, algorithms=['RS256'], options={'verify_iss': True}, issuer='') ``` --- ## CSP (Content Security Policy) At minimum, allow the CDN for the SDK script. The exact API domains for `connect-src` and `frame-src` are listed in your Transcodes Console → Authentication Cluster → Installation Guide. ```html ``` --- ## Common mistakes ```javascript // WRONG: isAuthenticated() is async — Promise is always truthy if (transcodes.token.isAuthenticated()) { ... } // CORRECT if (await transcodes.token.isAuthenticated()) { ... } ``` ```javascript // WRONG: method doesn't exist await transcodes.token.logout(); // CORRECT await transcodes.token.signOut(); ``` ```javascript // WRONG: openAuthMfaModal doesn't exist await transcodes.openAuthMfaModal(); // CORRECT: use openAuthIdpModal for step-up MFA await transcodes.openAuthIdpModal({ resource: 'settings', action: 'delete' }); ``` ```javascript // WRONG: response uses .user (old), not .member const { token, user } = result.payload[0]; // CORRECT const { token, member } = result.payload[0]; ``` ```javascript // WRONG: openAuthIdpModal takes resource+action, not action+allowedRoles await transcodes.openAuthIdpModal({ action: 'delete', allowedRoles: ['admin'] }); // CORRECT await transcodes.openAuthIdpModal({ resource: 'users', action: 'delete' }); ``` ```javascript // WRONG: only checking outer result.success is not enough for IDP modal if (result.success) { proceedWithSensitiveAction(); } // CORRECT: check BOTH success flags if (result.success && result.payload[0]?.success) { proceedWithSensitiveAction(); } ``` ```javascript // WRONG: JWT issuer issuer: '' // CORRECT issuer: '' ``` --- ## Browser support | Browser | Minimum | | ------- | ------- | | Chrome | 67+ | | Safari | 14+ | | Firefox | 60+ | | Edge | 79+ | WebAuthn requires **HTTPS** in production. `localhost` / `127.0.0.1` work for development. --- ## What Transcodes handles vs what you handle | Transcodes handles ✅ | You handle ❌ | | ----------------------------------- | -------------------------------------- | | Passkey credential storage | XSS / CSRF protection | | JWT issuance and refresh | Application-level security | | WebAuthn ceremony (challenge/verify)| Server-side token verification | | Pre-built UI modals | HTTPS setup | | Token memory management | Rate limiting on your API | --- ## Links - [Full docs](https://transcodes.io/docs) - [API Reference](https://transcodes.io/docs/api-reference) - [Quick Integration](https://transcodes.io/docs/quick-integration) - [Passkey Login demo](https://transcodes.io/docs/demonstration/passkey-login) - [Step-up MFA demo](https://transcodes.io/docs/demonstration/stepup-mfa) - [Authentication Cluster](https://transcodes.io/docs/authentication-cluster) - [Console](https://app.transcodes.io) - [FAQ](https://transcodes.io/qnas) - [Book a demo](https://transcodes.io/booking)