Skip to content

Commit

Permalink
Additional allowed cookieOptions
Browse files Browse the repository at this point in the history
Added `expires` allowed as null or 0 to set an ephemeral cookie (documented
method of setting duration to 0 did not work). Also added `maxAge` as an
additional way to set this cookie duration. If both are omitted, cookie expires
defaults to appSessionDuration.
  • Loading branch information
joshcanhelp committed Jan 17, 2020
1 parent b9feb4a commit d2ba7ee
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 59 deletions.
4 changes: 2 additions & 2 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ If you are using a response type that includes `code` (typically combined with a

Additional configuration keys that can be passed to `auth()` on initialization:

- **`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.
- **`appSessionCookie`** - Object defining application session cookie attributes. Allowed keys are `domain`, `ephemeral`, `httpOnly`, `maxAge`, `path`, `secure`, and `sameSite`. Defaults are `true` for `httpOnly`, `Lax` for `sameSite`, and `false` for `ephemeral`. See the [Express Response documentation](https://expressjs.com/en/api.html#res.cookie) for more information on how to use these properties.
- **`appSessionDuration`** - Integer value, in seconds, for application session duration. 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.
Expand Down
36 changes: 35 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import { AuthorizationParameters, TokenSet, UserinfoResponse } from 'openid-client';
import { Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from 'express';

/**
* Configuration parameters passed to the auth() middleware.
*/
interface ConfigParams {
/**
* Object defining application session cookie attributes.
Expand Down Expand Up @@ -131,12 +134,43 @@ interface ConfigParams {
routes?: boolean;
}

/**
* Configuration parameters used in appSessionCookie.
*
* @see https://expressjs.com/en/api.html#res.cookie
*/
interface SessionCookieConfigParams {
/**
* Domain name for the cookie.
*/
domain?: string;

/**
* Set to true to use an ephemeral cookie.
* Default is false which will use appSessionDuration.
*/
ephemeral?: boolean;

/**
* Flags the cookie to be accessible only by the web server.
* Set to `true` by default in lib/config.
*/
httpOnly?: boolean;

/**
* Path for the cookie.
*/
path?: string;
sameSite?: string;

/**
* Marks the cookie to be used with HTTPS only.
*/
secure?: boolean;

/**
* Value of the “SameSite” Set-Cookie attribute.
*/
sameSite?: string;
}

export function auth(params?: ConfigParams): RequestHandler;
Expand Down
6 changes: 4 additions & 2 deletions lib/appSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ module.exports = ({ name, secret, duration, cookieOptions = {} }) => {

if (req[name] && Object.keys(req[name]).length > 0) {
const value = encrypt(JSON.stringify(req[name]), { iat, uat, exp });
const expires = !duration ? 0 : new Date(exp * 1000);

res.cookie(name, value, {expires, ...cookieOptions});
cookieOptions.expires = cookieOptions.ephemeral ? 0 : new Date(exp * 1000);
delete cookieOptions.ephemeral;

res.cookie(name, value, cookieOptions);
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ const authorizationParamsSchema = Joi.object().keys({

const appSessionCookieSchema = Joi.object().keys({
domain: Joi.string().optional(),
ephemeral: Joi.boolean().optional().default(false),
httpOnly: Joi.boolean().optional(),
path: Joi.string().optional(),
maxAge: Joi.number().integer().optional(),
sameSite: Joi.string().valid(['Lax', 'Strict', 'None']).optional(),
secure: Joi.boolean().optional()
}).unknown(false);
Expand Down
80 changes: 26 additions & 54 deletions test/config.tests.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
const { assert } = require('chai');
const { get: getConfig } = require('../lib/config');

const defaultConfig = {
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org'
};

describe('config', function() {
describe('simple case', function() {
const config = getConfig({
appSessionSecret: false,
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
});
const config = getConfig(defaultConfig);

it('should default to response_type=id_token', function() {
assert.equal(config.authorizationParams.response_type, 'id_token');
Expand All @@ -28,16 +30,13 @@ describe('config', function() {
});

describe('when authorizationParams is response_type=x', function() {
const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',
const customConfig = Object.assign({}, defaultConfig, {
clientSecret: '__test_client_secret__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
authorizationParams: {
response_type: 'code'
}
});
const config = getConfig(customConfig);

it('should default to response_type=id_token', function() {
assert.equal(config.authorizationParams.response_type, 'code');
Expand All @@ -53,13 +52,7 @@ describe('config', function() {
});

describe('with auth0Logout', function() {
const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
auth0Logout: true
});
const config = getConfig(Object.assign({}, defaultConfig, {auth0Logout: true}));

it('should set idpLogout to true', function() {
assert.equal(config.auth0Logout, true);
Expand All @@ -68,12 +61,7 @@ describe('config', function() {
});

describe('without auth0Logout nor idpLogout', function() {
const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
});
const config = getConfig(defaultConfig);

it('should set both to false', function() {
assert.equal(config.auth0Logout, false);
Expand All @@ -82,13 +70,7 @@ describe('config', function() {
});

describe('with idpLogout', function() {
const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
idpLogout: true
});
const config = getConfig(Object.assign({}, defaultConfig, {idpLogout: true}));

it('should set both to false', function() {
assert.equal(config.auth0Logout, false);
Expand All @@ -97,12 +79,7 @@ describe('config', function() {
});

describe('default auth paths', function() {
const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
});
const config = getConfig(defaultConfig);

it('should set the default callback path', function() {
assert.equal(config.redirectUriPath, '/callback');
Expand All @@ -118,15 +95,12 @@ describe('config', function() {
});

describe('custom auth paths', function() {
const config = getConfig({
appSessionSecret: '__test_session_secret__',
clientID: '__test_client_id__',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
const customConfig = Object.assign({}, defaultConfig, {
redirectUriPath: '/custom-callback',
loginPath: '/custom-login',
logoutPath: '/custom-logout',
});
const config = getConfig(customConfig);

it('should accept the custom callback path', function() {
assert.equal(config.redirectUriPath, '/custom-callback');
Expand All @@ -142,12 +116,7 @@ describe('config', function() {
});

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'
});
const config = getConfig(defaultConfig);

it('should set the app session secret', function() {
assert.equal(config.appSessionSecret, '__test_session_secret__');
Expand All @@ -171,40 +140,43 @@ describe('config', function() {
});

describe('app session cookie configuration', function() {
const config = getConfig({
const customConfig = Object.assign({}, defaultConfig, {
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__',
ephemeral: true,
httpOnly: false,
maxAge: 999,
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() {
const config = getConfig(customConfig);
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() {
const config = getConfig(customConfig);
assert.equal(config.appSessionDuration, 1234567890);
assert.equal(config.appSessionName, '__test_custom_session_name__');
});

it('should set the session cookie attributes to custom values', function() {
const config = getConfig(customConfig);
assert.equal(config.appSessionCookie.domain, '__test_custom_domain__');
assert.equal(config.appSessionCookie.path, '__test_custom_path__');
assert.equal(config.appSessionCookie.ephemeral, true);
assert.equal(config.appSessionCookie.httpOnly, false);
assert.equal(config.appSessionCookie.maxAge, 999);
assert.equal(config.appSessionCookie.secure, true);
assert.equal(config.appSessionCookie.sameSite, 'Lax');
});
});

});

0 comments on commit d2ba7ee

Please sign in to comment.