Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix post logout redirect, add config for default #40

Merged
merged 23 commits into from
Jan 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b66cd83
add new ways to specify post logout redirect url
balazsorban44 Nov 26, 2019
19cb830
destroy session after calling endSessionUrl
balazsorban44 Nov 26, 2019
0c2401d
whitespace formatting
balazsorban44 Nov 26, 2019
6193f07
changes after code review
balazsorban44 Dec 6, 2019
fd5070a
update logout tests to cover new cases
balazsorban44 Dec 6, 2019
4063ab4
add postLogoutRedirectUri to optional keys
balazsorban44 Dec 6, 2019
46eb8a9
add postLogoutRedirectParams to ConfigParams
balazsorban44 Dec 6, 2019
7162c46
Merge branch 'master' into 'post-logout-redirect'
balazsorban44 Jan 13, 2020
a874d33
make postLogoutRedirectUri relative
balazsorban44 Jan 13, 2020
0ca9e8e
add tests for postRedirectUrl and returnTo query string
balazsorban44 Jan 13, 2020
c907f75
Merge branch 'master' into post-logout-redirect
balazsorban44 Jan 14, 2020
1b8b4c2
Merge branch 'master' into post-logout-redirect
balazsorban44 Jan 16, 2020
2886790
accept relative path and full URI in postLogoutRedirectUri
balazsorban44 Jan 16, 2020
6454f80
add postLogoutRedirectUri tests, relative path & full URI
balazsorban44 Jan 16, 2020
0ddc8b4
update description of postLogoutRedirectUri in API.md
balazsorban44 Jan 16, 2020
678d075
add postLogoutRedirectUris to docs and typings
balazsorban44 Jan 16, 2020
3084c01
require returnTo URIs to be present in postLogoutRedirectUris
balazsorban44 Jan 16, 2020
e4b6c07
add tests for postLogoutRedirectUris
balazsorban44 Jan 16, 2020
903a23b
Revert adding postLogoutRedirectUris
balazsorban44 Jan 23, 2020
5e9b7f3
Merge branch 'master' into post-logout-redirect
balazsorban44 Jan 23, 2020
36c13a7
changes after code review
balazsorban44 Jan 23, 2020
2bae74c
fix logout tests
balazsorban44 Jan 23, 2020
586feaa
Merge branch 'master' into post-logout-redirect
balazsorban44 Jan 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"indent": [
"error",
2,
{ "SwitchCase": 1 }
{ "SwitchCase": 1 }
],
"linebreak-style": [
"error",
Expand Down
1 change: 1 addition & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Additional configuration keys that can be passed to `auth()` on initialization:
- **`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`.
- **`postLogoutRedirectUri`** - Either a relative path to the application or a valid URI to an external domain. The user will be redirected to this after a logout has been performed. Default is `baseUrl`.
- **`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`.

Expand Down
7 changes: 7 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ interface ConfigParams {
*/
redirectUriPath?: string;

/**
* Either a relative path to the application
* or a valid URI to an external domain.
* The user will be redirected to this after a logout has been performed.
*/
postLogoutRedirectUri?: string;

/**
* Require authentication for all routes.
*/
Expand Down
2 changes: 1 addition & 1 deletion lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const paramsSchema = Joi.object().keys({
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),
postLogoutRedirectUri: Joi.string().uri({allowRelative: true}).optional().default('/')
});

function buildAuthorizeParams(authorizationParams) {
Expand Down Expand Up @@ -119,7 +120,6 @@ module.exports.get = function(params) {
}

config = paramsValidation.value;

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

Expand Down
21 changes: 16 additions & 5 deletions lib/context.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const cb = require('cb');
const url = require('url');
const urlJoin = require('url-join');
const transient = require('./transientHandler');
const { get: getClient } = require('./client');
Expand Down Expand Up @@ -88,24 +89,34 @@ class ResponseContext {
const next = cb(this._next).once();
const req = this._req;
const res = this._res;
const returnURL = params.returnTo || this._config.baseURL;

let returnURL = params.returnTo || req.query.returnTo || this._config.postLogoutRedirectUri;

if (url.parse(returnURL).host === null) {
returnURL = urlJoin(this._config.baseURL, returnURL);
}

if (!req.isAuthenticated()) {
return res.redirect(returnURL);
}

req[this._config.appSessionName] = undefined;

if (!this._config.idpLogout) {
return res.redirect(returnURL);
}

const client = this._req.openid.client;
try {
const client = this._req.openid.client;
const url = client.endSessionUrl({
returnURL = client.endSessionUrl({
post_logout_redirect_uri: returnURL,
id_token_hint: req.openid.tokens,
});
res.redirect(url);
} catch(err) {
next(err);
return next(err);
}

res.redirect(returnURL);
}

}
Expand Down
87 changes: 78 additions & 9 deletions test/logout.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('logout route', function() {

it('should redirect to the base url', function() {
assert.equal(logoutResponse.statusCode, 302);
assert.equal(logoutResponse.headers.location, 'https://example.org');
assert.equal(logoutResponse.headers.location, 'https://example.org/');
joshcanhelp marked this conversation as resolved.
Show resolved Hide resolved
});
});

Expand Down Expand Up @@ -83,15 +83,84 @@ describe('logout route', function() {

it('should redirect to the base url', function() {
assert.equal(logoutResponse.statusCode, 302);
const parsedUrl = url.parse(logoutResponse.headers.location, true);
assert.deepInclude(parsedUrl, {
protocol: 'https:',
hostname: 'test.auth0.com',
query: { returnTo: 'https://example.org', client_id: '__test_client_id__' },
pathname: '/v2/logout',
});
assert.equal(logoutResponse.headers.location, 'https://example.org/');
});
});


});
describe('should use postLogoutRedirectUri if present', function() {
describe('should allow relative paths, and prepend with baseURL', () => {
let baseUrl;
const jar = request.jar();

before(async function() {
const middleware = auth({
idpLogout: false,
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
appSessionSecret: '__test_session_secret__',
postLogoutRedirectUri: '/after-logout-in-auth-config',
required: false,
});
baseUrl = await server.create(middleware);
await request.post({
uri: '/session',
json: {
openidTokens: {
id_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
}
},
baseUrl, jar
});
});

it('should redirect to postLogoutRedirectUri in auth() config', async function() {
const logoutResponse = await request.get({uri: '/logout', baseUrl, jar, followRedirect: false});
assert.equal(logoutResponse.headers.location, 'https://example.org/after-logout-in-auth-config');
});

it('should redirect to returnTo in logout query', async function() {
const logoutResponse = await request.get({uri: '/logout', qs: {returnTo: '/after-logout-in-logout-query'}, baseUrl, jar, followRedirect: false});
assert.equal(logoutResponse.headers.location, 'https://example.org/after-logout-in-logout-query');
});
});

describe('should allow absolute paths', () => {
let baseUrl;
const jar = request.jar();

before(async function() {
const middleware = auth({
idpLogout: false,
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
appSessionSecret: '__test_session_secret__',
postLogoutRedirectUri: 'https://external-domain.com/after-logout-in-auth-config',
required: false,
});
baseUrl = await server.create(middleware);
await request.post({
uri: '/session',
json: {
openidTokens: {
id_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
}
},
baseUrl, jar
});
});

it('should redirect to postLogoutRedirectUri in auth() config', async function() {
const logoutResponse = await request.get({uri: '/logout', baseUrl, jar, followRedirect: false});
assert.equal(logoutResponse.headers.location, 'https://external-domain.com/after-logout-in-auth-config');
});

it('should redirect to returnTo in logout query', async function() {
const logoutResponse = await request.get({uri: '/logout', qs: {returnTo: 'https://external-domain.com/after-logout-in-logout-query'}, baseUrl, jar, followRedirect: false});
assert.equal(logoutResponse.headers.location, 'https://external-domain.com/after-logout-in-logout-query');
});
});
});
});