An open API service indexing awesome lists of open source software.

https://github.com/makerxstudio/express-bearer

An express middleware to decode and verify JWTs from bearer authorization headers.
https://github.com/makerxstudio/express-bearer

auth express express-bearer npm package typescript

Last synced: about 1 year ago
JSON representation

An express middleware to decode and verify JWTs from bearer authorization headers.

Awesome Lists containing this project

README

          

# Express bearer

An express middleware to decode and verify JWTs from bearer authorization headers.

## What does this do?

- loads signing keys from a JWKS endpoint using [jwks-rsa](https://github.com/auth0/node-jwks-rsa#readme)
- verifies and decodes a JWT from a Bearer authorization header using [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback)
- sets `req.user` to the verified decoded JWT payload (claims)

## Usage

```ts
import { bearerTokenMiddleware, BearerConfig } from '@makerxstudio/express-bearer'

const app = express()
const config: BearerConfig = {
jwksUri: 'https://login.microsoftonline.com//discovery/v2.0/keys',
verifyOptions: {
issuer: 'https://login.microsoftonline.com//v2.0',
audience: '',
},
}

// add the bearer token middleware (to all routes)
app.use(bearerTokenMiddleware({ config }))
// or... add to a specific route
app.post('/api/admin/*', bearerTokenMiddleware({ config }))
// or... add to a specific route + make authentication mandatory
app.post('/api/admin/*', bearerTokenMiddleware({ config, tokenIsRequired: true }))

// access the user, check the roles claim
app.post('/api/admin/*', (req, res, next) => {
const roles = (req.user?.roles as string[]) ?? []
if (!roles.includes('Admin')) throw new Error('Authorization failed')
next()
})
```

The middleware will:

- Return `401 Unauthorized` when the JWT fails decoding / verification
- Return `401 Unauthorized` if there is no `Bearer {token}` authorization header and `tokenIsRequired` is set to `true` (default is `false`)

## Options

`BearerAuthOptions`:

| Option | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------- |
| `config` | The JWT handling config \*`BearerConfig` (or \*`BearerConfigCallback` for per-host config). |
| `tokenIsRequired` | Controls whether requests with no `Bearer {token}` authorization header are rejected, default: `false`. |
| `logger` | Optional logger implementation to log token validation errors, handler setup info entry etc. |

JWT handling `config`:

| Option | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `jwksUri` | The endpoint to load signing keys via [jwks-rsa](https://github.com/auth0/node-jwks-rsa#readme) |
| `verifyOptions` | The options passed into [jwt.verify](https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback) |
| `unauthorizedResponse` | Optional. Callback of type `(req: Request, res: Response) => Response)` which provides a way to customise the HTTP response when the bearer token is required and not present, or the validation fails.
If not provided, a plain text 401 Unauthorized response is returned. |
| `explicitNoIssuerValidation` | Optional. The default behaviour is to enforce issuer validation through `verifyOptions.issuer` to avoid security issues through misconfiguration.
If it's intentional to not validate the issuer of incoming tokens, set this property to `true`. |
| `explicitNoAudienceValidation` | Optional. The default behaviour is to enforce audience validation through `verifyOptions.audience` to avoid security issues through misconfiguration.
If it's intentional to not validate the audience of incoming tokens, set this property to `true`. |

### Multitenant apps

To specify per-host config, provide a \*`BearerConfigCallback` in the form of `(host: string) => BearerConfig`.

Note: the callback will only be called once per host (config is cached).

### Apps accepting bearer tokens from multiple issuers

If your app needs to accept bearer tokens from multiple issuers (OIDC endpoints) **each with different JWKS URIs** on a single endpoint (not varied by host), `multiIssuerBearerTokenMiddleware` supports this with a different approach. It will:

- decode the token without verifying it
- use the `iss` claim to access the issuer-specific config `IssuerOptions` (or return unauthorized, if not found)
- verify the token using the issuer-specific config (caching JwksClient instances per JWKS URI)

#### Multi-issuer options

```ts
import { IssuerOptions, multiIssuerBearerTokenMiddleware, MultiIssuerBearerAuthOptions } from '@makerxstudio/express-bearer'

const app = express()
const issuerOptions: Record = {
'https://example.com/oidc': {
jwksUri: 'https://example.com/oidc/jwks',
verifyOptions: {
audience: 'https://api.example.com',
},
},
'https://login.microsoftonline.com//v2.0': {
jwksUri: 'https://login.microsoftonline.com//discovery/v2.0/keys',
verifyOptions: {
audience: '',
},
},
}

// add the multi issuer bearer token middleware (to all routes)
app.use(multiIssuerBearerTokenMiddleware({ issuerOptions, tokenIsRequired: true }))
```

## Logging

Set the logger implementation to an object that fulfills the `Logger` definition:

```ts
type Logger = {
error(message: string, ...optionalParams: unknown[]): void
warn(message: string, ...optionalParams: unknown[]): void
info(message: string, ...optionalParams: unknown[]): void
verbose(message: string, ...optionalParams: unknown[]): void
debug(message: string, ...optionalParams: unknown[]): void
}
```

Note: this type is compatible with [winston loggers](https://github.com/winstonjs/winston).

The following example uses console logging:

```ts
const logger: Logger = {
error: (message: string, ...params: unknown[]) => console.error(message, ...params),
warn: (message: string, ...params: unknown[]) => console.warn(message, ...params),
info: (message: string, ...params: unknown[]) => console.info(message, ...params),
verbose: (message: string, ...params: unknown[]) => console.trace(message, ...params),
debug: (message: string, ...params: unknown[]) => console.debug(message, ...params),
}

const config: BearerConfig = {
jwksUri: ...,
verifyOptions: { ... },
logger,
}
```