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

Respect auth mode for redirects #186

Merged
merged 5 commits into from
May 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Empty file modified .travis.yml
100755 → 100644
Empty file.
Empty file modified LICENSE
100755 → 100644
Empty file.
9 changes: 3 additions & 6 deletions README.md
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,13 @@ The `'cookie`' scheme takes the following options:
- `isSecure` - if `false`, the cookie is allowed to be transmitted over insecure connections which
exposes it to attacks. Defaults to `true`.
- `isHttpOnly` - if `false`, the cookie will not include the 'HttpOnly' flag. Defaults to `true`.
- `redirectTo` - optional login URI or function `function(request)` that returns a URI to redirect unauthenticated requests to. Note that using
`redirectTo` with authentication mode `'try'` will cause the protected endpoint to always
redirect, voiding `'try'` mode. To set an individual route to use or disable redirections, use
the route `plugins` config (`{ options: { plugins: { 'hapi-auth-cookie': { redirectTo: false } } } }`).
- `redirectTo` - optional login URI or function `function(request)` that returns a URI to redirect unauthenticated requests to. Note that it will only
trigger when the authentication mode is `'required'`. To enable or disable redirections for a specific route,
set the route `plugins` config (`{ options: { plugins: { 'hapi-auth-cookie': { redirectTo: false } } } }`).
Defaults to no redirection.
- `appendNext` - if `true` and `redirectTo` is `true`, appends the current request path to the
query component of the `redirectTo` URI using the parameter name `'next'`. Set to a string to use
a different parameter name. Defaults to `false`.
- `redirectOnTry` - if `false` and route authentication mode is `'try'`, authentication errors will
not trigger a redirection. Defaults to `true`;
- `async validateFunc` - an optional session validation function used to validate the content of the
session cookie on each request. Used to verify that the internal session state is still valid
(e.g. user account still exists). The function has the signature `function(request, session)`
Expand Down
9 changes: 1 addition & 8 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ internals.schema = Joi.object({
isHttpOnly: Joi.boolean().default(true),
redirectTo: Joi.alternatives(Joi.string(), Joi.func()).allow(false),
appendNext: Joi.alternatives(Joi.string(), Joi.boolean()).default(false),
redirectOnTry: Joi.boolean().default(true),
validateFunc: Joi.func(),
requestDecoratorName: Joi.string().default('cookieAuth'),
ignoreIfDecorated: Joi.boolean().default(true)
Expand Down Expand Up @@ -197,12 +196,6 @@ internals.implementation = (server, options) => {

const unauthenticated = (err, result) => {

if (settings.redirectOnTry === false && // Defaults to true
request.auth.mode === 'try') {

return h.unauthenticated(err);
}

let redirectTo = settings.redirectTo;
if (request.route.settings.plugins['hapi-auth-cookie'] &&
request.route.settings.plugins['hapi-auth-cookie'].redirectTo !== undefined) {
Expand All @@ -212,7 +205,7 @@ internals.implementation = (server, options) => {

let uri = (typeof (redirectTo) === 'function') ? redirectTo(request) : redirectTo;

if (!uri) {
if (!uri || request.auth.mode !== 'required') {
return h.unauthenticated(err);
}

Expand Down
89 changes: 54 additions & 35 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -403,9 +403,9 @@ describe('scheme', () => {
const schema = () => {

return {
authenticate: (request, reply) => {
authenticate: (request, h) => {

return reply.authenticated({ credentials: { user: 'bogus-user' } });
return h.authenticated({ credentials: { user: 'bogus-user' } });
}
};
};
Expand All @@ -431,10 +431,10 @@ describe('scheme', () => {
server.route({
method: 'GET', path: '/login/{user}',
config: {
handler: function (request, reply) {
handler: function (request, h) {

request.cookieAuth.set({ user: request.params.user });
return reply.response(request.params.user);
return h.response(request.params.user);
}
}
});
Expand All @@ -443,9 +443,9 @@ describe('scheme', () => {
method: 'GET', path: '/resource',
config: {
auth: { mode: 'required', strategies: ['first', 'second'] },
handler: function (request, reply) {
handler: function (request, h) {

return reply.response('valid-resource');
return h.response('valid-resource');
}
}
});
Expand Down Expand Up @@ -1241,39 +1241,33 @@ describe('scheme', () => {
expect(res.statusCode).to.equal(401);
});

it('skips when redirectOnTry is false in try mode', async () => {
it('sends to login page (uri with query)', async () => {

const server = Hapi.server();
await server.register(require('../'));

server.auth.strategy('default', 'cookie', {
password: 'password-should-be-32-characters',
ttl: 60 * 1000,
redirectOnTry: false,
redirectTo: 'http://example.com/login',
redirectTo: 'http://example.com/login?mode=1',
appendNext: true
});
server.auth.default({
mode: 'try',
strategy: 'default'
});
server.auth.default('default');

server.route({
method: 'GET',
path: '/',
handler: function (request, h) {
method: 'GET', path: '/', handler: function () {

return h.response(request.auth.isAuthenticated);
return 'never';
}
});

const res = await server.inject('/');

expect(res.statusCode).to.equal(200);
expect(res.result).to.equal(false);
expect(res.statusCode).to.equal(302);
expect(res.headers.location).to.equal('http://example.com/login?mode=1&next=%2F');
});

it('sends to login page (uri with query)', async () => {
it('sends to login page and does not append the next query when appendNext is false', async () => {

const server = Hapi.server();
await server.register(require('../'));
Expand All @@ -1282,24 +1276,24 @@ describe('scheme', () => {
password: 'password-should-be-32-characters',
ttl: 60 * 1000,
redirectTo: 'http://example.com/login?mode=1',
appendNext: true
appendNext: false
});
server.auth.default('default');

server.route({
method: 'GET', path: '/', handler: function (request, reply) {
method: 'GET', path: '/', handler: function (request, h) {

return reply('never');
return h.response('never');
}
});

const res = await server.inject('/');

expect(res.statusCode).to.equal(302);
expect(res.headers.location).to.equal('http://example.com/login?mode=1&next=%2F');
expect(res.headers.location).to.equal('http://example.com/login?mode=1');
});

it('sends to login page and does not append the next query when appendNext is false', async () => {
it('appends the custom query when appendNext is string', async () => {

const server = Hapi.server();
await server.register(require('../'));
Expand All @@ -1308,7 +1302,7 @@ describe('scheme', () => {
password: 'password-should-be-32-characters',
ttl: 60 * 1000,
redirectTo: 'http://example.com/login?mode=1',
appendNext: false
appendNext: 'done'
});
server.auth.default('default');

Expand All @@ -1322,36 +1316,36 @@ describe('scheme', () => {
const res = await server.inject('/');

expect(res.statusCode).to.equal(302);
expect(res.headers.location).to.equal('http://example.com/login?mode=1');
expect(res.headers.location).to.equal('http://example.com/login?mode=1&done=%2F');
});

it('appends the custom query when appendNext is string', async () => {
it('redirect for required mode', async () => {

const server = Hapi.server();
await server.register(require('../'));

server.auth.strategy('default', 'cookie', {
password: 'password-should-be-32-characters',
ttl: 60 * 1000,
redirectTo: 'http://example.com/login?mode=1',
appendNext: 'done'
redirectTo: 'http://example.com/login',
appendNext: true
});
server.auth.default('default');

server.route({
method: 'GET', path: '/', handler: function (request, h) {
method: 'GET', path: '/', config: { auth: { mode: 'required' } }, handler: function (request, h) {

return h.response('never');
return h.response('required');
}
});

const res = await server.inject('/');

expect(res.statusCode).to.equal(302);
expect(res.headers.location).to.equal('http://example.com/login?mode=1&done=%2F');
expect(res.headers.location).to.equal('http://example.com/login?next=%2F');
});

it('redirect on try', async () => {
it('skips redirect for try mode', async () => {

const server = Hapi.server();
await server.register(require('../'));
Expand All @@ -1373,7 +1367,32 @@ describe('scheme', () => {

const res = await server.inject('/');

expect(res.statusCode).to.equal(302);
expect(res.statusCode).to.equal(200);
});

it('skips redirect for optional mode', async () => {

const server = Hapi.server();
await server.register(require('../'));

server.auth.strategy('default', 'cookie', {
password: 'password-should-be-32-characters',
ttl: 60 * 1000,
redirectTo: 'http://example.com/login',
appendNext: true
});
server.auth.default('default');

server.route({
method: 'GET', path: '/', config: { auth: { mode: 'optional' } }, handler: function (request, h) {

return h.response('optional');
}
});

const res = await server.inject('/');

expect(res.statusCode).to.equal(200);
});
});

Expand Down