Authorization Flow

A detailed walkthrough of the Euler Stream OAuth 2.0 authorization code flow with QR code authentication.

The Euler Stream OAuth system uses the standard OAuth 2.0 Authorization Code flow, enhanced with TikTok's QR code authentication for secure, password-less user verification.

Flow Overview

┌──────────┐     ┌──────────────┐     ┌─────────────┐     ┌────────────┐
│  Your    │     │ Euler Stream  │     │   TikTok    │     │ Euler Stream│
│   App    │     │  Dashboard   │     │  Mobile App │     │    API     │
└────┬─────┘     └──────┬───────┘     └──────┬──────┘     └─────┬──────┘
     │                  │                    │                  │
     │ 1. Redirect      │                    │                  │
     │─────────────────>│                    │                  │
     │                  │                    │                  │
     │                  │ 2. Show QR Code    │                  │
     │                  │<───────────────────│                  │
     │                  │                    │                  │
     │                  │ 3. User Scans QR   │                  │
     │                  │<───────────────────│                  │
     │                  │                    │                  │
     │                  │ 4. Generate Code   │                  │
     │                  │────────────────────────────────────-->│
     │                  │                    │                  │
     │ 5. Redirect      │                    │                  │
     │  with code       │                    │                  │
     │<─────────────────│                    │                  │
     │                  │                    │                  │
     │ 6. Exchange code for tokens           │                  │
     │─────────────────────────────────────────────────────────>│
     │                  │                    │                  │
     │ 7. Access + Refresh tokens            │                  │
     │<─────────────────────────────────────────────────────────│

Step 1: Initiate Authorization

Redirect users to the authorization endpoint with required parameters:

const authUrl = new URL('https://www.eulerstream.com/tiktok/oauth/authorize');
 
authUrl.searchParams.set('client_id', 'YOUR_CLIENT_ID');
authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'webcast:fetch webcast:live_check');
authUrl.searchParams.set('state', generateRandomState()); // CSRF protection
 
window.location.href = authUrl.toString();

Required Parameters

ParameterDescription
client_idYour OAuth client ID from the dashboard
redirect_uriMust exactly match a registered redirect URI
response_typeMust be code
scopeSpace-separated list of requested scopes

Optional Parameters

ParameterDescription
stateRandom string for CSRF protection (highly recommended)

Step 2: User Authorization Flow

When users arrive at the authorization page, they go through three steps:

Introduction Screen

Users see your application's branding and a brief explanation of what's happening. This builds trust and transparency.

Permissions Review

Users review the exact permissions (scopes) your application is requesting. Each scope is displayed with a clear description of what it allows.

QR Code Authentication

Users scan a QR code with their TikTok mobile app to authenticate. This is the same secure method TikTok uses for web login:

  1. QR code is displayed and refreshes automatically if it expires
  2. User opens TikTok app and scans the code
  3. User confirms login on their phone
  4. Authorization completes automatically

Step 3: Handle the Callback

After successful authorization, users are redirected to your redirect_uri:

https://yourapp.com/callback?code=abc123xyz&state=YOUR_STATE_VALUE

Successful Response Parameters

ParameterDescription
codeAuthorization code (valid for ~10 minutes)
stateThe state value you provided (verify this!)

Error Response Parameters

If authorization fails or the user cancels:

https://yourapp.com/callback?error=access_denied&error_description=User%20cancelled&state=YOUR_STATE_VALUE
ParameterDescription
errorError code (see Error Codes)
error_descriptionHuman-readable error description
stateThe state value you provided

Step 4: Exchange Code for Tokens

Exchange the authorization code for access and refresh tokens:

interface TokenResponse {
  access_token: string;
  refresh_token: string;
  token_type: 'Bearer';
  expires_in: number; // seconds until access token expires (3600 = 1 hour)
  scope: string;
}
 
async function exchangeCodeForTokens(code: string): Promise<TokenResponse> {
  const response = await fetch('https://tiktok.eulerstream.com/tiktok/oauth/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code: code,
      redirect_uri: 'https://yourapp.com/callback',
      client_id: process.env.OAUTH_CLIENT_ID!,
      client_secret: process.env.OAUTH_CLIENT_SECRET!,
    }),
  });
 
  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error_description || 'Token exchange failed');
  }
 
  return response.json();
}

Token Request Parameters

ParameterDescription
grant_typeMust be authorization_code
codeThe authorization code from the callback
redirect_uriMust match the original request
client_idYour OAuth client ID
client_secretYour OAuth client secret

Step 5: Store and Use Tokens

After receiving tokens, store them securely:

interface UserTokens {
  accessToken: string;
  refreshToken: string;
  expiresAt: Date;
  scope: string[];
}
 
function storeTokens(response: TokenResponse): UserTokens {
  const expiresAt = new Date(Date.now() + response.expires_in * 1000);
 
  return {
    accessToken: response.access_token,
    refreshToken: response.refresh_token,
    expiresAt,
    scope: response.scope.split(' '),
  };
}

Use the access token for API requests:

async function makeAuthenticatedRequest(
  endpoint: string,
  accessToken: string,
  body?: object
) {
  const response = await fetch(`https://tiktok.eulerstream.com${endpoint}`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: body ? JSON.stringify(body) : undefined,
  });
 
  return response.json();
}

Complete TypeScript Example

Here's a complete Express.js implementation:

import express from 'express';
import crypto from 'crypto';
 
const app = express();
 
// Store state tokens (use Redis in production)
const stateStore = new Map<string, { createdAt: Date }>();
 
// Step 1: Initiate OAuth flow
app.get('/auth/tiktok', (req, res) => {
  const state = crypto.randomBytes(32).toString('hex');
  stateStore.set(state, { createdAt: new Date() });
 
  const authUrl = new URL('https://www.eulerstream.com/tiktok/oauth/authorize');
  authUrl.searchParams.set('client_id', process.env.OAUTH_CLIENT_ID!);
  authUrl.searchParams.set('redirect_uri', `${process.env.APP_URL}/auth/callback`);
  authUrl.searchParams.set('response_type', 'code');
  authUrl.searchParams.set('scope', 'webcast:fetch webcast:live_check');
  authUrl.searchParams.set('state', state);
 
  res.redirect(authUrl.toString());
});
 
// Step 3: Handle callback
app.get('/auth/callback', async (req, res) => {
  const { code, state, error, error_description } = req.query;
 
  // Handle errors
  if (error) {
    return res.status(400).json({ error, error_description });
  }
 
  // Verify state (CSRF protection)
  if (!state || !stateStore.has(state as string)) {
    return res.status(400).json({ error: 'Invalid state parameter' });
  }
  stateStore.delete(state as string);
 
  // Step 4: Exchange code for tokens
  try {
    const tokenResponse = await fetch('https://tiktok.eulerstream.com/tiktok/oauth/token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'authorization_code',
        code: code as string,
        redirect_uri: `${process.env.APP_URL}/auth/callback`,
        client_id: process.env.OAUTH_CLIENT_ID!,
        client_secret: process.env.OAUTH_CLIENT_SECRET!,
      }),
    });
 
    const tokens = await tokenResponse.json();
 
    if (!tokenResponse.ok) {
      throw new Error(tokens.error_description || 'Token exchange failed');
    }
 
    // Store tokens in your database associated with the user
    // await saveUserTokens(userId, tokens);
 
    res.redirect('/dashboard?auth=success');
  } catch (err) {
    console.error('Token exchange error:', err);
    res.redirect('/auth/error');
  }
});
 
app.listen(3000);

Security Best Practices

  1. Always validate the state parameter to prevent CSRF attacks
  2. Never expose your client secret in client-side code
  3. Use HTTPS for all redirect URIs (except localhost for development)
  4. Store tokens securely - encrypt at rest, never log them
  5. Implement token refresh before access tokens expire
  6. Handle token revocation gracefully when users disconnect

Next Steps