Skip to main content
react v1.0.0 core v14.0.0

Token Setup

Configure authentication tokens for NativeFrame frontend components

Authentication tokens are required for all NativeFrame operations. Tokens identify users, define permissions, and secure your video streams. This guide explains how to obtain tokens from your backend and use them in your frontend application.

How Authentication Works

NativeFrame uses a token-based authentication system where:

  1. Your backend server requests tokens from the NativeFrame Auth Service
  2. Your frontend application receives these tokens from your backend
  3. The frontend passes tokens to NativeFrame components for authentication
┌──────────┐     1. Request token      ┌──────────────────┐
│ Frontend │ ─────────────────────────▶│ Your Backend │
└──────────┘ └──────────────────┘
▲ │
│ │ 2. Request from Auth Service
│ ▼
│ 4. Return token ┌──────────────────┐
└────────────────────────────────│ NativeFrame API │
└──────────────────┘

Prerequisites


Token Types and Scopes

Tokens define what actions a user can perform. Choose the appropriate scope based on your use case.

Available Scopes

ScopeDescriptionPermissions
broadcasterCreate public broadcastsCreate calls, produce/consume video and audio
viewerWatch public streamsConsume video and audio only
private-broadcasterCreate private broadcasts (1-to-many)Create calls, produce/consume (max 1 producer)
private-viewerParticipate in private callsProduce and consume in private calls
conference-ownerHost group video callsCreate/manage conferences (max 10 producers)
conference-participantJoin group video callsParticipate in conferences
adminModerate callsConsume and manage peer permissions

Choosing the Right Scope

Use CaseRecommended Scope
Live streaming to many viewersbroadcaster (host) + viewer (audience)
One-on-one video callsprivate-broadcaster + private-viewer
Group video meetingsconference-owner (host) + conference-participant (attendees)
Private streaming with interactionprivate-broadcaster + private-viewer

Permission Details

Permissionbroadcasterviewerprivate-viewerconference-ownerconference-participant
Create callsYesNoNoYesNo
Produce videoYesNoYesYesYes
Produce audioYesNoYesYesYes
Consume videoYesYesYesYesYes
Consume audioYesYesYesYesYes

Using Tokens in Your Application

Creating an AuthClient (React)

The useAuthClient hook creates an authentication client that manages your token:

/**
* CORE VIDEO CLIENT IMPORTS
*
* These are the fundamental building blocks from @video/video-client-react:
*
* 1. context: Provides React Context providers for sharing state
* - Think of Context as a way to share data across your component tree
* - Without Context, you'd need to pass props through every level
*
* 2. hooks: Custom React hooks for video functionality
* - Hooks are reusable functions that manage state and side effects
* - Video client hooks handle complex video/audio logic for you
*
* 3. types: TypeScript type definitions
* - These help catch errors at compile-time
* - Provide autocomplete and documentation in your IDE
*/


import { context, hooks, types } from "@video/video-client-react";
  1. useAuthClient - Creates an AuthClient instance from your token
  2. usePreviewPlayer - Sets up camera/microphone and preview
  3. context providers - Share auth and media state with child components

Basic Usage

import { hooks } from "@video/video-client-react";

const { useAuthClient } = hooks;

function MyBroadcaster({ token, backendEndpoint, streamKey }) {
// Create the auth client from your token
const authClient = useAuthClient(token);

// Configure call options with the auth client
const callOptions = {
streamKey,
backendEndpoints: [backendEndpoint],
auth: authClient, // Pass auth client here
user: { userId: "user-123", displayName: "Broadcaster" }
};

// Use callOptions with createCall, JoinCallButton, etc.
}

Passing AuthClient to Components

The authClient is passed via the auth property in call options:

  /**
* STEP 2: Set Up Authentication
*
* The useAuthClient hook creates an authentication client that:
* - Validates your token with the backend
* - Handles automatic token refresh when it expires
* - Provides credentials for all API requests
*
* This is like showing your ID before entering a secure building.
* Without valid authentication, you can't create calls or broadcast.
*/
const authClient = useAuthClient(token);
  /**
* STEP 3: Configure Call Options
*
* These options define HOW your call will be created:
*
* - streamKey: Your unique broadcast identifier
* - user: Information about who's broadcasting
* - userId: Unique ID (can be any string, often from your auth system)
* - displayName: Name shown to other participants
* - backendEndpoints: Array of server URLs to connect to
* - auth: Authentication client (proves you have permission)
*
* Think of this like filling out a registration form before
* joining a meeting.
*/
const callOptions: types.CallOptions = {
streamKey,
user: { userId: "123", displayName: "John Doe" },
backendEndpoints: [backendEndpoint],
auth: authClient,
};

/**
* STEP 4: Configure Broadcast Options
*
* These options control your broadcast stream:
*
* - streamName: Name for this broadcast stream
* - "default" is the standard name for main camera/mic
* - You can have multiple streams (e.g., "screenshare")
* - Each stream name must be unique within a call
*
* The streamName helps viewers distinguish between different
* types of content (main camera vs. screen share).
*/
const broadcastOptions: types.BroadcastOptions = {
streamName: "default",
}

Viewer Example

For viewers joining a call:

import { useState } from "react";
import { components, hooks, context } from "@video/video-client-react";

const { JoinCallButton, EndCallButton } = components;
const { useAuthClient } = hooks;
const { CallAPIProvider } = context;

function ViewStream({ callId, token, backendEndpoint }) {
const [call, setCall] = useState(null);
const authClient = useAuthClient(token);

if (!authClient) return <div>Loading...</div>;

if (!call) {
return (
<JoinCallButton
callId={callId}
joinCallOptions={{
auth: authClient,
backendEndpoints: [backendEndpoint],
user: { userId: "viewer-123", displayName: "Viewer" }
}}
setCall={setCall}
/>
);
}

return (
<CallAPIProvider callAPI={call}>
{/* Your viewer UI */}
<EndCallButton onDisposed={() => setCall(null)} />
</CallAPIProvider>
);
}

Token Refresh

Tokens expire after a set period (typically 5-15 minutes). To maintain uninterrupted sessions, implement token refresh.

Implementing Token Refresh (React)

Pass a refresh callback to useAuthClient:

import { hooks } from "@video/video-client-react";

const { useAuthClient } = hooks;

function MyComponent({ initialToken, backendEndpoint }) {
// Function to fetch a new token from your backend
const refreshToken = async () => {
const response = await fetch('/api/refresh-token');
const { token } = await response.json();
return token;
};

// Pass both the initial token and refresh function
const authClient = useAuthClient(initialToken, refreshToken);

// authClient will automatically refresh when the token expires
}

Handling Refresh Failures

const refreshToken = async () => {
try {
const response = await fetch('/api/refresh-token');
if (!response.ok) {
throw new Error('Token refresh failed');
}
const { token } = await response.json();
return token;
} catch (error) {
// Handle refresh failure (e.g., redirect to login)
console.error('Token refresh failed:', error);
window.location.href = '/login';
throw error;
}
};

Security Best Practices

Token Storage

MethodSecurity LevelRecommended For
Memory onlyHighShort-lived sessions, highest security
sessionStorageMediumSingle-tab applications
HttpOnly cookiesHighServer-rendered applications

Backend Token Endpoint

Your backend should:

  1. Authenticate users before issuing tokens
  2. Request appropriate scopes based on user role
  3. Set reasonable TTLs (5-15 minutes)
  4. Never expose credentials to the frontend
// Your backend API endpoint
app.post('/api/get-video-token', authenticateUser, async (req, res) => {
const { scope } = req.body; // 'broadcaster' or 'viewer'

// Request token from NativeFrame Auth Service
const response = await fetch(`$nativeframe.com/auth/v1/access-tokens`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${CUSTOMER_TOKEN}`, // Your secret token
'Content-Type': 'application/json'
},
body: JSON.stringify({
scopes: [scope],
ttl: 600, // 10 minutes
userId: req.user.id
})
});

const { token } = await response.json();
res.json({ token, backendEndpoint: YOUR_ENDPOINT });
});

What NOT to Do

// BAD: Hardcoded token
const authClient = useAuthClient("eyJhbGciOiJIUzI1NiIs...");

// BAD: Token in source code
const TOKEN = process.env.REACT_APP_VIDEO_TOKEN;
const authClient = useAuthClient(TOKEN);

// BAD: Exposing backend credentials
const response = await fetch('https://api.nativeframe.com/auth/v1/access-tokens', {
headers: { 'Authorization': `Bearer ${CUSTOMER_SECRET}` } // NEVER do this!
});
// GOOD: Fetch token from your backend
const response = await fetch('/api/get-video-token');
const { token } = await response.json();
const authClient = useAuthClient(token);

Troubleshooting

Common Errors

"Authentication failed" / 401 Error

Cause: Token is invalid, expired, or malformed.

Solutions:

  • Verify your backend is correctly fetching tokens from the Auth Service
  • Check that the token hasn't expired
  • Ensure the token is being passed correctly to the AuthClient

"User does not have permission" / 403 Error

Cause: Token scope doesn't match the operation.

Solutions:

  • Verify the token has the correct scope for the operation
  • Broadcaster operations require broadcaster or private-broadcaster scope
  • Viewer operations require viewer or private-viewer scope

Token expires during session

Cause: Token TTL exceeded without refresh.

Solutions:

  • Implement token refresh callback
  • Increase TTL when creating tokens (up to your security requirements)
  • Handle the error gracefully and prompt user to reconnect

Debugging Tips

  1. Inspect token contents: Use jwt.io to decode and inspect JWT tokens
  2. Check network requests: Verify tokens are being sent in API calls
  3. Verify scope: Ensure the token scope matches your intended operation
  4. Check expiration: Verify the token hasn't expired (exp claim in JWT)
// Debug: Log token info (development only)
function debugToken(token) {
try {
const payload = JSON.parse(atob(token.split('.')[1]));
console.log('Token payload:', payload);
console.log('Expires:', new Date(payload.exp * 1000));
console.log('Scopes:', payload['@video/token']?.scopes);
} catch (e) {
console.log('Not a JWT token');
}
}

API Reference

useAuthClient (React)

function useAuthClient(
token: string | null,
refresh?: () => Promise<string>
): AuthClient
ParameterTypeDescription
tokenstring | nullJWT or AccessToken string
refresh() => Promise<string>Optional callback to fetch new token

Returns: AuthClient instance for use in call options.

BaseAuthClient (Vanilla JS)

class BaseAuthClient {
constructor(
token: string | null,
refresh?: () => Promise<string>
)
}
ParameterTypeDescription
tokenstring | nullJWT or AccessToken string
refresh() => Promise<string>Optional callback to fetch new token

Next Steps

Now that you understand token authentication: