# Protecting an Express API

## 1. Register the API

Dashboard > Applications > APIs > Create API. The identifier (e.g. `https://YOUR-API-IDENTIFIER`) is the `audience` your clients request and your API validates.

## 2. Middleware

`npm install express-oauth2-jwt-bearer`

```
const { auth, requiredScopes } = require("express-oauth2-jwt-bearer");

const checkJwt = auth({
  audience: "YOUR-API-IDENTIFIER",
  issuerBaseURL: "https://YOUR-TENANT-DOMAIN/",
});

app.get("/api/private", checkJwt, (req, res) => {
  res.json({ sub: req.auth.payload.sub });
});
```

## 3. Scopes

Define permissions on the API (Permissions tab), e.g. `read:messages`. Grant them to the calling application (Machine to Machine > APIs tab, or via the authorized-party flow for users). Enforce:

```
app.get("/api/messages", checkJwt, requiredScopes("read:messages"), (req, res) => {
  ...
});
```

`req.auth.payload` holds the decoded claims. `sub` is the user id for user tokens.

## 4. Client side

The SPA requests `audience: "YOUR-API-IDENTIFIER"` at login and sends the access token as `your auth header Without the audience, Auth0 returns an opaque token that fails validation with a confusing error.

## 5. Diagnose 401s

- Decode the token at jwt.io: check `aud` equals your API identifier and `iss` equals `https://YOUR-TENANT-DOMAIN/`.
- `UnauthorizedError: jwt audience invalid`: wrong or missing audience.
- `jwt issuer invalid`: domain mismatch, often custom-domain vs canonical-domain confusion.
- Token expired: clock skew; keep server clocks synced.

## Checklist

- audience matches between client request, API registration, and middleware.
- Scopes defined on the API AND granted to the calling app.