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
34
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.5.0 to 0.6.0

.github/stale.yml

20

API.md

@@ -11,2 +11,3 @@ # Public API

- **`appSessionSecret`** - The secret used to derive an encryption key for the user identity in a session cookie. It must be a string, an array of strings, or `false` to skip this internal storage and provide your own session mechanism in `getUser`. When array is provided the first member is used for signing and other members can be used for decrypting old cookies, this is to enable appSessionSecret rotation. This can be set automatically with an `APP_SESSION_SECRET` variable in your environment.
- **`baseURL`** - The root URL for the application router. This can be set automatically with a `BASE_URL` variable in your environment.

@@ -18,3 +19,3 @@ - **`clientID`** - The Client ID for your application. This can be set automatically with a `CLIENT_ID` variable in your environment.

- **`clientSecret`** - The Client ID for your application. This can be set automatically with a `CLIENT_SECRET` variable in your environment.
- **`clientSecret`** - The Client Secret for your application. This can be set automatically with a `CLIENT_SECRET` variable in your environment.

@@ -25,9 +26,16 @@ ### Optional Keys

- **`appSessionCookie`** - Object defining application session cookie attributes. Allowed keys are `domain`, `httpOnly`, `path`, `secure`, and `sameSite`. Defaults are `true` for `httpOnly` and `Lax` for `sameSite`.
- **`appSessionDuration`** - Integer value, in seconds, for application session duration. Set to `0` to indicate the cookie should be ephemeral (no expiration). Default is 7 days.
- **`appSessionName`** - String value for the cookie name used for the internal session. This value must only include letters, numbers, and underscores. Default is `identity`.
- **`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`.
- **`getUser`** - Function that returns the profile for `req.openid.user`. This runs on each application page load for authenticated users. Default is [here](lib/hooks/getUser.js).
- **`handleCallback`** - Function that runs on the callback route, after callback processing but before redirection. Default is [here](lib/hooks/handleCallback.js).
- **`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.
- **`identityClaimFilter`** - Array value of claims to remove from the ID token before storing the cookie session. Default is `['aud', 'iss', 'iat', 'exp', 'nonce', 'azp', 'auth_time']`.
- **`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`.
- **`idTokenAlg`** - String value for the expected ID token algorithm. Default is `RS256`.
- **`legacySameSiteCookie`** - Set a fallback cookie with no SameSite attribute when `authorizationParams.response_mode` is `form_post`. Default is `true`.
- **`loginPath`** - Relative path to application login. Default is `/login`.

@@ -105,8 +113,8 @@ - **`logoutPath`** - Relative path to application logout. Default is `/logout`.

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`.
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` and `req.isAuthenticated()` returns `false`.
- **`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.user`** - Contains the user information returned from the authorization server. You can change what is provided here by passing a function to the `getUser` configuration key.
- **`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.
- **`req.makeTokenSet()`** - Make a TokenSet object from a JSON representation of one.

@@ -120,3 +128,3 @@ ### Response

- `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:
- **`res.openid.logout({})`** - trigger the openid connect logout if supported 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.6.0](https://github.com/auth0/express-openid-connect/tree/v0.6.0) (2020-01-14)
[Full Changelog](https://github.com/auth0/express-openid-connect/compare/v0.5.0...v0.6.0)
**Breaking changes in this release:**
This release includes important changes to user session and token handling which will require an update for all applications.
First, a new, required configuration key - `appSessionSecret`- has been added. The value here will be used to generate keys which are in turn used to encrypt the user identity returned from the identity provider. This encrypted and signed identity is stored in a cookie and used to populate the `req.openid.user` property, as before. This key should be set to either a secure, random value to use this built-in session or `false` to provide [your own custom application session handling](https://github.com/auth0/express-openid-connect/blob/master/EXAMPLES.md#4-custom-user-session-handling). A value for this can be generated with `openssl` like so:
```
❯ openssl rand -hex 32
f334eb9ee5898101f90047ec46f18c2f4c082f5eeef109920d6b0fc5b79b6f29
```
As part of these changes, a session middleware is no longer required for this library. One can be added and used for application session and tokens (see above and below, respectively) but initialization will no longer fail if one is not present.
Additionally, tokens returned from the identity provider will no longer be stored in a session middleware automatically. If your application requires access, refresh, or ID tokens to be retrieved and stored (not just the user identity), you will need to provide a method for that storage in version 0.6.0 and beyond. [See our examples page for guidance](https://github.com/auth0/express-openid-connect/blob/master/EXAMPLES.md#5-obtaining-and-storing-access-tokens-to-call-external-apis).
**Closed issues**
- "legacySameSiteCookie" for auth config params is not yet available in the typings file. [\#44](https://github.com/auth0/express-openid-connect/issues/44)
- Validate configured routes [\#21](https://github.com/auth0/express-openid-connect/issues/21)
**Added**
- Add path validation [\#47](https://github.com/auth0/express-openid-connect/pull/47) ([joshcanhelp](https://github.com/joshcanhelp))
- Add typescript defs new config [\#46](https://github.com/auth0/express-openid-connect/pull/46) ([joshcanhelp](https://github.com/joshcanhelp))
- Add SameSite support [\#39](https://github.com/auth0/express-openid-connect/pull/39) ([joshcanhelp](https://github.com/joshcanhelp))
- Add custom callback handling [\#37](https://github.com/auth0/express-openid-connect/pull/37) ([joshcanhelp](https://github.com/joshcanhelp))
- Add body parser to login and callback route [\#33](https://github.com/auth0/express-openid-connect/pull/33) ([davidpatrick](https://github.com/davidpatrick))
**Changed**
- Change session and token handling [\#42](https://github.com/auth0/express-openid-connect/pull/42) ([joshcanhelp](https://github.com/joshcanhelp))
## [v0.5.0](https://github.com/auth0/express-openid-connect/tree/v0.5.0) (2019-10-17)

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

# Examples
## 1. Basic Setup
## 1. Basic setup

@@ -13,4 +13,3 @@ The simplest use case for this middleware:

BASE_URL=https://YOUR_APPLICATION_ROOT_URL
SESSION_NAME=YOUR_SESSION_NAME
COOKIE_SECRET=LONG_RANDOM_VALUE
APP_SESSION_SECRET=LONG_RANDOM_STRING
```

@@ -21,11 +20,3 @@

const { auth } = require('express-openid-connect');
const session = require('cookie-session');
app.use(express.urlencoded({ extended: false }));
app.use(session({
name: process.env.SESSION_NAME,
secret: process.env.COOKIE_SECRET
}));
app.use(auth({

@@ -48,3 +39,3 @@ required: true

If your application has routes accessible to anonymous users, you can enable authorization per routes:
If your application has routes accessible to anonymous users, you can enable authorization per route:

@@ -71,3 +62,2 @@ ```js

//initialization
app.use(auth({

@@ -82,5 +72,5 @@ required: req => req.originalUrl.startsWith('/admin/')

## 3. Route Customization
## 3. Route customization
If you need to customize the routes, you can opt-out from the default routes and write your own route handler:
If you need to customize the provided login and logout routes, you can disable the default routes and write your own route handler:

@@ -104,15 +94,92 @@ ```js

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.
Please note that the login and logout routes are not required. Trying to access any protected resource triggers a redirect directly to Auth0 to login. These are helpful if you need to provide user-facing links to login or logout.
## 4. Using refresh tokens
## 4. Custom user session handling
Refresh tokens can be requested along with access tokens using the `offline_access` scope during login:
By default, this library uses an encrypted and signed cookie to store the user identity claims as an application session. If the size of the user identity is too large or you're concerned about sensitive data being stored, you can provide your own session handling as part of the `getUser` function.
If, for example, you want the user session to be stored on the server, you can use a session middleware like `express-session`. We recommend persisting the data in a session store other than in-memory (which is the default), otherwise all sessions will be lost when the server restarts. The basics of handling the user identity server-side is below:
```js
const session = require('express-session');
app.use(session({
secret: 'replace this with a long, random, static string',
cookie: {
// Sets the session cookie to expire after 7 days.
maxAge: 7 * 24 * 60 * 60 * 1000
}
}));
app.use(auth({
// Setting this configuration key to false will turn off internal session handling.
appSessionSecret: false,
handleCallback: async function (req, res, next) {
// This will store the user identity claims in the session
req.session.userIdentity = req.openidTokens.claims();
next();
},
getUser: async function (req) {
return req.session.userIdentity;
}
}));
```
## 5. Obtaining and storing access tokens to call external APIs
If your application needs to request and store [access tokens](https://auth0.com/docs/tokens/access-tokens) for external APIs, you must provide a method to store the incoming tokens during callback. We recommend to use a persistant store, like a database or Redis, to store these tokens directly associated with the user for which they were requested.
If the tokens only need to be used during the user's session, they can be stored using a session middleware like `express-session`. We recommend persisting the data in a session store other than in-memory (which is the default), otherwise all tokens will be lost when the server restarts. The basics of handling the tokens is below:
```js
const session = require('express-session');
app.use(session({
secret: 'replace this with a long, random, static string',
cookie: {
// Sets the session cookie to expire after 7 days.
maxAge: 7 * 24 * 60 * 60 * 1000
}
}));
app.use(auth({
authorizationParams: {
response_type: 'code',
audience: process.env.API_URL,
scope: 'openid profile email read:reports'
},
handleCallback: async function (req, res, next) {
req.session.openidTokens = req.openidTokens;
next();
}
}));
```
On a route that needs to use the access token, pull the token data from the storage and initialize a new `TokenSet` using `makeTokenSet()` method exposed by this library:
```js
app.get('/route-that-calls-an-api', async (req, res, next) => {
const tokenSet = req.openid.makeTokenSet(req.session.openidTokens);
let apiData = {};
// Check for and use tokenSet.access_token for the API call ...
});
```
## 6. Obtaining and using refresh tokens
[Refresh tokens](https://auth0.com/docs/tokens/refresh-token/current) can be requested along with access tokens using the `offline_access` scope during login. Please see the section on access tokens above for information on token storage.
```js
app.use(auth({
authorizationParams: {
response_type: 'code id_token',
response_mode: 'form_post',
// API identifier to indicate which API this application will be calling.
audience: process.env.API_URL,
// Include the required scopes as well as offline_access to generate a refresh token.
scope: 'openid profile email read:reports offline_access'
},
handleCallback: async function (req, res, next) {
// See the "Using access tokens" section above for token handling.
next();
}

@@ -128,5 +195,8 @@ }));

let apiData = {};
let tokenSet = req.openid.tokens;
if (tokenSet.expired() && tokenSet.refresh_token) {
// How the tokenSet is created will depend on how the tokens are stored.
let tokenSet = req.openid.makeTokenSet(req.session.openidTokens);
let refreshToken = tokenSet.refresh_token;
if (tokenSet && tokenSet.expired() && refreshToken) {
try {

@@ -138,23 +208,34 @@ tokenSet = await req.openid.client.refresh(tokenSet);

tokenSet.refresh_token = req.openid.tokens.refresh_token;
req.openid.tokens = tokenSet;
}
// New tokenSet may not include a new refresh token.
tokenSet.refresh_token = tokenSet.refresh_token ?? refreshToken;
try {
apiData = await request(
process.env.API_URL,
{
headers: { authorization: `Bearer ${tokenSet.access_token}` },
json: true
}
);
} catch(err) {
next(err);
// Where you store the refreshed tokenSet will depend on how the tokens are stored.
req.session.openidTokens = tokenSet;
}
res.render('api-data-template', {
user: req.openid && req.openid.user,
apiData
});
// Check for and use tokenSet.access_token for the API call ...
});
```
## 7. Calling userinfo
If your application needs to call the userinfo endpoint for the user's identity instead of the ID token used by default, add a `handleCallback` function during initialization that will make this call. Save the claims retrieved from the userinfo endpoint to the `appSessionName` on the request object (default is `identity`):
```js
app.use(auth({
handleCallback: async function (req, res, next) {
const client = req.openid.client;
req.identity = req.identity || {};
try {
req.identity.claims = await client.userinfo(req.openidTokens);
next();
} catch(e) {
next(e);
}
},
authorizationParams: {
response_type: 'code',
scope: 'openid profile email'
}
}));
```
// Type definitions for express-openid-connect
import { AuthorizationParameters, TokenSet, UserinfoResponse } from 'openid-client';
import { Request, RequestHandler } from 'express';
import { Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from 'express';
interface ConfigParams {
/**
* Object defining application session cookie attributes.
*/
appSessionCookie?: SessionCookieConfigParams;
/**
* Integer value, in seconds, for application session duration.
*/
appSessionDuration?: number;
/**
* String value for the cookie name used for the internal session.
*/
appSessionName?: string;
/**
* REQUIRED. The secret(s) used to derive an encryption key for the user identity in a session cookie.
* Can use env key APP_SESSION_SECRET instead.
*/
appSessionSecret: boolean | string | string[];
/**
* Boolean value to enable Auth0's logout feature.
*/
auth0Logout?: boolean;
/**
* URL parameters used when redirecting users to the authorization server to log in.
*/
authorizationParams?: AuthorizationParameters
/**
* REQUIRED. The root URL for the application router.
* Can use env key BASE_URL instead.
*/
baseURL?: string;
/**
* REQUIRED. The Client ID for your application.
* Can use env key CLIENT_ID instead.
*/
clientID?: string;
/**
* The Client Secret for your application.
* Required when requesting access tokens.
* Can use env key CLIENT_SECRET instead.
*/
clientSecret?: string;
/**
* Integer value for the system clock's tolerance (leeway) in seconds for ID token verification.
*/
clockTolerance?: number;
/**
* Throw a 401 error instead of triggering the login process for routes that require authentication.
*/
errorOnRequiredAuth?: boolean;
getUser?: (tokenSet: TokenSet) => undefined | UserinfoResponse;
/**
* Function that returns the profile for `req.openid.user`.
*/
getUser?: (req: Request, config: ConfigParams) => undefined | UserinfoResponse;
/**
* Function that runs on the callback route, after callback processing but before redirection.
*/
handleCallback?: RequestHandler;
/**
* Default options object used for all HTTP calls made by the library.
*/
httpOptions?: object;
/**
* Array value of claims to remove from the ID token before storing the cookie session.
*/
identityClaimFilter?: string[];
/**
* Boolean value to log the user out from the identity provider on application logout.
*/
idpLogout?: boolean;
/**
* String value for the expected ID token algorithm.
*/
idTokenAlg?: string;
/**
* REQUIRED. The root URL for the token issuer with no trailing slash.
* Can use env key ISSUER_BASE_URL instead.
*/
issuerBaseURL?: string;
/**
* Set a fallback cookie with no SameSite attribute when response_mode is form_post.
*/
legacySameSiteCookie?: boolean;
/**
* Relative path to application login.
*/
loginPath?: string;
/**
* Relative path to application logout.
*/
logoutPath?: string;
/**
* Relative path to the application callback to process the response from the authorization server.
*/
redirectUriPath?: string;
/**
* Require authentication for all routes.
*/
required?: boolean | ((request: Request) => boolean);
/**
* Boolean value to automatically install the login and logout routes.
*/
routes?: boolean;
}
interface SessionCookieConfigParams {
domain?: string;
httpOnly?: boolean;
path?: string;
sameSite?: string;
secure?: boolean;
}
export function auth(params?: ConfigParams): RequestHandler;
export function requiresAuth(): RequestHandler;
export function unauthorizedHandler(): RequestHandler;
export function unauthorizedHandler(): ErrorRequestHandler;
const Joi = require('@hapi/joi');
const clone = require('clone');
const loadEnvs = require('./loadEnvs');
const getUser = require('./getUser');
const getUser = require('./hooks/getUser');
const handleCallback = require('./hooks/handleCallback');
const defaultAuthorizeParams = {
response_mode: 'form_post',
response_type: 'id_token',
response_mode: 'form_post',
scope: 'openid profile email'

@@ -13,27 +14,49 @@ };

const authorizationParamsSchema = Joi.object().keys({
response_mode: [Joi.string().optional(), Joi.allow(null).optional()],
response_type: Joi.string().required(),
response_mode: [Joi.string().optional(), Joi.allow(null).optional()],
scope: Joi.string().required()
}).unknown(true);
// const requiredParams = ['issuerBaseURL', 'baseURL', 'clientID'];
const appSessionCookieSchema = Joi.object().keys({
domain: Joi.string().optional(),
httpOnly: Joi.boolean().optional(),
path: Joi.string().optional(),
sameSite: Joi.string().valid(['Lax', 'Strict', 'None']).optional(),
secure: Joi.boolean().optional()
}).unknown(false);
const paramsSchema = Joi.object().keys({
httpOptions: Joi.object().optional(),
issuerBaseURL: Joi.alternatives([ Joi.string().uri(), Joi.string().hostname() ]).required(),
appSessionCookie: Joi.object().optional(),
appSessionDuration: Joi.number().integer().optional().default(7 * 24 * 60 * 60),
appSessionName: Joi.string().token().optional().default('identity'),
appSessionSecret: Joi.alternatives([
// Single string key.
Joi.string(),
// Array of keys to allow for rotation.
Joi.array().items(Joi.string()),
// False to stop client session from being created.
Joi.boolean().valid([false])
]).required(),
auth0Logout: Joi.boolean().optional().default(false),
authorizationParams: Joi.object().optional(),
baseURL: Joi.string().uri().required(),
clientID: Joi.string().required(),
clientSecret: Joi.string().optional(),
idTokenAlg: Joi.string().not('none').optional().default('RS256'),
authorizationParams: Joi.object().optional(),
clockTolerance: Joi.number().optional().default(60),
errorOnRequiredAuth: Joi.boolean().optional().default(false),
getUser: Joi.func().optional().default(getUser),
handleCallback: Joi.func().optional().default(handleCallback),
httpOptions: Joi.object().optional(),
identityClaimFilter: Joi.array().optional().default(['aud', 'iss', 'iat', 'exp', 'nonce', 'azp', 'auth_time']),
idpLogout: Joi.boolean().optional().default(false).when(
'auth0Logout', { is: true, then: Joi.boolean().optional().default(true) }
),
idTokenAlg: Joi.string().not('none').optional().default('RS256'),
issuerBaseURL: Joi.alternatives([ Joi.string().uri(), Joi.string().hostname() ]).required(),
legacySameSiteCookie: Joi.boolean().optional().default(true),
loginPath: Joi.string().uri({relativeOnly: true}).optional().default('/login'),
logoutPath: Joi.string().uri({relativeOnly: true}).optional().default('/logout'),
redirectUriPath: Joi.string().uri({relativeOnly: true}).optional().default('/callback'),
required: Joi.alternatives([ Joi.func(), Joi.boolean()]).optional().default(true),
routes: Joi.boolean().optional().default(true),
errorOnRequiredAuth: Joi.boolean().optional().default(false),
auth0Logout: Joi.boolean().optional().default(false),
redirectUriPath: Joi.string().optional().default('/callback'),
loginPath: Joi.string().optional().default('/login'),
logoutPath: Joi.string().optional().default('/logout'),
idpLogout: Joi.boolean().optional().default(false)
.when('auth0Logout', { is: true, then: Joi.boolean().optional().default(true) })
});

@@ -68,2 +91,15 @@

function buildAppSessionCookieConfig(cookieConfig) {
cookieConfig = cookieConfig && Object.keys(cookieConfig).length ? cookieConfig : {};
cookieConfig = Object.assign({ httpOnly: true }, cookieConfig);
const cookieConfigValidation = Joi.validate(cookieConfig, appSessionCookieSchema);
if(cookieConfigValidation.error) {
throw new Error(cookieConfigValidation.error.details[0].message);
}
return cookieConfig;
}
module.exports.get = function(params) {

@@ -83,2 +119,3 @@ let config = typeof params == 'object' ? clone(params) : {};

config.authorizationParams = buildAuthorizeParams(config.authorizationParams);
config.appSessionCookie = buildAppSessionCookieConfig(config.appSessionCookie);

@@ -85,0 +122,0 @@ // Code grant requires a client secret to exchange the code for tokens

const cb = require('cb');
const urlJoin = require('url-join');
const crypto = require('crypto');
const transient = require('./transientHandler');
const { get: getClient } = require('./client');

@@ -15,17 +15,10 @@ const { TokenSet } = require('openid-client');

get tokens() {
if (!this._req.session || !this._req.session.openidTokens) {
return undefined;
}
return new TokenSet(this._req.session.openidTokens);
get isAuthenticated() {
return !!this.user;
}
set tokens(value) {
this._req.session.openidTokens = value;
makeTokenSet(tokenSet) {
return new TokenSet(tokenSet);
}
get isAuthenticated() {
return !!this.user;
}
async load() {

@@ -35,5 +28,4 @@ if (!this.client) {

}
if (this.tokens) {
this.user = await this._config.getUser(this.tokens);
}
this.user = await this._config.getUser(this._req, this._config);
}

@@ -62,26 +54,28 @@ }

const res = this._res;
const authorizeParams = this._config.authorizationParams;
const config = this._config;
const client = req.openid.client;
const authorizeParams = config.authorizationParams;
const transientOpts = {
legacySameSiteCookie: config.legacySameSiteCookie,
sameSite: config.authorizationParams.response_mode === 'form_post' ? 'None' : 'Lax'
};
try {
const redirect_uri = this.getRedirectUri();
if (typeof req.session === 'undefined') {
return next(new Error('This router needs the session middleware'));
}
const client = this._req.openid.client;
req.session.nonce = crypto.randomBytes(8).toString('hex');
req.session.state = crypto.randomBytes(10).toString('hex');
if(params.returnTo) {
req.session.returnTo = params.returnTo;
let returnTo;
if (params.returnTo) {
returnTo = params.returnTo;
} else if (req.method === 'GET') {
req.session.returnTo = req.originalUrl;
returnTo = req.originalUrl;
} else {
req.session.returnTo = this._config.baseURL;
returnTo = this._config.baseURL;
}
// TODO: Store this in state
transient.store('returnTo', res, Object.assign({value: returnTo}, transientOpts));
const authParams = Object.assign({
nonce: req.session.nonce,
state: req.session.state,
redirect_uri
nonce: transient.store('nonce', res, transientOpts),
state: transient.store('state', res, transientOpts),
redirect_uri: this.getRedirectUri()
}, authorizeParams, params.authorizationParams || {});

@@ -102,12 +96,4 @@

if (!req.session || !req.openid) {
return res.redirect(returnURL);
}
req[this._config.appSessionName] = undefined;
if (typeof req.session.destroy === 'function') {
req.session.destroy();
} else {
req.session = null;
}
if (!this._config.idpLogout) {

@@ -114,0 +100,0 @@ return res.redirect(returnURL);

@@ -6,2 +6,3 @@ const fieldsEnvMap = {

'clientSecret': 'CLIENT_SECRET',
'appSessionSecret': 'APP_SESSION_SECRET',
};

@@ -11,15 +12,7 @@

Object.keys(fieldsEnvMap).forEach(k => {
if (params[k]) {
return;
if (typeof params[k] === 'undefined') {
params[k] = process.env[fieldsEnvMap[k]];
}
params[k] = process.env[fieldsEnvMap[k]];
});
if (!params.baseURL &&
!process.env.BASE_URL &&
process.env.PORT &&
process.env.NODE_ENV !== 'production') {
params.baseURL = `http://localhost:${process.env.PORT}`;
}
};
const express = require('express');
const cb = require('cb');
const createError = require('http-errors');
const cookieParser = require('cookie-parser');
const { get: getConfig } = require('../lib/config');
const { get: getClient } = require('../lib/client');
const requiresAuth = require('./requiresAuth');
const transient = require('../lib/transientHandler');
const { RequestContext, ResponseContext } = require('../lib/context');
const appSession = require('../lib/appSession');
const enforceLeadingSlash = (path) => {
return '/' === path.split('')[0] ? path : '/' + path;
};
/**
* Returns a router with two routes /login and /callback
*
* @param {Object} [params] The parameters object
* @param {string} [params.issuerBaseURL] The url address for the token issuer.
* @param {string} [params.baseURL] The url of the web application where you are installing the router.
* @param {string} [params.clientID] The client id.
* @param {string} [params.clientSecret] The client secret, only required for some grants.
* @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.
* @param {boolean|Function} [params.required=true] a boolean to indicate that every route after this middleware requires authentication or
* a function receiving a request and return a boolean to determine which routes needs authentication.
* @param {boolean} [params.errorOnRequiredAuth=false] automatically handle unauthorized errors by triggering the authentication process
* @param {boolean} [params.idpLogout=false] logout the user from the identity provider on logout
* @param {boolean} [params.auth0Logout=false] use the auth0's logout mechanism if OpenID Connect session management is not supported
* @param {boolean|Function} [params.routes=true] a boolean indicating if the routes /login and /logout should be added to the application
* @param {string} [params.redirectUriPath=/callback] The path for the redirect uri, defaults to /callback.
* @param {Object} [params.authorizationParams] The parameters for the authorization call. Defaults to
* - response_type: "id_token"
* - reponse_mode: "form_post"
* - scope: "openid profile email"
* @param {string} params.authorizationParams.response_type The response type.
* @param {string} [params.authorizationParams.response_mode] The response mode.
* @param {string} [params.authorizationParams.scope=openid profile email] The scope.
* @param {Object} [params] The parameters object; see index.d.ts for types and descriptions.
*

@@ -46,23 +34,29 @@ * @returns {express.Router} the router

// Only use the internal cookie-based session if appSessionSecret is provided.
if (config.appSessionSecret) {
router.use(appSession({
name: config.appSessionName,
secret: config.appSessionSecret,
duration: config.appSessionDuration,
cookieOptions: config.appSessionCookie
}));
}
router.use(async (req, res, next) => {
req.openid = new RequestContext(config, req, res, next);
try {
req.openid = new RequestContext(config, req, res, next);
await req.openid.load();
res.openid = new ResponseContext(config, req, res, next);
req.isAuthenticated = () => req.openid.isAuthenticated;
next();
} catch(err) {
next(err);
}
res.openid = new ResponseContext(config, req, res, next);
req.isAuthenticated = () => req.openid.isAuthenticated;
next();
});
if (config.routes) {
router.get(config.loginPath, (req, res) => {
router.get(enforceLeadingSlash(config.loginPath), express.urlencoded({ extended: false }), (req, res) => {
res.openid.login({ returnTo: config.baseURL });
});
router.get(config.logoutPath, (req, res) => res.openid.logout());
router.get(enforceLeadingSlash(config.logoutPath), (req, res) => res.openid.logout());
}

@@ -83,11 +77,8 @@

router[callbackMethod](config.redirectUriPath, async (req, res, next) => {
router[callbackMethod](enforceLeadingSlash(config.redirectUriPath), express.urlencoded({ extended: false }), cookieParser(), async (req, res, next) => {
next = cb(next).once();
try {
const { nonce, state } = req.session;
delete req.session.nonce;
delete req.session.state;
const redirect_uri = res.openid.getRedirectUri();
const client = req.openid.client;
const transientOpts = { legacySameSiteCookie: config.legacySameSiteCookie };

@@ -99,4 +90,4 @@ let tokenSet;

tokenSet = await client.callback(redirect_uri, callbackParams, {
nonce,
state,
nonce: transient.getOnce('nonce', req, res, transientOpts),
state: transient.getOnce('state', req, res, transientOpts),
response_type: authorizeParams.response_type,

@@ -108,11 +99,23 @@ });

req.session.openidTokens = tokenSet;
req.openidTokens = tokenSet;
const returnTo = req.session.returnTo || '/';
delete req.session.returnTo;
if (config.appSessionSecret) {
let identityClaims = tokenSet.claims();
config.identityClaimFilter.forEach(claim => {
delete identityClaims[claim];
});
res.redirect(returnTo);
req[config.appSessionName].claims = identityClaims;
}
next();
} catch (err) {
next(err);
}
},
config.handleCallback,
function (req, res) {
const transientOpts = { legacySameSiteCookie: config.legacySameSiteCookie };
const returnTo = transient.getOnce('returnTo', req, res, transientOpts) || config.baseURL;
res.redirect(returnTo);
});

@@ -119,0 +122,0 @@

{
"name": "express-openid-connect",
"version": "0.5.0",
"description": "An Express.js middleware to protect OpenID Connect web applications.",
"version": "0.6.0",
"description": "Express middleware to protect web applications using OpenID Connect.",
"homepage": "https://github.com/auth0/express-openid-connect",

@@ -17,4 +17,9 @@ "license": "MIT",

"clone": "^2.1.2",
"cookie": "^0.4.0",
"cookie-parser": "^1.4.4",
"futoin-hkdf": "^1.2.1",
"http-errors": "^1.7.3",
"openid-client": "^3.7.3",
"jose": "^1.17.2",
"on-headers": "^1.0.2",
"openid-client": "^3.9.2",
"p-memoize": "^3.1.0",

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

"@types/express": "^4.17.1",
"body-parser": "^1.19.0",
"chai": "^4.2.0",

@@ -28,0 +32,0 @@ "cookie-session": "^2.0.0-beta.3",

@@ -9,3 +9,2 @@ # Express OpenID Connect

- Express v4.16 or higher
- A session middleware like [express-session](https://www.npmjs.com/package/express-session) or [cookie-session](https://www.npmjs.com/package/cookie-session)

@@ -30,9 +29,7 @@ **Please Note:** This library is currently in pre-release status and has not had a complete security review. We **do not** recommend using this library in production yet. As we move towards early access, please be aware that releases may contain breaking changes. We will be monitoring the Issues queue here for feedback and questions. PRs and comments on existing PRs are welcome!

More complete documentation coming soon!
- Our [Express Quickstart](https://auth0.com/docs/quickstart/webapp/express) is the quickest way to get up and running from scratch.
- Use the [Examples for common configurations](https://github.com/auth0/express-openid-connect/blob/master/EXAMPLES.md) for use cases beyond the basics.
- The [API documentation](https://github.com/auth0/express-openid-connect/blob/master/API.md) details all configuration options, methods, and data that this library provides.
- You can [run the sample application](https://github.com/auth0-samples/auth0-express-webapp-sample/tree/master) to see how this SDK functions without writing your own integration.
- [Examples for common configurations](EXAMPLES.md)
- [API documentation](API.md)
- [Sample application](https://github.com/joshcanhelp/express-oidc-connect-starter)
- [Sample implementation](https://github.com/auth0/node-sample-with-openid-client)
## Installation

@@ -48,10 +45,4 @@

The library needs the following values to request and accept authentication:
The library needs [the following required configuration keys](https://github.com/auth0/express-openid-connect/blob/master/API.md#required-keys) to request and accept authentication. These can be configured in a `.env` file in the root of your application:
- **Issuer Base URL**: The base URL of the authorization server. If you're using Auth0, this is your tenant **Domain** pre-pended with `https://` (like `https://tenant.auth0.com`) found on the **Settings** tab for your Application in the [Auth0 dashboard](https://manage.auth0.com).
- **Client ID**: The identifier for your Application. If you're using Auth0, this value can also be found on the **Settings** tab for your Application.
- **Base URL**: The root URL for your application.
These can be configured in a `.env` file in the root of your application:
```text

@@ -63,2 +54,3 @@ # .env

BASE_URL=https://YOUR_APPLICATION_ROOT_URL
APP_SESSION_SECRET=LONG_RANDOM_VALUE
```

@@ -71,12 +63,17 @@

const { auth } = require('express-openid-connect');
app.use(auth({
required: true,
issuerBaseURL: 'https://YOUR_DOMAIN',
baseURL: 'https://YOUR_APPLICATION_ROOT_URL',
clientID: 'YOUR_CLIENT_ID'
clientID: 'YOUR_CLIENT_ID',
appSessionKey: 'LONG_RANDOM_STRING'
}));
```
See [Examples](EXAMPLES.md) for how to get started authenticating users.
With this basic configuration, your application will require authentication for all routes and store the user identity in an encrypted and signed cookie.
See the [examples](EXAMPLES.md) for route-specific authentication, custom application session handling, requesting and using access tokens for external APIs, and more.
See the [API documentation](API.md) for additional configuration possibilities and provided methods.
## Contributing

@@ -83,0 +80,0 @@

@@ -16,2 +16,18 @@ const assert = require('chai').assert;

const getCookieFromResponse = (res, cookieName) => {
const cookieHeaders = res.headers['set-cookie'];
const foundHeader = cookieHeaders.filter(header => header.substring(0,6) === cookieName + '=')[0];
if (!foundHeader) {
return false;
}
const cookieValuePart = foundHeader.split('; ')[0];
if (!cookieValuePart) {
return false;
}
return cookieValuePart.split('=')[1];
};
describe('auth', function() {

@@ -24,2 +40,3 @@ describe('default', () => {

router = expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -54,3 +71,2 @@ baseURL: 'https://example.org',

assert.equal(parsed.query.client_id, '__test_client_id__');
assert.equal(parsed.query.scope, 'openid profile email');

@@ -63,5 +79,6 @@ assert.equal(parsed.query.response_type, 'id_token');

const session = (await request.get('/session', { jar, baseUrl, json: true })).body;
assert.equal(session.nonce, parsed.query.nonce);
assert.equal(session.state, parsed.query.state);
const cookies = jar.getCookies(baseUrl + '/login');
assert.equal(cookies.filter(cookie => cookie.key === '_nonce')[0].value, parsed.query.nonce);
assert.equal(cookies.filter(cookie => cookie.key === '_state')[0].value, parsed.query.state);
});

@@ -77,2 +94,3 @@

router = expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -118,3 +136,4 @@ baseURL: 'https://example.org',

before(async function() {
router = router = expressOpenid.auth({
router = expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -149,2 +168,6 @@ clientSecret: '__test_client_secret__',

assert.property(parsed.query, 'state');
assert.property(res.headers, 'set-cookie');
assert.equal(getCookieFromResponse(res, 'nonce'), parsed.query.nonce);
assert.equal(getCookieFromResponse(res, 'state'), parsed.query.state);
});

@@ -162,3 +185,4 @@

before(async function() {
router = router = expressOpenid.auth({
router = expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -206,8 +230,9 @@ baseURL: 'https://example.org',

router = expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
redirectUriPath: '/custom-callback',
loginPath: '/custom-login',
logoutPath: '/custom-logout',
redirectUriPath: 'custom-callback',
loginPath: 'custom-login',
logoutPath: 'custom-logout',
});

@@ -214,0 +239,0 @@ baseUrl = await server.create(router);

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

return () => {
const router = expressOpenid.auth({
const authOpts = Object.assign({}, {
appSessionSecret: '__test_session_secret__',
clientID: clientID,

@@ -21,3 +22,4 @@ baseURL: 'https://example.org',

required: false
});
}, params.authOpts || {});
const router = expressOpenid.auth(authOpts);

@@ -31,23 +33,12 @@ let baseUrl;

this.baseUrl = baseUrl = await server.create(router);
await request.post({
uri: '/session',
baseUrl, jar,
json: params.session
});
});
before(async function() {
this.response = await request.post('/callback', {
baseUrl,
jar,
json: params.body
Object.keys(params.cookies).forEach(function(cookieName) {
jar.setCookie(
`${cookieName}=${params.cookies[cookieName]}; Max-Age=3600; Path=/; HttpOnly;`,
baseUrl + '/callback',
);
});
});
before(async function() {
this.currentSession = await request.get('/session', {
baseUrl,
jar,
json: true,
}).then(r => r.body);
this.response = await request.post('/callback', {baseUrl, jar, json: params.body});
this.currentUser = await request.get('/user', {baseUrl, jar, json: true}).then(r => r.body);
});

@@ -59,2 +50,18 @@

function makeIdToken(payload) {
if (typeof payload !== 'object' ) {
payload = {
'nickname': '__test_nickname__',
'sub': '__test_sub__',
'iss': 'https://test.auth0.com/',
'aud': clientID,
'iat': Math.round(Date.now() / 1000),
'exp': Math.round(Date.now() / 1000) + 60000,
'nonce': '__test_nonce__'
};
}
return jwt.sign(payload, cert.key, { algorithm: 'RS256', header: { kid: cert.kid } });
}
//For the purpose of this test the fake SERVER returns the error message in the body directly

@@ -67,3 +74,3 @@ //production application should have an error middleware.

describe('when body is empty', testCase({
session: {
cookies: {
nonce: '__test_nonce__',

@@ -84,4 +91,21 @@ state: '__test_state__'

describe('when state is missing', testCase({
cookies: {},
body: {
state: '__test_state__',
id_token: '__invalid_token__'
},
assertions() {
it('should return 400', function() {
assert.equal(this.response.statusCode, 400);
});
it('should return the reason to the error handler', function() {
assert.equal(this.response.body.err.message, 'checks.state argument is missing');
});
}
}));
describe("when state doesn't match", testCase({
session: {
cookies: {
nonce: '__test_nonce__',

@@ -105,3 +129,3 @@ state: '__valid_state__'

describe("when id_token can't be parsed", testCase({
session: {
cookies: {
nonce: '__test_nonce__',

@@ -126,3 +150,3 @@ state: '__test_state__'

describe('when id_token has invalid alg', testCase({
session: {
cookies: {
nonce: '__test_nonce__',

@@ -147,3 +171,3 @@ state: '__test_state__'

describe('when id_token is missing issuer', testCase({
session: {
cookies: {
nonce: '__test_nonce__',

@@ -154,3 +178,3 @@ state: '__test_state__'

state: '__test_state__',
id_token: jwt.sign({sub: '__test_sub__'}, cert.key, { algorithm: 'RS256' })
id_token: makeIdToken({sub: '__test_sub__'})
},

@@ -168,6 +192,5 @@ assertions() {

describe('when id_token is valid', testCase({
session: {
describe('when nonce is missing from cookies', testCase({
cookies: {
state: '__test_state__',
nonce: '__test_nonce__',
returnTo: '/return-to'

@@ -177,16 +200,22 @@ },

state: '__test_state__',
id_token: jwt.sign({
'nickname': '__test_nickname__',
'name': '__test_name__',
'email': '__test_email__',
'email_verified': true,
'iss': 'https://test.auth0.com/',
'sub': '__test_sub__',
'aud': clientID,
'iat': Math.round(Date.now() / 1000),
'exp': Math.round(Date.now() / 1000) + 60000,
'nonce': '__test_nonce__'
}, cert.key, { algorithm: 'RS256', header: { kid: cert.kid } })
id_token: makeIdToken()
},
assertions() {
it('should return the reason to the error handler', function() {
assert.match(this.response.body.err.message, /nonce mismatch/i);
});
}
}));
describe('when id_token is valid', testCase({
cookies: {
_state: '__test_state__',
_nonce: '__test_nonce__',
_returnTo: '/return-to'
},
body: {
state: '__test_state__',
id_token: makeIdToken()
},
assertions() {
it('should return 302', function() {

@@ -201,5 +230,15 @@ assert.equal(this.response.statusCode, 302);

it('should contain the claims in the current session', function() {
assert.ok(this.currentSession.openidTokens);
assert.ok(this.currentUser);
assert.equal(this.currentUser.sub, '__test_sub__');
assert.equal(this.currentUser.nickname, '__test_nickname__');
});
it('should strip validation claims from the ID tokens', function() {
assert.notExists(this.currentUser.iat);
assert.notExists(this.currentUser.iss);
assert.notExists(this.currentUser.aud);
assert.notExists(this.currentUser.exp);
assert.notExists(this.currentUser.nonce);
});
it('should expose the user in the request', async function() {

@@ -216,2 +255,70 @@ const res = await request.get('/user', {

describe('when legacy samesite fallback is off', testCase({
authOpts: {
// Do not check the fallback cookie value.
legacySameSiteCookie: false
},
cookies: {
// Only set the fallback cookie value.
_state: '__test_state__'
},
body: {
state: '__test_state__',
id_token: '__invalid_token__'
},
assertions() {
it('should return 400', function() {
assert.equal(this.response.statusCode, 400);
});
it('should return the reason to the error handler', function() {
assert.equal(this.response.body.err.message, 'checks.state argument is missing');
});
}
}));
describe('uses custom callback handling', testCase({
authOpts: {
handleCallback: () => {
throw new Error('__test_callback_error__');
}
},
cookies: {
_state: '__test_state__',
_nonce: '__test_nonce__'
},
body: {
state: '__test_state__',
id_token: makeIdToken()
},
assertions() {
it('throws an error from the custom handler', function() {
assert.equal(this.response.body.err.message, '__test_callback_error__');
});
}
}));
describe('uses custom claim filtering', testCase({
authOpts: {
identityClaimFilter: []
},
cookies: {
_state: '__test_state__',
_nonce: '__test_nonce__'
},
body: {
state: '__test_state__',
id_token: makeIdToken()
},
assertions() {
it('should have previously-stripped claims', function() {
assert.equal(this.currentUser.iss, 'https://test.auth0.com/');
assert.equal(this.currentUser.aud, clientID);
assert.equal(this.currentUser.nonce, '__test_nonce__');
assert.exists(this.currentUser.iat);
assert.exists(this.currentUser.exp);
});
}
}));
});

@@ -19,2 +19,3 @@ const { assert } = require('chai');

const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -30,3 +31,3 @@ clientSecret: '__test_client_secret__',

});
it('should save the passed values', async function() {

@@ -36,15 +37,15 @@ assert.equal('__test_client_id__', client.client_id);

});
it('should send the correct default headers', async function() {
const headers = await client.introspect('__test_token__', '__test_hint__');
const headerProps = Object.getOwnPropertyNames(headers);
assert.include(headerProps, 'auth0-client');
const decodedTelemetry = JSON.parse(Buffer.from(headers['auth0-client'], 'base64').toString('ascii'));
assert.equal( 'express-oidc', decodedTelemetry.name );
assert.equal( pkg.version, decodedTelemetry.version );
assert.equal( process.version, decodedTelemetry.env.node );
assert.include( headerProps, 'user-agent');

@@ -57,2 +58,3 @@ assert.equal( `express-openid-connect/${pkg.version}`, headers['user-agent']);

const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -75,15 +77,15 @@ clientSecret: '__test_client_secret__',

});
it('should send the correct default headers', async function() {
const headers = await client.introspect('__test_token__', '__test_hint__');
const headerProps = Object.getOwnPropertyNames(headers);
// User agent header should be overridden.
assert.include(headerProps, 'user-agent');
assert.equal('__test_custom_user_agent__', headers['user-agent']);
// Custom header should be added.
assert.include(headerProps, 'x-custom-header');
assert.equal('__test_custom_header__', headers['x-custom-header']);
// Telemetry header should not be overridden.

@@ -90,0 +92,0 @@ assert.include(headerProps, 'auth0-client');

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

const config = getConfig({
appSessionSecret: false,
clientID: '__test_client_id__',

@@ -32,2 +33,3 @@ issuerBaseURL: 'https://test.auth0.com',

const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -57,2 +59,3 @@ clientSecret: '__test_client_secret__',

const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -72,2 +75,3 @@ issuerBaseURL: 'https://test.auth0.com',

const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -86,2 +90,3 @@ issuerBaseURL: 'https://test.auth0.com',

const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -101,2 +106,3 @@ issuerBaseURL: 'https://test.auth0.com',

const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -122,2 +128,3 @@ issuerBaseURL: 'https://test.auth0.com',

const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -144,2 +151,68 @@ issuerBaseURL: 'https://test.auth0.com',

describe('app session default configuration', function() {
const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org'
});
it('should set the app session secret', function() {
assert.equal(config.appSessionSecret, '__test_session_secret__');
});
it('should set the session length to 7 days by default', function() {
assert.equal(config.appSessionDuration, 604800);
});
it('should set the session name to "identity" by default', function() {
assert.equal(config.appSessionName, 'identity');
});
it('should set the session cookie attributes to correct defaults', function() {
assert.notExists(config.appSessionCookie.domain);
assert.notExists(config.appSessionCookie.path);
assert.notExists(config.appSessionCookie.secure);
assert.notExists(config.appSessionCookie.sameSite);
assert.equal(config.appSessionCookie.httpOnly, true);
});
});
describe('app session cookie configuration', function() {
const config = getConfig({
appSessionSecret: [ '__test_session_secret_1__', '__test_session_secret_2__' ],
appSessionName: '__test_custom_session_name__',
appSessionDuration: 1234567890,
appSessionCookie: {
domain: '__test_custom_domain__',
path: '__test_custom_path__',
httpOnly: false,
secure: true,
sameSite: 'Lax',
},
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org'
});
it('should set an array of secrets', function() {
assert.equal(config.appSessionSecret.length, 2);
assert.equal(config.appSessionSecret[0], '__test_session_secret_1__');
assert.equal(config.appSessionSecret[1], '__test_session_secret_2__');
});
it('should set the custom session values', function() {
assert.equal(config.appSessionDuration, 1234567890);
assert.equal(config.appSessionName, '__test_custom_session_name__');
});
it('should set the session cookie attributes to custom values', function() {
assert.equal(config.appSessionCookie.domain, '__test_custom_domain__');
assert.equal(config.appSessionCookie.path, '__test_custom_path__');
assert.equal(config.appSessionCookie.httpOnly, false);
assert.equal(config.appSessionCookie.secure, true);
assert.equal(config.appSessionCookie.sameSite, 'Lax');
});
});
});

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

router = expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -25,0 +26,0 @@ baseURL: 'https://example.org',

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

app.get('/session', (req, res) => {
res.json(req.session);
res.json(req.identity);
});
app.post('/session', (req, res) => {
req.session = req.body;
res.json(req.body);
});
app.get('/user', (req, res) => {

@@ -30,0 +25,0 @@ res.json(req.openid.user);

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

const router = expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
idTokenAlg: '__invalid_alg__'

@@ -17,0 +18,0 @@ });

const { assert } = require('chai');
const expressOpenid = require('..');
const validConfiguration = {
appSessionSecret: '__test_session_secret__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
clientID: '__test_client_id__',
};
function getTestConfig(modify) {
return Object.assign({}, validConfiguration, modify);
}
describe('invalid parameters', function() {

@@ -8,2 +19,3 @@ it('should fail when the issuerBaseURL is invalid', function() {

expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
baseURL: 'https://example.org',

@@ -19,2 +31,3 @@ issuerBaseURL: '__invalid_url__',

expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
baseURL: '__invalid_url__',

@@ -30,2 +43,3 @@ issuerBaseURL: 'https://test.auth0.com',

expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
baseURL: 'https://example.org',

@@ -40,2 +54,3 @@ issuerBaseURL: 'https://test.auth0.com',

expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
issuerBaseURL: 'https://test.auth0.com',

@@ -47,5 +62,16 @@ clientID: '__test_client_id__',

it('should fail when the appSessionSecret is not provided', function() {
assert.throws(() => {
expressOpenid.auth({
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
clientID: '__test_client_id__',
});
}, '"appSessionSecret" is required');
});
it('should fail when client secret is not provided and using the response type code in mode query', function() {
assert.throws(() => {
expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
issuerBaseURL: 'https://test.auth0.com',

@@ -63,10 +89,67 @@ baseURL: 'https://example.org',

assert.throws(() => {
expressOpenid.auth({
issuerBaseURL: 'http://foobar.auth0.com',
baseURL: 'http://foobar.com',
clientID: 'asdas',
idTokenAlg: 'HS256'
});
expressOpenid.auth(getTestConfig({idTokenAlg: 'HS256'}));
}, '"clientSecret" is required for ID tokens with HS algorithms');
});
it('should fail when app session length is not an integer', function() {
assert.throws(() => {
expressOpenid.auth(getTestConfig({appSessionDuration: 3.14159}));
}, '"appSessionDuration" must be an integer');
});
it('should fail when app session secret is invalid', function() {
assert.throws(() => {
expressOpenid.auth(getTestConfig({appSessionSecret: {key: '__test_session_secret__'}}));
}, '"appSessionSecret" must be a string');
});
it('should fail when app session cookie httpOnly is not a boolean', function() {
assert.throws(() => {
expressOpenid.auth(getTestConfig({
appSessionCookie: {
httpOnly: '__invalid_httponly__'
}
}));
}, '"httpOnly" must be a boolean');
});
it('should fail when app session cookie secure is not a boolean', function() {
assert.throws(() => {
expressOpenid.auth(getTestConfig({
appSessionCookie: {
secure: '__invalid_secure__'
}
}));
}, '"secure" must be a boolean');
});
it('should fail when app session cookie sameSite is invalid', function() {
assert.throws(() => {
expressOpenid.auth(getTestConfig({
appSessionCookie: {
sameSite: '__invalid_samesite__'
}
}));
}, '"sameSite" must be one of [Lax, Strict, None]');
});
it('should fail when app session cookie domain is invalid', function() {
assert.throws(() => {
expressOpenid.auth(getTestConfig({
appSessionCookie: {
domain: false
}
}));
}, '"domain" must be a string');
});
it('should fail when app session cookie sameSite is an invalid value', function() {
assert.throws(() => {
expressOpenid.auth(getTestConfig({
appSessionCookie: {
path: 123
}
}));
}, '"path" must be a string');
});
});

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

const router = expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -14,0 +15,0 @@ baseURL: 'https://example.org',

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

const router = expressOpenid.auth({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -14,0 +15,0 @@ baseURL: 'https://example.org',

@@ -24,2 +24,3 @@ const { assert } = require('chai');

issuerBaseURL: 'https://test.auth0.com',
appSessionSecret: '__test_session_secret__',
required: false

@@ -63,2 +64,3 @@ });

issuerBaseURL: 'https://test.auth0.com',
appSessionSecret: '__test_session_secret__',
required: false

@@ -65,0 +67,0 @@ });

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

const router = auth({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -56,2 +57,3 @@ baseURL: 'https://example.org',

const router = auth({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',

@@ -58,0 +60,0 @@ baseURL: 'https://example.org',

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