- Implement hapi-auth-cookie first.
- Add hapi-passport-saml.
server.auth.strategy('single-sign-on', 'saml', { ... });
server.auth.strategy('session', 'cookie', true, { ... })
- Add SAML login handler: This handler manages SAML login request and redirects to initial secure page (similar to hapi-auth-cookie)
plugin.route({
method: 'GET',
path: '/saml',
config: {
auth: {
mode: 'try',
strategies: ['single-sign-on']
},
plugins: {
'hapi-auth-cookie': {
redirectTo: false
}
},
handler: samlCtrl.saml,
}
});
exports.saml = function (request, reply) {
console.log('is auth', request.auth);
if (request.auth.isAuthenticated) {
return reply().redirect('/welcome');
}
return reply(`Not Authenticated, click to login with cookie <a href="/cookie-creds">cookie</a>.`);
}
- Any secure route must have SAML as the last strategy.
plugin.route({
method: 'GET',
path: '/welcome',
config: {
auth: {
mode: 'required',
strategies: ['session', 'single-sign-on']
},
handler: samlCtrl.main,
}
});
exports.main = function (request, reply) {
console.log('is auth', request.auth);
if (request.state && request.state.session && request.state.session.sid) {
return reply(`<a href="/cookie-logout">Click to logout</a>.`);
}
return reply(`Secure page for user ${request.auth.credentials.username}. <a href="/sso/v1/logout">Click to logout</a>.`);
}
- Add cookie check in hapi-auth-cookie
validateFunc
server.register(serverPlugins, function (err) {
server.auth.strategy('single-sign-on', 'saml', schemeOpts);
server.auth.strategy('session', 'cookie', true, {
password: 'password-should-be-32-characters',
cookie: 'session',
redirectTo: '/login',
isSecure: false,
isSameSite: false,
isHttpOnly: false,
validateFunc: function (request, session, callback) {
if (session && session.username) {
// saml
return callback(null, true)
}
if (session && session.sid) {
// cookie
return callback(null, true)
}
// 401
return callback(true);
}
- Because both strategies shared the same authenticated credentials, both cookie state needs to merge. For that, use hapi-passport-saml assert hook. This is sample code, you can use Object.assign or similar.
assertHooks: {
onResponse: (profile, req) => {
console.log(profile)
const username = profile['urn:oid:2.5.4.4'];
const samlCreds = { ...profile, username }; // or append existing cookie
// Save SAML props for cookie creds
req.cookieAuth.set(samlCreds)
return samlCreds
}
Known Issues
Logout
Use ngrok to test SAML Logout, SAML requires callbacks to a public facing url.
Incomplete login
If either strategy login sequence are not completed, you might get 401 Unauthorized or 200 JSON with an error message. In that case use try
mode and handle the fallback.
hapi-auth-cookie redirect ignore setting
plugin.route({
method: 'GET',
path: '/saml',
config: {
auth: {
mode: 'try',
strategies: ['single-sign-on']
},
plugins: {
'hapi-auth-cookie': {
redirectTo: false
}
},
handler: samlCtrl.saml,
}
});
// Get cookie creds
plugin.route({
method: 'GET',
path: '/cookie-creds',
config: {
auth: {
mode: 'try',
strategies: ['session']
},
plugins: {
'hapi-auth-cookie': {
redirectTo: false
}
},
handler: samlCtrl.getCreds,
}
});