plugins/auth0/skills/auth0-express/SKILL.md
Use when adding authentication (login, logout, protected routes) to Express.js web applications - integrates express-openid-connect for session-based auth.
npx skillsauth add auth0/agent-skills auth0-expressInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Add authentication to Express.js web applications using express-openid-connect.
auth0-quickstart skill firstauth0-react, auth0-vue, or auth0-angular for client-side authauth0-nextjs skill which handles both client and serverauth0-react-native for React Native/Exponpm install express-openid-connect dotenv
For automated setup with Auth0 CLI, see Setup Guide for complete scripts.
For manual setup:
Create .env:
SECRET=<openssl-rand-hex-32>
BASE_URL=http://localhost:3000
CLIENT_ID=your-client-id
CLIENT_SECRET=your-client-secret
ISSUER_BASE_URL=https://your-tenant.auth0.com
AUDIENCE=https://your-api-identifier # only required if calling external APIs (Step 3a)
Generate secret: openssl rand -hex 32
Update your Express app (app.js or index.js):
require('dotenv').config();
const express = require('express');
const { auth, requiresAuth } = require('express-openid-connect');
const app = express();
// Configure Auth0 middleware
app.use(auth({
authRequired: false, // Don't require auth for all routes
auth0Logout: true, // Enable logout endpoint
secret: process.env.SECRET,
baseURL: process.env.BASE_URL,
clientID: process.env.CLIENT_ID,
issuerBaseURL: process.env.ISSUER_BASE_URL,
clientSecret: process.env.CLIENT_SECRET
}));
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
Calling external APIs? If you need an access token for a downstream API, you must add
authorizationParams— see Step 3a below.
This automatically creates:
/login - Login endpoint/logout - Logout endpoint/callback - OAuth callbackWhen you need an access token for an external API, audience must go inside authorizationParams — putting it at the top level is silently ignored and no access token is issued.
// SDK auto-loads SECRET, BASE_URL, CLIENT_ID, ISSUER_BASE_URL, CLIENT_SECRET from env vars
app.use(auth({
authRequired: false,
auth0Logout: true,
authorizationParams: { // ← required for access tokens
response_type: 'code', // ← required: authorization code flow
audience: process.env.AUDIENCE, // ← API identifier (never top-level)
scope: 'openid profile email'
}
}));
Then access the token in your route:
app.get('/api-call', requiresAuth(), async (req, res) => {
const { access_token } = req.oidc.accessToken; // object, not a string
const response = await fetch('https://your-api.com/data', {
headers: { Authorization: `Bearer ${access_token}` }
});
res.json(await response.json());
});
// Public route
app.get('/', (req, res) => {
res.send(req.oidc.isAuthenticated() ? 'Logged in' : 'Logged out');
});
// Protected route
app.get('/profile', requiresAuth(), (req, res) => {
res.send(`
<h1>Profile</h1>
<p>Name: ${req.oidc.user.name}</p>
<p>Email: ${req.oidc.user.email}</p>
<pre>${JSON.stringify(req.oidc.user, null, 2)}</pre>
<a href="/logout">Logout</a>
`);
});
// Login/logout links
app.get('/', (req, res) => {
res.send(`
${req.oidc.isAuthenticated() ? `
<p>Welcome, ${req.oidc.user.name}!</p>
<a href="/profile">Profile</a>
<a href="/logout">Logout</a>
` : `
<a href="/login">Login</a>
`}
`);
});
Start your server:
node app.js
Visit http://localhost:3000 and test the login flow.
| Mistake | Fix |
|---------|-----|
| Forgot to add callback URL in Auth0 Dashboard | Add /callback path to Allowed Callback URLs (e.g., http://localhost:3000/callback) |
| Missing or weak SECRET | Generate secure secret with openssl rand -hex 32 and store in .env as SECRET |
| Setting authRequired: true globally | Set to false and use requiresAuth() middleware on specific routes |
| App created as SPA type in Auth0 | Must be Regular Web Application type for server-side auth |
| Session secret exposed in code | Always use environment variables, never hardcode secrets |
| Wrong baseURL for production | Update BASE_URL to match your production domain |
| Not handling logout returnTo | Add your domain to Allowed Logout URLs in Auth0 Dashboard |
| audience as a top-level config key | Move audience inside authorizationParams with response_type: 'code' and scope — top-level audience is silently ignored, no access token is issued |
| req.oidc.accessToken used as a string | It is an object — destructure with const { access_token } = req.oidc.accessToken |
auth0-quickstart - Basic Auth0 setupauth0-migration - Migrate from another auth providerauth0-mfa - Add Multi-Factor Authenticationauth0-cli - Manage Auth0 resources from the terminalMiddleware Options:
authRequired - Require auth for all routes (default: false)auth0Logout - Enable /logout endpoint (default: false)secret - Session secret (required)baseURL - Application URL (required)clientID - Auth0 client ID (required)issuerBaseURL - Auth0 tenant URL (required)Request Properties:
req.oidc.isAuthenticated() - Check if user is logged inreq.oidc.user - User profile objectreq.oidc.accessToken - Access token object ({ access_token, token_type, expires_in }); expires_in is seconds remaining. Destructure with const { access_token } = req.oidc.accessToken. Also exposes isExpired() and refresh() methods. Only populated when authorizationParams with audience + response_type: 'code' is configuredreq.oidc.idToken - ID tokenreq.oidc.refreshToken - Refresh tokenCommon Use Cases:
requiresAuth() middleware (see Step 4)req.oidc.isAuthenticated()req.oidc.userdevelopment
Use when adding login, logout, and user profile to a Laravel web application using session-based authentication - integrates auth0/login (laravel-auth0) for guard-based auth with auto-registered routes.
tools
Use when securing Laravel API endpoints with JWT Bearer token validation, scope/permission checks, or stateless auth - integrates auth0/login (laravel-auth0) with the AuthorizationGuard for REST APIs receiving access tokens from SPAs, mobile apps, or other clients. Triggers on: Laravel API auth, auth0.authorizer, AuthorizationGuard, Laravel JWT, stateless Bearer.
development
Use when adding Auth0 authentication to a Flutter web application — integrates the auth0_flutter SDK (web platform) for browser-based authentication using redirect login, popup login, and credential caching.
development
Use when adding Auth0 authentication to a Flutter mobile application (iOS/Android) — integrates the auth0_flutter SDK (native platform) for Web Auth login/logout via the system browser, with secure credential storage and biometric protection through the CredentialsManager.