Skip to content

Instantly share code, notes, and snippets.

@joaodrp
Forked from petermikitsh/client_code.js
Created October 16, 2017 21:30
Show Gist options
  • Save joaodrp/8b526adc50f2202b974d71b2a13cd614 to your computer and use it in GitHub Desktop.
Save joaodrp/8b526adc50f2202b974d71b2a13cd614 to your computer and use it in GitHub Desktop.
SAML implementation for feathers.js
/* SAML Authentication Flow
* - Open GET /SSO/SAML2 in an iframe
* - this will redirect to the identity provider ("IdP")
* - The user will insert their credentials in the IdP's website
* - The IdP will redirect to POST /SSO/SAML2
* - The response is validated
* - A user is created (should check if it exists first)
* - Set the JWT cookie
* - Send HTML response to instruct parent window to close the iframe
*/
// this is a slightly abridged version of my implementation (split across various react/redux files)
import authentication from 'feathers-authentication/client'
import feathers from 'feathers/client'
import hooks from 'feathers-hooks'
import io from 'socket.io-client'
import socketio from 'feathers-socketio/client'
var client = feathers()
.configure(hooks())
.configure(socketio(io(window.location.origin)))
.configure(authentication({storage: window.localStorage}));
client
.authenticate()
.then(function (user) {
dispatch({
type: 'SET_USER',
value: user
});
})
'use strict';
var config = require('../config');
var fs = require('fs');
var saml = require('passport-saml');
module.exports = function() {
const app = this;
var strategy = new saml.Strategy({
callbackUrl: 'http://127.0.0.1:3000/SSO/SAML2',
entryPoint: 'https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO',
issuer: 'http://127.0.0.1:3000/shibboleth',
identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
decryptionPvk: fs.readFileSync(config.privateKeyPath, 'utf8'),
privateCert: fs.readFileSync(config.privateKeyPath, 'utf8'),
cert: `{redacted}`,
signatureAlgorithm: 'sha1'
}, function (profile, done) {
return done(null, profile);
});
app.get('/SSO/SAML2', function (req, res) {
strategy._saml.getAuthorizeUrl(req, function (err, result) {
// redirect to the identity provider
res.redirect(err ? '/?loginFailed=true' : result);
});
});
app.post('/SSO/SAML2', function (req, res) {
// decrypt the response with my private key
// validate the response's signature
strategy._saml.validatePostResponse(req.body, function (err, result) {
if (err) {
res.redirect('/?loginFailed=true');
} else {
app
.service('/users')
.create({email: result['urn:oid:1.3.6.1.4.1.5923.1.1.1.6']})
.then(user => {
app
.service('auth/token')
.create(user)
.then(authorization => {
// set cookie
res.cookie('feathers-jwt', authorization.token);
// this is an iframe in a webpage...
// so I instruct the parent to close the iframe
res.end(`
<html>
<body>
<script>
window.parent.closeLogin();
</script>
</body>
</html>`);
})
.catch(error => {
console.log(error);
res.redirect('/?loginFailed=true')
});
})
.catch(error => {
console.log(error);
res.redirect('/?loginFailed=true')
});
}
});
})
app.get('/SSO/SAML2/Metadata', function (req, res) {
var cert = fs.readFileSync(config.publicKeyPath, 'utf8');
res.writeHead(200, {'Content-Type': 'application/xml'});
res.end(strategy._saml.generateServiceProviderMetadata(cert), 'utf-8');
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment