-
-
Save joaodrp/8b526adc50f2202b974d71b2a13cd614 to your computer and use it in GitHub Desktop.
SAML implementation for feathers.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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 | |
}); | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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