RegisterLogin
DocsPricing
RegisterLogin
  • Getting Started
  • Introduction
  • Quick Start
  • SDKs
  • React
  • TypeScript
  • Next.js
  • Express
  • NestJS
  • Python
  • API Reference
  • Support and Resources
  • FAQ
  • Contact Support

AuthSafe

Product

HighlightFeatureIntegrationPricingFAQ

Company

AboutBlogContactSitemap

Developer

DashboardDocumentation

Legal

Terms & ConditionsPrivacyComplianceShippingCancellation

© 2026 AuthSafe. All rights reserved.

We value your privacy

This website uses cookies for anonymous analytics to help us improve your experience. No personal information is stored or shared. You can allow or reject analytics tracking at any time. See our Privacy Policy.

We use cookies for anonymous analytics. No personal info is stored. See our Privacy Policy.

Route Handlers

Pre-built handlers for OAuth authentication flow in Express.js applications.

Overview

AuthSafe provides ready-to-use route handlers for all OAuth 2.1 flows:
  1. handleSignIn() - Initiate OAuth sign in
  2. handleCallback() - Process OAuth callback
  3. handleLogout() - Sign out and clear session
  4. handleRefresh() - Refresh access tokens

Import

import {
  handleSignIn,
  handleCallback,
  handleLogout,
  handleRefresh,
} from 'authsafe-express';

handleSignIn()

Initiate OAuth sign in by redirecting to AuthSafe authorization endpoint.
async function handleSignIn(
  req: Request,
  res: Response,
  config: AuthSafeConfig,
): Promise<void>;

Parameters

ParameterTypeDescription
reqRequestExpress request object
resResponseExpress response object
configAuthSafeConfigAuthSafe configuration

Basic Usage

app.get('/auth/signin', (req, res) => {
  handleSignIn(req, res, {
    clientId: process.env.AUTHSAFE_CLIENT_ID!,
    domain: process.env.AUTHSAFE_DOMAIN!,
    redirectUri: `${process.env.APP_URL}/auth/callback`,
  });
});

With Return URL

The returnTo query parameter specifies where to redirect after sign in:
// User visits: /auth/signin?returnTo=/dashboard
app.get('/auth/signin', (req, res) => {
  handleSignIn(req, res, {
    clientId: process.env.AUTHSAFE_CLIENT_ID!,
    domain: process.env.AUTHSAFE_DOMAIN!,
    redirectUri: `${process.env.APP_URL}/auth/callback`,
  });
});
// After successful authentication, redirects to /dashboard

Custom Scopes

app.get('/auth/signin', (req, res) => {
  handleSignIn(req, res, {
    clientId: process.env.AUTHSAFE_CLIENT_ID!,
    domain: process.env.AUTHSAFE_DOMAIN!,
    redirectUri: `${process.env.APP_URL}/auth/callback`,
    scopes: ['openid', 'email', 'profile', 'admin:read'],
  });
});

What It Does

  1. Generates PKCE code challenge
  2. Generates nonce for ID token validation
  3. Creates state parameter with returnTo URL
  4. Stores code verifier in cookie
  5. Redirects to AuthSafe authorization endpoint

handleCallback()

Process OAuth callback and exchange authorization code for tokens.
async function handleCallback(
  req: Request,
  res: Response,
  config: AuthSafeConfig,
): Promise<void>;

Usage

app.get('/auth/callback', (req, res) => {
  handleCallback(req, res, {
    clientId: process.env.AUTHSAFE_CLIENT_ID!,
    clientSecret: process.env.AUTHSAFE_CLIENT_SECRET!,
    domain: process.env.AUTHSAFE_DOMAIN!,
    redirectUri: `${process.env.APP_URL}/auth/callback`,
  });
});
Client Secret Required
The callback handler requires clientSecret for token exchange.

OAuth Flow

  1. User clicks sign in
  2. Redirected to /auth/signin
  3. Redirected to AuthSafe
  4. After authentication, redirected to /auth/callback?code=...&state=...
  5. handleCallback exchanges code for tokens
  6. Tokens stored in secure HttpOnly cookies
  7. User redirected to original destination

Error Handling

The handler automatically handles OAuth errors:
// OAuth error from AuthSafe
// GET /auth/callback?error=access_denied&error_description=User cancelled

// Redirects to:
// /error?error=access_denied&description=User%20cancelled
Custom error page:
app.get('/error', (req, res) => {
  const { error, description } = req.query;
  res.render('error', { error, description });
});

handleLogout()

Sign out user and clear session cookies.
async function handleLogout(
  req: Request,
  res: Response,
  config: AuthSafeConfig,
): Promise<void>;

Usage

app.post('/auth/logout', (req, res) => {
  handleLogout(req, res, {
    clientId: process.env.AUTHSAFE_CLIENT_ID!,
    domain: process.env.AUTHSAFE_DOMAIN!,
  });
});

With Return URL

// POST /auth/logout?returnTo=/
app.post('/auth/logout', (req, res) => {
  handleLogout(req, res, {
    clientId: process.env.AUTHSAFE_CLIENT_ID!,
    domain: process.env.AUTHSAFE_DOMAIN!,
  });
});

Client-Side Logout

// Frontend logout button
async function logout() {
  await fetch('/auth/logout?returnTo=/', {
    method: 'POST',
    credentials: 'include',
  });
  window.location.href = '/';
}

What It Does

  1. Retrieves ID token from cookies
  2. Clears all auth cookies
  3. Redirects to AuthSafe OIDC logout endpoint
  4. AuthSafe redirects back to returnTo URL

handleRefresh()

Refresh expired access tokens using refresh token.
async function handleRefresh(
  req: Request,
  res: Response,
  config: AuthSafeConfig,
): Promise<void>;

Usage

app.post('/auth/refresh', (req, res) => {
  handleRefresh(req, res, {
    clientId: process.env.AUTHSAFE_CLIENT_ID!,
    clientSecret: process.env.AUTHSAFE_CLIENT_SECRET!,
    domain: process.env.AUTHSAFE_DOMAIN!,
  });
});
Requires Refresh Token
Include offline_access scope to receive refresh tokens.

Client-Side Refresh

async function refreshAccessToken() {
  const response = await fetch('/auth/refresh', {
    method: 'POST',
    credentials: 'include',
  });

  if (response.ok) {
    console.log('Token refreshed');
  } else {
    console.error('Refresh failed, redirect to login');
    window.location.href = '/auth/signin';
  }
}

Automatic Refresh

// Automatically refresh before expiration
setInterval(
  async () => {
    try {
      await fetch('/auth/refresh', {
        method: 'POST',
        credentials: 'include',
      });
    } catch (error) {
      console.error('Auto-refresh failed:', error);
    }
  },
  50 * 60 * 1000,
); // Every 50 minutes

Response

Success:
{
  "success": true
}
Error:
{
  "error": "refresh_failed",
  "message": "No refresh token available"
}

Complete Setup

import express from 'express';
import cookieParser from 'cookie-parser';
import {
  handleSignIn,
  handleCallback,
  handleLogout,
  handleRefresh,
} from 'authsafe-express';

const app = express();
app.use(cookieParser());

const authConfig = {
  clientId: process.env.AUTHSAFE_CLIENT_ID!,
  clientSecret: process.env.AUTHSAFE_CLIENT_SECRET!,
  domain: process.env.AUTHSAFE_DOMAIN!,
  redirectUri: `${process.env.APP_URL}/auth/callback`,
};

// Sign in
app.get('/auth/signin', (req, res) => {
  handleSignIn(req, res, authConfig);
});

// OAuth callback
app.get('/auth/callback', (req, res) => {
  handleCallback(req, res, authConfig);
});

// Logout
app.post('/auth/logout', (req, res) => {
  handleLogout(req, res, authConfig);
});

// Token refresh
app.post('/auth/refresh', (req, res) => {
  handleRefresh(req, res, authConfig);
});

app.listen(3000);

Advanced Examples

Conditional Redirect After Sign In

app.get('/auth/callback', async (req, res) => {
  // Custom logic before callback
  const { state } = req.query;

  await handleCallback(req, res, authConfig);

  // After callback, redirect based on user role
  // (Note: handleCallback already redirects, this is pseudo-code)
});

Logging

app.get('/auth/signin', (req, res) => {
  const returnTo = req.query.returnTo || '/';
  console.log(`[Auth] Sign in initiated, returnTo: ${returnTo}`);

  handleSignIn(req, res, authConfig);
});

app.get('/auth/callback', async (req, res) => {
  const { code, state } = req.query;
  console.log(
    `[Auth] Callback received, code: ${code ? 'present' : 'missing'}`,
  );

  await handleCallback(req, res, authConfig);
});

Custom Error Handling

app.get('/auth/callback', async (req, res) => {
  try {
    await handleCallback(req, res, authConfig);
  } catch (error) {
    console.error('Auth callback error:', error);
    res.redirect('/error?message=Authentication%20failed');
  }
});

Rate Limiting

import rateLimit from 'express-rate-limit';

const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // 5 requests per window
  message: 'Too many authentication attempts',
});

app.get('/auth/signin', authLimiter, (req, res) => {
  handleSignIn(req, res, authConfig);
});

URL Parameters

handleSignIn

  • returnTo - Where to redirect after successful sign in
GET /auth/signin?returnTo=/dashboard

handleLogout

  • returnTo - Where to redirect after logout
POST /auth/logout?returnTo=/

Security Considerations

  1. Use POST for logout - Prevent CSRF attacks
  2. Validate returnTo URLs - Prevent open redirect vulnerabilities
  3. Set secure cookies - Enable HttpOnly, SameSite, Secure flags
  4. Use HTTPS in production - Required for secure cookies
  5. Implement rate limiting - Prevent brute force attacks
  6. Log auth events - Monitor for suspicious activity

Validate Return URLs

const ALLOWED_RETURN_URLS = ['/dashboard', '/profile', '/settings'];

app.get('/auth/signin', (req, res) => {
  const returnTo = req.query.returnTo as string;

  if (returnTo && !ALLOWED_RETURN_URLS.includes(returnTo)) {
    return res.status(400).json({ error: 'Invalid return URL' });
  }

  handleSignIn(req, res, authConfig);
});

Troubleshooting

Callback Fails with "Missing code verifier"

Ensure cookies are enabled and cookie-parser is applied:
app.use(cookieParser()); // Before auth routes

Redirect Loop

Check that callback URL matches configured redirect URI:
// ✅ Correct - URLs match
redirectUri: 'http://localhost:3000/auth/callback';
app.get('/auth/callback', handleCallback);

// ❌ Wrong - URLs don't match
redirectUri: 'http://localhost:3000/callback';
app.get('/auth/callback', handleCallback);

CORS Errors

Enable credentials for cross-origin requests:
import cors from 'cors';

app.use(
  cors({
    origin: process.env.FRONTEND_URL,
    credentials: true,
  }),
);

Related

  • Setup - Initial configuration
  • Middleware - Protect routes
  • Session Management - Cookie management
  • JWT Utilities - Token verification