hapi-passport-saml
A Hapi plugin that wraps passport-saml for SAML SSO (as SP)
Install
npm install hapi-passport-saml
Configuring Hapi server example
Uses Feide.no as IdP, read passport-saml for how to use options
var Hapi = require('hapi');
var Boom = require('boom');
var debug = require('debug')('api:main');
var fs = require('fs');
// Create a server with a host and port
var server = module.exports = new Hapi.Server();
server.connection({
host: 'localhost',
port: 8080
});
var decryptionCert = fs.readFileSync(__dirname + '/../../localhost-saml.crt').toString();
// Dependencies
server.app = {
decryptionCert: decryptionCert
};
var controllers = [
{
register: require('../controllers/saml')
}
];
var serverPlugins = [
{
register: require('hapi-passport-saml'),
options: {
callbackUrl: 'http://localhost/api/sso/v1/assert',
host: 'localhost',
protocol: 'http',
entryPoint: 'https://openidp.feide.no/simplesaml/saml2/idp/SSOService.php',
decryptionPvk: fs.readFileSync(__dirname + '/localhost-saml.pem').toString(),
cert: fs.readFileSync(__dirname + '/feide-idp.crt').toString(),
issuer: 'my-issuer-sp-saml'
}
}
];
server.register(serverPlugins, function(err) {
server.register(controllers,
{
routes: {
prefix: '/api'
}
}, function() {
if (!module.parent) {
server.start(function () {
console.log('Server started at port ' + server.info.port);
});
}
});
});
Hapi routes
var debug = require('debug')('saml');
var samlCtrl = require('./v1');
exports.register = function(plugin, options, next) {
plugin.route({
method: 'GET',
path: '/sso/v1/metadata.xml',
handler: samlCtrl.metadata,
config: {
description: 'metadata',
notes: 'metadata',
tags: ['api']
}
});
plugin.route({
method: 'GET',
path: '/sso/v1/login',
handler: samlCtrl.login,
config: {
description: 'login',
notes: 'login',
tags: ['api']
}
});
plugin.route({
method: 'POST',
path: '/sso/v1/assert',
handler: samlCtrl.assert,
config: {
description: 'assert',
notes: 'assert',
tags: ['api']
}
});
plugin.route({
method: 'GET',
path: '/sso/v1/logout',
handler: samlCtrl.logout,
config: {
description: 'logout',
notes: 'logout',
tags: ['api']
}
});
next();
};
exports.register.attributes = {
pkg: require('./package.json')
};
Hapi controller
var debug = require('debug')('saml:ctrl');
/**
* Endpoint to retrieve metadata
* @function
* @param {Object} request - A Hapi Request
* @param {Object} reply - A Hapi Reply
*/
exports.metadata = function (request, reply) {
var saml = request.server.plugins['hapi-passport-saml'].instance;
return reply(saml.generateServiceProviderMetadata(request.server.app.decryptionCert))
.type('application/xml');
};
/**
* Login
* @function
* @param {Object} request - A Hapi Request
* @param {Object} reply - A Hapi Reply
*/
exports.login = function (request, reply) {
var saml = request.server.plugins['hapi-passport-saml'].instance;
saml.getAuthorizeUrl({
headers: request.headers,
body: request.payload,
query: request.query
}, function(err, loginUrl) {
if (err !== null)
return reply.code(500);
return reply.redirect(loginUrl);
});
};
/**
* Assert endpoint for when login completes
* @function
* @param {Object} request - A Hapi Request
* @param {Object} reply - A Hapi Reply
*/
exports.assert = function (request, reply) {
var saml = request.server.plugins['hapi-passport-saml'].instance;
var options = {
request_body: request.payload
};
if (request.payload.SAMLRequest) {
// Implement your SAMLRequest handling here
debug(request.payload);
return reply(500);
}
if (request.payload.SAMLResponse) {
// Handles SP use cases, e.g. IdP is external and SP is Hapi
saml.validatePostResponse(request.payload, function(err, profile) {
debug(err);
debug(profile);
if (err !== null)
return reply.code(500);
// Save name_id and session_index for logout
// Note: In practice these should be saved in the user session, not globally.
var name_id = profile.nameID;
var session_index = profile.sessionIndex;
request.server.app.name_id = name_id;
request.server.app.session_index = session_index;
return reply("Hello " + profile.nameID+"!");
});
}
};
/**
* Logout
* @function
* @param {Object} request - A Hapi Request
* @param {Object} reply - A Hapi Reply
*/
exports.logout = function (request, reply) {
var options = {
name_id: request.server.app.name_id,
session_index: request.server.app.session_index
};
var saml = request.server.plugins['hapi-passport-saml'].instance;
saml.getLogoutUrl(request, function(err, url) {
if (err !== null)
return reply.code(500);
return reply.redirect(url);
});
};
References, Ideas and Based from
License
MIT