handleSignIn() - Initiate OAuth sign inhandleCallback() - Process OAuth callbackhandleLogout() - Sign out and clear sessionhandleRefresh() - Refresh access tokensimport {
handleSignIn,
handleCallback,
handleLogout,
handleRefresh,
} from 'authsafe-express';async function handleSignIn(
req: Request,
res: Response,
config: AuthSafeConfig,
): Promise<void>;| Parameter | Type | Description |
|---|---|---|
req | Request | Express request object |
res | Response | Express response object |
config | AuthSafeConfig | AuthSafe configuration |
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`,
});
});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 /dashboardapp.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'],
});
});async function handleCallback(
req: Request,
res: Response,
config: AuthSafeConfig,
): Promise<void>;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`,
});
});/auth/signin/auth/callback?code=...&state=...handleCallback exchanges code for tokens// OAuth error from AuthSafe
// GET /auth/callback?error=access_denied&error_description=User cancelled
// Redirects to:
// /error?error=access_denied&description=User%20cancelledapp.get('/error', (req, res) => {
const { error, description } = req.query;
res.render('error', { error, description });
});async function handleLogout(
req: Request,
res: Response,
config: AuthSafeConfig,
): Promise<void>;app.post('/auth/logout', (req, res) => {
handleLogout(req, res, {
clientId: process.env.AUTHSAFE_CLIENT_ID!,
domain: process.env.AUTHSAFE_DOMAIN!,
});
});// POST /auth/logout?returnTo=/
app.post('/auth/logout', (req, res) => {
handleLogout(req, res, {
clientId: process.env.AUTHSAFE_CLIENT_ID!,
domain: process.env.AUTHSAFE_DOMAIN!,
});
});// Frontend logout button
async function logout() {
await fetch('/auth/logout?returnTo=/', {
method: 'POST',
credentials: 'include',
});
window.location.href = '/';
}returnTo URLasync function handleRefresh(
req: Request,
res: Response,
config: AuthSafeConfig,
): Promise<void>;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!,
});
});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';
}
}// 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{
"success": true
}{
"error": "refresh_failed",
"message": "No refresh token available"
}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);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)
});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);
});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');
}
});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);
});returnTo - Where to redirect after successful sign inGET /auth/signin?returnTo=/dashboardreturnTo - Where to redirect after logoutPOST /auth/logout?returnTo=/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);
});app.use(cookieParser()); // Before auth routes// ✅ 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);import cors from 'cors';
app.use(
cors({
origin: process.env.FRONTEND_URL,
credentials: true,
}),
);