Skip to main content
This Quickstart is currently in Beta. We’d love to hear your feedback!
Prerequisites: Before you begin, ensure you have the following installed:

Get Started

This quickstart demonstrates how to protect Express.js API endpoints using JWT access tokens. You’ll build a secure API that validates Auth0 access tokens, protects routes, and implements scope- and claim-based authorization.
1

Create a new project

Create a new directory for your Express API and initialize a Node.js project.Update your package.json to use ES modules and add start scripts:
2

Install the SDK

Install @auth0/auth0-express-api along with express and dotenv:
3

Setup your Auth0 API

You need to create a new API on your Auth0 tenant and configure your environment variables.
Run the following command in your project root to create an Auth0 API:After creation, copy the Identifier and your Domain values, then create your .env file:Replace YOUR_API_IDENTIFIER with the identifier you used above (e.g., https://my-express-api.example.com).
4

Configure the JWT middleware

Register createAuth0Api() on your Express application to configure JWT validation. Then add public and protected routes.
server.js
What this does:
  • createAuth0Api() reads AUTH0_DOMAIN and AUTH0_AUDIENCE from environment variables automatically
  • requiresAuth() validates the Authorization: Bearer <token> header on each request
  • req.auth0.user contains the decoded JWT claims for authenticated requests — sub is the user’s unique identifier
5

Protect a route with a required scope

Beyond requiring a valid token, you can require a specific scope. Pass a scopes option to requiresAuth() — the SDK returns 403 insufficient_scope if the token is missing the scope.
server.js
Define the scope in your API’s Permissions tab (see Advanced Usage) and request it when obtaining the access token. For matching multiple scopes or authorizing on custom claims, the SDK also provides scopesInclude, claimEquals, claimIncludes, and claimCheck — covered in Advanced Usage.
6

Run your API

Start the development server:
Your API is now running at http://localhost:3001.
7

Test your API

Test the public endpoint (no token required):
Expected response:
To call the protected endpoint, you need an access token:
  1. Go to Auth0 DashboardApplications > APIs
  2. Select your API → Test tab
  3. Copy the generated access token
Test the protected endpoint:
Expected response:
CheckpointYou should now have a protected API. Your API:
  1. Accepts requests to public endpoints without a token
  2. Returns the protected response when a valid access token is provided
  3. Validates JWTs against your Auth0 domain and audience
  4. Exposes decoded token claims via req.auth0.user

Advanced Usage

Use scopesInclude when a route should accept any one of several scopes, or require several at once. By default it matches any of the listed scopes; pass { match: 'all' } to require all of them. Scopes can be passed as an array or a space-separated string — these examples use arrays.
server.js
When authorization depends on claims other than scope, use claimEquals, claimIncludes, or claimCheck. Each runs after requiresAuth() and returns 401 invalid_token if the claim requirement isn’t met.
server.js
If you’re using TypeScript, augment the Token interface to get type-safe access to custom claims:
server.ts
Install type support:
Enable CORS to allow your web application to call the API:
server.js
For production, specify exact allowed origins instead of wildcards.
To use scope-based authorization, first define the permissions on your API:
  1. Go to Auth0 DashboardApplications > APIs → your API
  2. Navigate to the Permissions tab
  3. Add permissions like read:messages, write:messages, read:admin
  4. Click Save
Your client application must then request these scopes when obtaining an access token. If a token lacks the required scope, the API returns 403 Forbidden.

Troubleshooting

Cause: The Authorization header is missing or malformed, so no bearer token could be extracted. Per RFC 6750, the SDK returns 401 with a bare WWW-Authenticate: Bearer header and no error body in this case. This is distinct from a token that is present but invalid or expired, which returns 401 with an invalid_token error and a JSON body (see below).Fix:
  1. Ensure the header is present: Authorization: Bearer YOUR_TOKEN
  2. Verify “Bearer” (with a capital B and a space) precedes the token
Cause: The token was not issued for this API, or the domain/audience values don’t match.Fix:
  1. Decode your token at jwt.io
  2. Check iss matches https://{yourDomain}/ (note the trailing slash)
  3. Check aud matches your AUTH0_AUDIENCE exactly
  4. Make sure you’re using an access token, not an ID token — access tokens are obtained with the audience parameter
Cause: The token does not include the required scope.Fix:
  1. Verify the scope is defined in your API’s Permissions tab in the Auth0 Dashboard
  2. Ensure the client is requesting the scope when obtaining the access token
  3. Decode the token at jwt.io and check the scope claim
Cause: dotenv is not configured, or variable names are wrong.Fix:
  1. Ensure import 'dotenv/config' is the first import in your entry file
  2. Verify .env contains AUTH0_DOMAIN and AUTH0_AUDIENCE
  3. Debug:
Cause: The @auth0/auth0-express-api SDK uses ES modules.Fix: Add "type": "module" to your package.json:📁 package.json
Or rename your server file to server.mjs.

Next Steps


Resources