New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

express-openid-connect

Package Overview
Dependencies
Maintainers
36
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

express-openid-connect - npm Package Compare versions

Comparing version 0.4.0 to 0.5.0

index.d.ts

189

API.md

@@ -1,146 +0,117 @@

# API
# Public API
Please see the [Getting Started section of the README](https://github.com/auth0/express-openid-connect#getting-started) for how to apply configuration options.
## Configuration Keys
## openidClient.auth parameters
Please see the [Getting Started section of the README](https://github.com/auth0/express-openid-connect#getting-started) for examples of how to apply the configuration options to the `auth()` middleware.
In general, you won't need to configure this middleware besides the required parameters that can be specified through environment variables.
### Required Keys
| Name | Default | Description |
|---------------------|---------------------------------|--------------------------------------------------------------------------------|
| issuerBaseURL | `env.ISSUER_BASE_URL` | The url address for the token issuer. |
| baseURL | `env.BASE_URL` | The url of the web application where you are installing the router. |
| clientID | `env.CLIENT_ID` | The client id. |
| clientSecret | `env.CLIENT_SECRET` | The client secret, only required for some grants. |
| clockTolerance | `5` | The clock's tolerance in seconds for token verification. |
| getUser | `tokenSet => tokenSet.claims()` | An async function receiving a tokenset and returning the profile for `req.openid.user`. |
| required | `true` | If true requires authentication for all the routes in the stack. You can also provide a function to determine if is required based on the request. |
| handleUnauthorizedErrors | `true` | Install a middleware that handles Unauthorized/401 errors by triggering the login process. |
| routes | `true` | Installs the `GET /login` and `GET /logout` route. |
| idpLogout | `false` | Logout the user from the identity provider on logout |
| auth0Logout | `false` | Enable Auth0's non-compliant logout feature, only if Auth0 can be detected and the Auth0 instance does not support OpenID Connect session management. |
| authorizationParams | See bellow | The parameters for the authorization call. |
The `auth()` middleware has a few configuration keys that are required for initialization.
Default value for `authorizationParams` is:
- **`baseURL`** - The root URL for the application router. This can be set automatically with a `BASE_URL` variable in your environment.
- **`clientID`** - The Client ID for your application. This can be set automatically with a `CLIENT_ID` variable in your environment.
- **`issuerBaseURL`** - The root URL for the token issuer with no trailing slash. In Auth0, this is your Application's **Domain** prepended with `https://`. This can be set automatically with an `ISSUER_BASE_URL` variable in your environment.
```javascript
{
response_type: 'id_token',
response_mode: 'form_post',
scope: 'openid profile email'
}
```
If you are using a response type that includes `code` (typically combined with an `audience` parameter), you will need an additional key:
Commonly used `authorizationParams`:
- **`clientSecret`** - The Client ID for your application. This can be set automatically with a `CLIENT_SECRET` variable in your environment.
| Name | Default | Description |
|---------------------|------------------------|--------------------------------------------------------------------------------------------------------------|
| response_type | **Required** | The desired authorization processing flow, including what parameters are returned from the endpoints used. |
| response_mode | `undefined` / optional | The mechanism to be used for returning Authorization Response parameters from the Authorization Endpoint. |
| scope | `openid profile email` | The scope of the access token. |
| audience | `undefined` / optional | The audience for the access token. |
### Optional Keys
## openidClient.requiresAuth
Additional configuration keys that can be passed to `auth()` on initialization:
The `requiresAuth()` middleware protects specific application routes:
- **`auth0Logout`** - Boolean value to enable Auth0's logout feature. Default is `false`.
- **`authorizationParams`** - Object that describes the authorization server request. [See below](#authorization-params-key) for defaults and more details.
- **`clockTolerance`** - Integer value for the system clock's tolerance (leeway) in seconds for ID token verification. Default is `60`.
- **`getUser`** - Asynchronous function that receives a token set and returns the profile for `req.openid.user`. This runs on each application page load for authenticated users. Default is [here](lib/getUser.js).
- **`errorOnRequiredAuth`** - Boolean value to throw a `Unauthorized 401` error instead of triggering the login process for routes that require authentication. Default is `false`.
- **`httpOptions`** - Default options object used for all HTTP calls made by the library ([possible options](https://github.com/sindresorhus/got/tree/v9.6.0#options)). Default is empty.
- **`idpLogout`** - Boolean value to log the user out from the identity provider on application logout. Requires the issuer to provide a `end_session_endpoint` value. Default is `false`.
- **`loginPath`** - Relative path to application login. Default is `/login`.
- **`logoutPath`** - Relative path to application logout. Default is `/logout`.
- **`redirectUriPath`** - Relative path to the application callback to process the response from the authorization server. This value is combined with the `baseUrl` and sent to the authorize endpoint as the `redirectUri` parameter. Default is `/callback`.
- **`required`** - Use a boolean value to require authentication for all routes. Pass a function instead to base this value on the request. Default is `true`.
- **`routes`** - Boolean value to automatically install the login and logout routes. See [the examples](EXAMPLES.md) for more information on how this key is used. Default is `true`.
```javascript
const { auth, requiresAuth } = require('express-openid-connect');
app.use( auth( { required: false } ) );
app.use( '/admin', requiresAuth() );
```
### Authorization Params Key
If all endpoints require the user to be logged in, the default `auth` middleware protects you from this:
The `authorizationParams` key defines the URL parameters used when redirecting users to the authorization server to log in. If this key is not provided by your application, its default value will be:
```javascript
app.use( auth( { required: true } ) );
```js
{
response_type: "id_token",
response_mode: "form_post",
scope: "openid profile email"
}
```
## Session and Context
A new object can be passed in to change what is returned from the authorization server depending on your specific scenario.
The middleware stores the [openid-client TokenSet](https://github.com/panva/node-openid-client/blob/master/docs/README.md#tokenset) in the user's session.
For example, to receive an access token for an API, you could initialize like the sample below. Note that `response_mode` can be omitted because the OAuth2 default mode of `query` is fine:
Every `req` object is augmented with the following properties when the request is authenticated
```js
app.use(auth({
authorizationParams: {
response_type: "code",
scope: "openid profile email read:reports",
audience: "https://your-api-identifier"
}
}));
```
- `req.openid.user`: contains the user information, use this if you need display an attribute of the user. You can change what's end up here by using the `getUser` parameter of the `auth` middleware.
- `req.openid.tokens`: is the instance of [TokenSet](https://github.com/panva/node-openid-client/blob/master/docs/README.md#tokenset).
- `req.openid.client`: is an instance of te [OpenID Client](https://github.com/panva/node-openid-client/blob/master/docs/README.md#client).
- `req.isAuthenticated()`: returns true if the request is authenticated.
Additional custom parameters can be added as well:
If the request is not authenticated, `req.openid` is `undefined`.
```js
app.use(auth({
authorizationParams: {
// Note: you need to provide required parameters if this object is set.
response_type: "id_token",
response_mode: "form_post",
scope: "openid profile email"
Every `res` object gets the following methods:
// Additional parameters
acr_value: "tenant:test-tenant",
custom_param: "custom-value"
}
}));
```
- `res.openid.login(params)`: trigger an authentication request from any route. It receives the following parameters:
- `params.returnTo`: The url to return to after authentication. Defaults to the current url for GETs and `baseURL` for other methods.
- `params.authorizationParams`: additional parameters for the authorization call.
- `res.openid.logout(params)`: trigger the openid connect logout if supporter by the issuer.
- `params.returnTo`: The url to return to after sign out. Defaults to the `baseURL` for other methods.
## `requiresAuth()`
## Authorization handling
The `requiresAuth()` function is an optional middleware that protects specific application routes when the `required` configuration key is set to `false`:
By default the library triggers the login process when authentication is required.
An anonymous request to the home page in this case will trigger the login process:
```js
app.use(auth()); // Remember that required is true by default
app.get('/', (req, res) => res.render('home'));
```javascript
const { auth, requiresAuth } = require('express-openid-connect');
app.use( auth( { required: false } ) );
app.use( '/admin', requiresAuth(), (req, res) => res.render('admin') );
```
The same happens in this case:
Using `requiresAuth()` on its own without initializing `auth()` will throw a `401 Unauthorized` error instead of triggering the login process:
```js
app.use(auth()); // Remember that required is true by default
// app.use(auth({required: true}));
app.get('/', requiresAuth(), (req, res) => res.render('home'));
```
If you remove the `auth()` middleware above like this:
## Session and Context
```js
// app.use(auth()); // Remember that required is true by default
app.get('/', requiresAuth(), (req, res) => res.render('home'));
```
This library adds properties and methods to the request and response objects used within route handling.
Instead of triggering the login process we get a 401 Unauthorized error.
### Request
It is a best practice to decouple your application logic from this library. If you need to raise a 401 error on your own logic and `requiresAuth` is not enough, you can add the `unauthorizedHandler` from this library:
Every request object (typically named `req` in your route handler) is augmented with the following when the request is authenticated. If the request is not authenticated, `req.openid` is `undefined`.
```js
const {
auth,
requiresAuth,
unauthorizedHandler
} = require('express-openid-connect');
- **`req.openid.user`** - Contains the user information returned from the authorization server. You can change what is provided here by using the `getUser` configuration key.
- **`req.openid.tokens`** - Is the [TokenSet](https://github.com/panva/node-openid-client/blob/master/docs/README.md#tokenset) instance obtained during login.
- **`req.openid.client`** - Is the [OpenID Client](https://github.com/panva/node-openid-client/blob/master/docs/README.md#client) instance that can be used for additional OAuth2 and OpenID calls. See [the examples](EXAMPLES.md) for more information on how this is used.
- **`req.isAuthenticated()`** - Returns true if the request is authenticated.
app.use(auth());
### Response
// your routes go here
app.get('/a-route', (req, res, next) => {
if (condition) {
return next(new UnauthorizedError('unauthorized because of xyz'));
}
});
Every response object (typically named `res` in your route handler) is augmented with the following:
//trigger login transactions on 401 errors.
app.use(unauthorizedHandler());
```
If you need an special logic for handling 401s, including the errors raised by this library, you can set `errorOnRequiredAuth` to `true` like this:
```js
const { auth, requiresAuth } = require('express-openid-connect');
app.use(auth({ errorOnRequiredAuth: true }));
// your routes go here
//handle unauthorized errors with the unauthorizedHandler or
//with your own middleware like this:
app.use((err, req, res, next) => {
if (err.statusCode === 401) {
return res.openid.login(); //trigger the login process like the `unauthorizedHandler`.
}
next(err);
});
```
- **`res.openid.login({})`** - trigger an authentication request from any route. It receives an object with the following keys:
- `returnTo`: The URL to return to after authentication. Defaults to the current URL for `GET` routes and `baseURL` for other methods.
- `authorizationParams`: Additional parameters for the authorization call.
- **`res.openid.logout({})`** - trigger the openid connect logout if supporter by the issuer. It receives an object with the following key:
- `returnTo`: The URL to return to after signing out at the authorization server. Defaults to the `baseURL`.
# CHANGELOG
## [v0.5.0](https://github.com/auth0/express-openid-connect/tree/v0.5.0) (2019-10-17)
[Full Changelog](https://github.com/auth0/express-openid-connect/compare/v0.4.0...v0.5.0)
**Closed issues**
- Removal of automatic refresh [\#11](https://github.com/auth0/express-openid-connect/issues/11)
**Added**
- Add configurable HTTP options [\#29](https://github.com/auth0/express-openid-connect/pull/29) ([joshcanhelp](https://github.com/joshcanhelp))
- add typescript types [\#27](https://github.com/auth0/express-openid-connect/pull/27) ([jbarrus](https://github.com/jbarrus))
- Add telemetry to HTTP requests [\#23](https://github.com/auth0/express-openid-connect/pull/23) ([joshcanhelp](https://github.com/joshcanhelp))
- feat: allow custom login and logout paths [\#14](https://github.com/auth0/express-openid-connect/pull/14) ([joshcanhelp](https://github.com/joshcanhelp))
**Changed**
- Update default leeway and re-write API documentation [\#30](https://github.com/auth0/express-openid-connect/pull/30) ([joshcanhelp](https://github.com/joshcanhelp))
## [v0.4.0](https://github.com/auth0/express-openid-connect/tree/v0.4.0) (2019-09-26)

@@ -4,0 +22,0 @@ [Full Changelog](https://github.com/auth0/express-openid-connect/compare/v0.3.0...v0.4.0)

### Example 1
# Examples
## 1. Basic Setup
The simplest use case for this middleware:

@@ -8,3 +10,2 @@

# .env
ISSUER_BASE_URL=https://YOUR_DOMAIN

@@ -18,8 +19,7 @@ CLIENT_ID=YOUR_CLIENT_ID

```javascript
// app.js
const { auth } = require('express-openid-connect');
const session = require('cookie-session');
app.use(express.urlencoded({
extended: false
}));
app.use(express.urlencoded({ extended: false }));

@@ -43,10 +43,44 @@ app.use(session({

- Every route after the `auth()` middleware requires authentication.
- If a user try to access a resource without being authenticated, the application will trigger the authentication process. After completion the user is redirected back to the resource.
- The application creates `GET /login` and `GET /logout` routes for easy linking.
- If a user tries to access a resource without being authenticated, the application will redirect the user to log in. After completion the user is redirected back to the resource.
- The application creates `/login` and `/logout` `GET` routes.
### Example 2
## 2. Require authentication for specific routes
If you need to customize the routes, you can opt-out from the default routes and handle this manually:
If your application has routes accessible to anonymous users, you can enable authorization per routes:
```js
const { auth, requiresAuth } = require('express-openid-connect');
app.use(auth({
required: false
}));
// Anyone can access the homepage
app.use('/', (req, res) => res.render('home'));
// Require routes under the /admin/ prefix to check authentication.
app.use('/admin/users', requiresAuth(), (req, res) => res.render('admin-users'));
app.use('/admin/posts', requiresAuth(), (req, res) => res.render('admin-posts'));
```
Another way to configure this scenario:
```js
const { auth } = require('express-openid-connect');
//initialization
app.use(auth({
required: req => req.originalUrl.startsWith('/admin/')
}));
app.use('/', (req, res) => res.render('home'));
app.use('/admin/users', (req, res) => res.render('admin-users'));
app.use('/admin/posts', (req, res) => res.render('admin-posts'));
```
## 3. Route Customization
If you need to customize the routes, you can opt-out from the default routes and write your own route handler:
```js
app.use(auth({ routes: false }));

@@ -58,26 +92,65 @@

Please note that both of these routes are completely optional and not required. Trying to access any protected resource triggers the authentication process if required.
... or you can define specific routes in configuration keys where the default handler will run:
### Example 3
```js
app.use(auth({
redirectUriPath: '/custom-callback-path',
loginPath: '/custom-login-path',
logoutPath: '/custom-logout-path',
}));
```
If your application has routes accessible to anonymous users, you can enable authorization per routes:
Please note that both of these routes are completely optional and not required. Trying to access any protected resource triggers a redirect directly to Auth0 to login.
```js
const { auth, requiresAuth } = require('express-openid-connect');
## 4. Using refresh tokens
app.use(auth({ required: false }));
Refresh tokens can be requested along with access tokens using the `offline_access` scope during login:
// Require every route under the /admin prefix to check authentication.
app.use('/admin', requiresAuth());;
```js
app.use(auth({
authorizationParams: {
response_type: 'code id_token',
response_mode: 'form_post',
audience: process.env.API_URL,
scope: 'openid profile email read:reports offline_access'
}
}));
```
Another way to configure this scenario:
On a route that calls an API, check for an expired token and attempt a refresh:
```js
const { auth } = require('express-openid-connect');
app.get('/route-that-calls-an-api', async (req, res, next) => {
//initialization
app.use(auth({
required: req => req.originalUrl.startsWith('/admin')
}));
let apiData = {};
let tokenSet = req.openid.tokens;
if (tokenSet.expired() && tokenSet.refresh_token) {
try {
tokenSet = await req.openid.client.refresh(tokenSet);
} catch(err) {
next(err);
}
tokenSet.refresh_token = req.openid.tokens.refresh_token;
req.openid.tokens = tokenSet;
}
try {
apiData = await request(
process.env.API_URL,
{
headers: { authorization: `Bearer ${tokenSet.access_token}` },
json: true
}
);
} catch(err) {
next(err);
}
res.render('api-data-template', {
user: req.openid && req.openid.user,
apiData
});
});
```

@@ -7,33 +7,45 @@ const { Issuer, custom } = require('openid-client');

custom.setHttpOptionsDefaults({
headers: {
'User-Agent': `${pkg.name}/${pkg.version} (${pkg.homepage})`
},
timeout: 4000
});
const telemetryHeader = {
name: 'express-oidc',
version: pkg.version,
env: {
node: process.version
}
};
async function get(config) {
const authorizeParams = config.authorizationParams;
const issuer = await Issuer.discover(config.issuerBaseURL);
if (Array.isArray(issuer.response_types_supported) &&
!issuer.response_types_supported.includes(authorizeParams.response_type)) {
throw new Error(`The issuer doesn't support the response_type ${authorizeParams.response_type}
Supported types:
- ${issuer.response_types_supported.sort().join('\n- ')}
`);
const issuerTokenAlgs = Array.isArray(issuer.id_token_signing_alg_values_supported) ?
issuer.id_token_signing_alg_values_supported : [];
if (!issuerTokenAlgs.includes(config.idTokenAlg)) {
throw new Error(
`ID token algorithm "${config.idTokenAlg}" is not supported by the issuer. ` +
`Supported ID token algorithms are: "${issuerTokenAlgs.join('", "')}". `
);
}
if (authorizeParams.response_mode && Array.isArray(issuer.response_modes_supported) &&
!issuer.response_modes_supported.includes(authorizeParams.response_mode)) {
throw new Error(`The issuer doesn't support the response_mode ${authorizeParams.response_mode}
Supported response modes:
- ${issuer.response_modes_supported.sort().join('\n- ')}
`);
const configRespType = config.authorizationParams.response_type;
const issuerRespTypes = Array.isArray(issuer.response_types_supported) ? issuer.response_types_supported : [];
if (!issuerRespTypes.includes(configRespType)) {
throw new Error(
`Response type "${configRespType}" is not supported by the issuer. ` +
`Supported response types are: "${issuerRespTypes.join('", "')}". `
);
}
const configRespMode = config.authorizationParams.response_mode;
const issuerRespModes = Array.isArray(issuer.response_modes_supported) ? issuer.response_modes_supported : [];
if (configRespMode && ! issuerRespModes.includes(configRespMode)) {
throw new Error(
`Response mode "${configRespMode}" is not supported by the issuer. ` +
`Supported response modes are "${issuerRespModes.join('", "')}". `
);
}
const client = new issuer.Client({
client_id: config.clientID,
client_secret: config.clientSecret
client_secret: config.clientSecret,
id_token_signed_response_alg: config.idTokenAlg,
});

@@ -56,2 +68,13 @@

let httpOptions = config.httpOptions || {};
httpOptions.headers = Object.assign(
// Allow configuration to override user agent header.
{'User-Agent': `${pkg.name}/${pkg.version}`},
httpOptions.headers || {},
// Do not allow overriding telemetry.
{'Auth0-Client': Buffer.from(JSON.stringify(telemetryHeader)).toString('base64')}
);
custom.setHttpOptionsDefaults(httpOptions);
client[custom.clock_tolerance] = config.clockTolerance;

@@ -58,0 +81,0 @@

@@ -20,2 +20,3 @@ const Joi = require('@hapi/joi');

const paramsSchema = Joi.object().keys({
httpOptions: Joi.object().optional(),
issuerBaseURL: Joi.alternatives([ Joi.string().uri(), Joi.string().hostname() ]).required(),

@@ -25,4 +26,5 @@ baseURL: Joi.string().uri().required(),

clientSecret: Joi.string().optional(),
idTokenAlg: Joi.string().not('none').optional().default('RS256'),
authorizationParams: Joi.object().optional(),
clockTolerance: Joi.number().optional().default(5),
clockTolerance: Joi.number().optional().default(60),
getUser: Joi.func().optional().default(getUser),

@@ -34,2 +36,4 @@ required: Joi.alternatives([ Joi.func(), Joi.boolean()]).optional().default(true),

redirectUriPath: Joi.string().optional().default('/callback'),
loginPath: Joi.string().optional().default('/login'),
logoutPath: Joi.string().optional().default('/logout'),
idpLogout: Joi.boolean().optional().default(false)

@@ -81,7 +85,11 @@ .when('auth0Logout', { is: true, then: Joi.boolean().optional().default(true) })

const missingClientSecret = !config.clientSecret &&
config.authorizationParams.response_type.split(' ').includes('code');
// Code grant requires a client secret to exchange the code for tokens
const responseTypeHasCode = config.authorizationParams.response_type.split(' ').includes('code');
if (responseTypeHasCode && !config.clientSecret) {
throw new Error('"clientSecret" is required for response_type code');
}
if(missingClientSecret) {
throw new Error('"clientSecret" is required for response_type code and response_mode query');
// HS256 ID tokens require a client secret to validate the signature.
if ('HS' === config.idTokenAlg.substring(0,2) && !config.clientSecret) {
throw new Error('"clientSecret" is required for ID tokens with HS algorithms');
}

@@ -88,0 +96,0 @@

@@ -25,8 +25,2 @@ /**

/**
* Authorization Response parameters are encoded in
* the fragment added to the redirect_uri when redirecting back to the Client.
*/
Fragment: 'fragment',
/**
* Authorization Response parameters are encoded as HTML form values

@@ -33,0 +27,0 @@ * that are auto-submitted in the User Agent, and thus are transmitted via the HTTP POST method

@@ -5,4 +5,2 @@ const express = require('express');

const { get: getConfig } = require('../lib/config');
const memoize = require('p-memoize');
const fs = require('fs');
const { get: getClient } = require('../lib/client');

@@ -12,4 +10,2 @@ const requiresAuth = require('./requiresAuth');

const getRepostView = memoize(() => fs.readFileSync(__dirname + '/../views/repost.html'));
/**

@@ -23,3 +19,3 @@ * Returns a router with two routes /login and /callback

* @param {string} [params.clientSecret] The client secret, only required for some grants.
* @param {string} [params.clockTolerance=5] The clock's tolerance in seconds for token verification.
* @param {string} [params.clockTolerance] The clock's tolerance in seconds for token verification.
* @param {Function} [params.getUser] An async function receiving a tokenset and returning the profile for req.user.

@@ -70,10 +66,9 @@ * @param {boolean|Function} [params.required=true] a boolean to indicate that every route after this middleware requires authentication or

if (config.routes) {
router.get('/login', (req, res) => {
router.get(config.loginPath, (req, res) => {
res.openid.login({ returnTo: config.baseURL });
});
router.get('/logout', (req, res) => res.openid.logout());
router.get(config.logoutPath, (req, res) => res.openid.logout());
}
let callbackMethod;
let repost;

@@ -87,14 +82,4 @@ switch (authorizeParams.response_mode) {

break;
case 'fragment':
callbackMethod = 'post';
repost = true;
break;
default:
if (/token/.test(authorizeParams.response_type)) {
callbackMethod = 'post';
repost = true;
}
else {
callbackMethod = 'get';
}
callbackMethod = 'get';
}

@@ -136,9 +121,2 @@

if (repost) {
router.get('/callback', async (req, res) => {
res.set('Content-Type', 'text/html');
res.send(getRepostView());
});
}
if (config.required) {

@@ -145,0 +123,0 @@ const requiresAuthMiddleware = requiresAuth();

{
"name": "express-openid-connect",
"version": "0.4.0",
"version": "0.5.0",
"description": "An Express.js middleware to protect OpenID Connect web applications.",
"homepage": "https://github.com/auth0/express-openid-connect",
"license": "MIT",
"author": "José F. Romaniello <jfromaniello@gmail.com> (https://joseoncode.com)",
"author": "Auth0 <support@auth0.com>",
"main": "index.js",

@@ -18,3 +18,3 @@ "scripts": {

"http-errors": "^1.7.3",
"openid-client": "^3.7.2",
"openid-client": "^3.7.3",
"p-memoize": "^3.1.0",

@@ -24,2 +24,3 @@ "url-join": "^4.0.1"

"devDependencies": {
"@types/express": "^4.17.1",
"body-parser": "^1.19.0",

@@ -32,8 +33,8 @@ "chai": "^4.2.0",

"jsonwebtoken": "^8.5.1",
"mocha": "^6.2.0",
"nock": "^11.3.5",
"mocha": "^6.2.1",
"nock": "^11.4.0",
"pem-jwk": "^2.0.0",
"proxyquire": "^2.1.3",
"request-promise-native": "^1.0.7",
"selfsigned": "^1.10.6",
"selfsigned": "^1.10.7",
"sinon": "^7.5.0"

@@ -40,0 +41,0 @@ },

@@ -14,7 +14,4 @@ # Express OpenID Connect

[![Build Status](https://travis-ci.org/auth0/express-openid-connect.svg?branch=master)](https://travis-ci.org/auth0/express-openid-connect)
[![NPM version](https://img.shields.io/npm/v/express-openid-connect.svg?style=flat-square)](https://npmjs.org/package/express-openid-connect)
[![Downloads](https://img.shields.io/npm/dm/express-openid-connect.svg.svg?style=flat-square)](https://npmjs.org/package/express-openid-connect.svg)
## Table of Contents

@@ -72,5 +69,6 @@

app.use(auth({
required: true,
issuerBaseURL: 'https://YOUR_DOMAIN',
baseURL: 'YOUR_CLIENT_ID',
clientID: 'https://YOUR_APPLICATION_ROOT_URL'
baseURL: 'https://YOUR_APPLICATION_ROOT_URL',
clientID: 'YOUR_CLIENT_ID'
}));

@@ -77,0 +75,0 @@ ```

const assert = require('chai').assert;
const url = require('url');
const fs = require('fs');
const request = require('request-promise-native').defaults({

@@ -24,5 +23,5 @@ simple: false,

router = expressOpenid.auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
required: false

@@ -51,5 +50,5 @@ });

const parsed = url.parse(res.headers.location, true);
assert.equal(parsed.hostname, 'flosser.auth0.com');
assert.equal(parsed.hostname, 'test.auth0.com');
assert.equal(parsed.pathname, '/authorize');
assert.equal(parsed.query.client_id, '123');
assert.equal(parsed.query.client_id, '__test_client_id__');

@@ -59,3 +58,3 @@ assert.equal(parsed.query.scope, 'openid profile email');

assert.equal(parsed.query.response_mode, 'form_post');
assert.equal(parsed.query.redirect_uri, 'https://myapp.com/callback');
assert.equal(parsed.query.redirect_uri, 'https://example.org/callback');
assert.property(parsed.query, 'nonce');

@@ -77,5 +76,5 @@ assert.property(parsed.query, 'state');

router = expressOpenid.auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
authorizationParams: {

@@ -97,9 +96,9 @@ response_mode: undefined,

assert.equal(parsed.hostname, 'flosser.auth0.com');
assert.equal(parsed.hostname, 'test.auth0.com');
assert.equal(parsed.pathname, '/authorize');
assert.equal(parsed.query.client_id, '123');
assert.equal(parsed.query.client_id, '__test_client_id__');
assert.equal(parsed.query.scope, 'openid profile email');
assert.equal(parsed.query.response_type, 'none');
assert.equal(parsed.query.response_mode, undefined);
assert.equal(parsed.query.redirect_uri, 'https://myapp.com/callback');
assert.equal(parsed.query.redirect_uri, 'https://example.org/callback');
assert.property(parsed.query, 'nonce');

@@ -120,6 +119,6 @@ assert.property(parsed.query, 'state');

router = router = expressOpenid.auth({
clientID: '123',
clientSecret: '456',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
clientSecret: '__test_client_secret__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
authorizationParams: {

@@ -141,9 +140,9 @@ response_mode: undefined,

assert.equal(parsed.hostname, 'flosser.auth0.com');
assert.equal(parsed.hostname, 'test.auth0.com');
assert.equal(parsed.pathname, '/authorize');
assert.equal(parsed.query.client_id, '123');
assert.equal(parsed.query.client_id, '__test_client_id__');
assert.equal(parsed.query.scope, 'openid profile email');
assert.equal(parsed.query.response_type, 'code');
assert.equal(parsed.query.response_mode, undefined);
assert.equal(parsed.query.redirect_uri, 'https://myapp.com/callback');
assert.equal(parsed.query.redirect_uri, 'https://example.org/callback');
assert.property(parsed.query, 'nonce');

@@ -164,5 +163,5 @@ assert.property(parsed.query, 'state');

router = router = expressOpenid.auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
authorizationParams: {

@@ -183,9 +182,9 @@ response_mode: undefined,

assert.equal(parsed.hostname, 'flosser.auth0.com');
assert.equal(parsed.hostname, 'test.auth0.com');
assert.equal(parsed.pathname, '/authorize');
assert.equal(parsed.query.client_id, '123');
assert.equal(parsed.query.client_id, '__test_client_id__');
assert.equal(parsed.query.scope, 'openid profile email');
assert.equal(parsed.query.response_type, 'id_token');
assert.equal(parsed.query.response_mode, undefined);
assert.equal(parsed.query.redirect_uri, 'https://myapp.com/callback');
assert.equal(parsed.query.redirect_uri, 'https://example.org/callback');
assert.property(parsed.query, 'nonce');

@@ -196,17 +195,48 @@ assert.property(parsed.query, 'state');

it('should contain the two callbacks route', function() {
assert.ok(router.stack.some(filterRoute('POST', '/callback')));
assert.ok(router.stack.some(filterRoute('GET', '/callback')));
});
it('should return an html on GET /callback', async function() {
const cookieJar = request.jar();
const res = await request.get('/callback', { cookieJar, baseUrl, followRedirect: false });
assert.equal(res.statusCode, 200);
assert.equal(res.headers['content-type'], 'text/html; charset=utf-8');
const expectedBody = fs.readFileSync(`${__dirname}/../views/repost.html`, 'utf-8');
assert.equal(res.body, expectedBody);
});
});
describe('custom path values', () => {
let baseUrl, router;
before(async function() {
router = expressOpenid.auth({
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
redirectUriPath: '/custom-callback',
loginPath: '/custom-login',
logoutPath: '/custom-logout',
});
baseUrl = await server.create(router);
});
it('should contain the custom login route', function() {
assert.ok(router.stack.some(filterRoute('GET', '/custom-login')));
});
it('should contain the custom logout route', function() {
assert.ok(router.stack.some(filterRoute('GET', '/custom-logout')));
});
it('should contain the custom callback route', function() {
assert.ok(router.stack.some(filterRoute('POST', '/custom-callback')));
});
it('should redirect to the authorize url properly on /login', async function() {
const jar = request.jar();
const res = await request.get('/custom-login', { jar, baseUrl, followRedirect: false });
assert.equal(res.statusCode, 302);
const parsed = url.parse(res.headers.location, true);
assert.equal(parsed.hostname, 'test.auth0.com');
assert.equal(parsed.pathname, '/authorize');
assert.equal(parsed.query.redirect_uri, 'https://example.org/custom-callback');
});
});
});

@@ -11,3 +11,3 @@ const assert = require('chai').assert;

const cert = require('./fixture/cert');
const clientID = 'foobar';
const clientID = '__test_client_id__';

@@ -18,4 +18,4 @@ function testCase(params) {

clientID: clientID,
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
required: false

@@ -66,4 +66,4 @@ });

session: {
nonce: '123',
state: '123'
nonce: '__test_nonce__',
state: '__test_state__'
},

@@ -84,9 +84,7 @@ body: true,

session: {
nonce: '123',
state: '123'
nonce: '__test_nonce__',
state: '__valid_state__'
},
body: {
nonce: '123',
state: '456',
id_token: 'sioua'
state: '__invalid_state__'
},

@@ -106,9 +104,8 @@ assertions() {

session: {
nonce: '123',
state: '123'
nonce: '__test_nonce__',
state: '__test_state__'
},
body: {
nonce: '123',
state: '123',
id_token: 'sioua'
state: '__test_state__',
id_token: '__invalid_token__'
},

@@ -128,9 +125,8 @@ assertions() {

session: {
nonce: '123',
state: '123'
nonce: '__test_nonce__',
state: '__test_state__'
},
body: {
nonce: '123',
state: '123',
id_token: jwt.sign({ foo: '123'}, 'f00')
state: '__test_state__',
id_token: jwt.sign({sub: '__test_sub__'}, '__invalid_alg__')
},

@@ -150,9 +146,8 @@ assertions() {

session: {
nonce: '123',
state: '123'
nonce: '__test_nonce__',
state: '__test_state__'
},
body: {
nonce: '123',
state: '123',
id_token: jwt.sign({ foo: '123'}, cert.key, { algorithm: 'RS256' })
state: '__test_state__',
id_token: jwt.sign({sub: '__test_sub__'}, cert.key, { algorithm: 'RS256' })
},

@@ -172,20 +167,19 @@ assertions() {

session: {
state: '123',
nonce: 'abcdefg',
returnTo: '/foobar'
state: '__test_state__',
nonce: '__test_nonce__',
returnTo: '/return-to'
},
body: {
nonce: '123',
state: '123',
state: '__test_state__',
id_token: jwt.sign({
'nickname': 'jjjj',
'name': 'Jeranio',
'email': 'jjjj@example.com',
'nickname': '__test_nickname__',
'name': '__test_name__',
'email': '__test_email__',
'email_verified': true,
'iss': 'https://flosser.auth0.com/',
'sub': 'xasdas',
'iss': 'https://test.auth0.com/',
'sub': '__test_sub__',
'aud': clientID,
'iat': Math.round(Date.now() / 1000),
'exp': Math.round(Date.now() / 1000) + 60000,
'nonce': 'abcdefg'
'nonce': '__test_nonce__'
}, cert.key, { algorithm: 'RS256', header: { kid: cert.kid } })

@@ -199,3 +193,3 @@ },

it('should redirect to the intended url', function() {
assert.equal(this.response.headers['location'], '/foobar');
assert.equal(this.response.headers['location'], '/return-to');
});

@@ -213,3 +207,3 @@

});
assert.equal(res.body.nickname, 'jjjj');
assert.equal(res.body.nickname, '__test_nickname__');
});

@@ -216,0 +210,0 @@ }

@@ -7,5 +7,5 @@ const { assert } = require('chai');

const config = getConfig({
clientID: '123',
issuerBaseURL: 'https://flosser.auth0.com',
baseURL: 'https://jjj.com',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
});

@@ -32,6 +32,6 @@

const config = getConfig({
clientID: '123',
clientSecret: '123',
issuerBaseURL: 'https://flosser.auth0.com',
baseURL: 'https://jjj.com',
clientID: '__test_client_id__',
clientSecret: '__test_client_secret__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
authorizationParams: {

@@ -57,5 +57,5 @@ response_type: 'code'

const config = getConfig({
clientID: '123',
issuerBaseURL: 'https://flosser.auth0.com',
baseURL: 'https://jjj.com',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
auth0Logout: true

@@ -72,5 +72,5 @@ });

const config = getConfig({
clientID: '123',
issuerBaseURL: 'https://flosser.auth0.com',
baseURL: 'https://jjj.com',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
});

@@ -86,5 +86,5 @@

const config = getConfig({
clientID: '123',
issuerBaseURL: 'https://flosser.auth0.com',
baseURL: 'https://jjj.com',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
idpLogout: true

@@ -99,2 +99,45 @@ });

describe('default auth paths', function() {
const config = getConfig({
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
});
it('should set the default callback path', function() {
assert.equal(config.redirectUriPath, '/callback');
});
it('should set the default login path', function() {
assert.equal(config.loginPath, '/login');
});
it('should set the default logout path', function() {
assert.equal(config.logoutPath, '/logout');
});
});
describe('custom auth paths', function() {
const config = getConfig({
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
redirectUriPath: '/custom-callback',
loginPath: '/custom-login',
logoutPath: '/custom-logout',
});
it('should accept the custom callback path', function() {
assert.equal(config.redirectUriPath, '/custom-callback');
});
it('should accept the login path', function() {
assert.equal(config.loginPath, '/custom-login');
});
it('should accept the logout path', function() {
assert.equal(config.logoutPath, '/custom-logout');
});
});
});

@@ -23,5 +23,5 @@ const assert = require('chai').assert;

router = expressOpenid.auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
required: false,

@@ -42,3 +42,3 @@ redirectUriPath: '/auth-finish'

const parsed = url.parse(res.headers.location, true);
assert.equal(parsed.query.redirect_uri, 'https://myapp.com/auth-finish');
assert.equal(parsed.query.redirect_uri, 'https://example.org/auth-finish');
});

@@ -45,0 +45,0 @@

@@ -7,3 +7,3 @@ const selfsigned = require('selfsigned');

name: 'commonName',
value: 'flosser.auth0.com'
value: 'test.auth0.com'
}

@@ -10,0 +10,0 @@ ];

@@ -10,4 +10,4 @@ const express = require('express');

app.use(cookieSession({
name: 'tests',
secret: 'blabla',
name: '__test_name__',
secret: '__test_secret__',
}));

@@ -14,0 +14,0 @@

{
"issuer": "https://flosser.auth0.com/",
"authorization_endpoint": "https://flosser.auth0.com/authorize",
"token_endpoint": "https://flosser.auth0.com/oauth/token",
"userinfo_endpoint": "https://flosser.auth0.com/userinfo",
"mfa_challenge_endpoint": "https://flosser.auth0.com/mfa/challenge",
"jwks_uri": "https://flosser.auth0.com/.well-known/jwks.json",
"registration_endpoint": "https://flosser.auth0.com/oidc/register",
"revocation_endpoint": "https://flosser.auth0.com/oauth/revoke",
"issuer": "https://test.auth0.com/",
"authorization_endpoint": "https://test.auth0.com/authorize",
"token_endpoint": "https://test.auth0.com/oauth/token",
"userinfo_endpoint": "https://test.auth0.com/userinfo",
"mfa_challenge_endpoint": "https://test.auth0.com/mfa/challenge",
"jwks_uri": "https://test.auth0.com/.well-known/jwks.json",
"registration_endpoint": "https://test.auth0.com/oidc/register",
"revocation_endpoint": "https://test.auth0.com/oauth/revoke",
"introspection_endpoint": "https://test.auth0.com/introspection",
"scopes_supported": ["openid", "profile", "offline_access", "name", "given_name", "family_name", "nickname", "email", "email_verified", "picture", "created_at", "identities", "phone", "address"],

@@ -11,0 +12,0 @@ "response_types_supported": ["code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token", "none"],

@@ -8,5 +8,5 @@ const { assert } = require('chai');

expressOpenid.auth({
baseURL: 'http://localhost',
issuerBaseURL: '123 r423.json xxx',
clientID: '123ewasda'
baseURL: 'https://example.org',
issuerBaseURL: '__invalid_url__',
clientID: '__test_client_id__'
});

@@ -19,5 +19,5 @@ }, '"issuerBaseURL" must be a valid uri');

expressOpenid.auth({
baseURL: 'xasxasa sads',
issuerBaseURL: 'http://foobar.com',
clientID: '123ewasda'
baseURL: '__invalid_url__',
issuerBaseURL: 'https://test.auth0.com',
clientID: '__test_client_id__'
});

@@ -30,4 +30,4 @@ }, '"baseURL" must be a valid uri');

expressOpenid.auth({
baseURL: 'http://foobar.com',
issuerBaseURL: 'http://foobar.com',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
});

@@ -40,4 +40,4 @@ }, '"clientID" is required');

expressOpenid.auth({
issuerBaseURL: 'http://foobar.com',
clientID: 'asdas',
issuerBaseURL: 'https://test.auth0.com',
clientID: '__test_client_id__',
});

@@ -50,5 +50,5 @@ }, '"baseURL" is required');

expressOpenid.auth({
issuerBaseURL: 'http://foobar.auth0.com',
baseURL: 'http://foobar.com',
clientID: 'asdas',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
clientID: '__test_client_id__',
authorizationParams: {

@@ -60,2 +60,13 @@ response_type: 'code id_token'

});
it('should fail when client secret is not provided and using an HS256 ID token algorithm', function() {
assert.throws(() => {
expressOpenid.auth({
issuerBaseURL: 'http://foobar.auth0.com',
baseURL: 'http://foobar.com',
clientID: 'asdas',
idTokenAlg: 'HS256'
});
}, '"clientSecret" is required for ID tokens with HS algorithms');
});
});

@@ -12,7 +12,7 @@ const assert = require('chai').assert;

const router = expressOpenid.auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
authorizationParams: {
response_mode: 'ffff',
response_mode: '__invalid_response_mode__',
response_type: 'id_token'

@@ -30,4 +30,4 @@ }

assert.equal(res.statusCode, 500);
assert.include(res.body.err.message, 'The issuer doesn\'t support the response_mode ffff');
assert.include(res.body.err.message, 'Response mode "__invalid_response_mode__" is not supported by the issuer.');
});
});

@@ -12,7 +12,7 @@ const assert = require('chai').assert;

const router = expressOpenid.auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
authorizationParams: {
response_type: 'bufanda'
response_type: '__invalid_response_type__'
}

@@ -29,4 +29,4 @@ });

assert.equal(res.statusCode, 500);
assert.include(res.body.err.message, 'The issuer doesn\'t support the response_type bufanda');
assert.include(res.body.err.message, 'Response type "__invalid_response_type__" is not supported by the issuer.');
});
});

@@ -21,5 +21,5 @@ const { assert } = require('chai');

idpLogout: false,
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
required: false

@@ -47,3 +47,3 @@ });

assert.equal(logoutResponse.statusCode, 302);
assert.equal(logoutResponse.headers.location, 'https://myapp.com');
assert.equal(logoutResponse.headers.location, 'https://example.org');
});

@@ -61,5 +61,5 @@ });

idpLogout: true,
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
required: false

@@ -90,4 +90,4 @@ });

protocol: 'https:',
hostname: 'flosser.auth0.com',
query: { returnTo: 'https://myapp.com', client_id: '123' },
hostname: 'test.auth0.com',
query: { returnTo: 'https://example.org', client_id: '__test_client_id__' },
pathname: '/v2/logout',

@@ -94,0 +94,0 @@ });

@@ -17,5 +17,5 @@ const { assert } = require('chai');

const router = auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
required: false

@@ -31,3 +31,3 @@ });

it('should contain a location header to the issuer', function() {
assert.include(response.headers.location, 'https://flosser.auth0.com');
assert.include(response.headers.location, 'https://test.auth0.com');
});

@@ -57,5 +57,5 @@ });

const router = auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
required: false,

@@ -62,0 +62,0 @@ errorOnRequiredAuth: true

@@ -6,3 +6,3 @@ const nock = require('nock');

before(function() {
nock('https://flosser.auth0.com', { allowUnmocked: true })
nock('https://test.auth0.com', { allowUnmocked: true })
.persist()

@@ -12,3 +12,3 @@ .get('/.well-known/openid-configuration')

nock('https://flosser.auth0.com', { allowUnmocked: true })
nock('https://test.auth0.com', { allowUnmocked: true })
.persist()

@@ -15,0 +15,0 @@ .get('/.well-known/jwks.json')

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc