diff --git a/README.md b/README.md index 1aca6d23..cd3de504 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ passport.use(new MultiSamlStrategy( * `acceptedClockSkewMs`: Time in milliseconds of skew that is acceptable between client and server when checking `OnBefore` and `NotOnOrAfter` assertion condition validity timestamps. Setting to `-1` will disable checking these conditions entirely. Default is `0`. * `attributeConsumingServiceIndex`: optional `AttributeConsumingServiceIndex` attribute to add to AuthnRequest to instruct the IDP which attribute set to attach to the response ([link](http://blog.aniljohn.com/2014/01/data-minimization-front-channel-saml-attribute-requests.html)) * `disableRequestedAuthnContext`: if truthy, do not request a specific authentication context. This is [known to help when authenticating against Active Directory](https://github.com/bergie/passport-saml/issues/226) (AD FS) servers. - * `authnContext`: if truthy, name identifier format to request auth context (default: `urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport`) + * `authnContext`: if truthy, name identifier format to request auth context (default: `urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport`); array of values is also supported * `forceAuthn`: if set to true, the initial SAML request from the service provider specifies that the IdP should force re-authentication of the user, even if they possess a valid session. * `providerName`: optional human-readable name of the requester for use by the presenter's user agent or the identity provider * `skipRequestCompression`: if set to true, the SAML request from the service provider won't be compressed. diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 674b4c54..9ffa31b7 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -44,6 +44,10 @@ SAML.prototype.initialize = function (options) { options.authnContext = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"; } + if (!Array.isArray(options.authnContext)) { + options.authnContext = [options.authnContext]; + } + if (!options.acceptedClockSkewMs) { // default to no skew options.acceptedClockSkewMs = 0; @@ -181,13 +185,18 @@ SAML.prototype.generateAuthorizeRequest = function (req, isPassive, callback) { } if (!self.options.disableRequestedAuthnContext) { + var authnContextClassRefs = []; + self.options.authnContext.forEach(function(value) { + authnContextClassRefs.push({ + '@xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion', + '#text': value + }); + }); + request['samlp:AuthnRequest']['samlp:RequestedAuthnContext'] = { '@xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', '@Comparison': 'exact', - 'saml:AuthnContextClassRef': { - '@xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion', - '#text': self.options.authnContext - } + 'saml:AuthnContextClassRef': authnContextClassRefs }; } diff --git a/test/tests.js b/test/tests.js index ce715087..bb0a0cc6 100644 --- a/test/tests.js +++ b/test/tests.js @@ -392,6 +392,42 @@ describe( 'passport-saml /', function() { [ { _: 'myAuthnContext', '$': { 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion' } } ] } ] } } }, + { name: "Config with multiple AuthnContext", + config: { + issuer: 'http://exampleSp.com/saml', + identifierFormat: 'alternateIdentifier', + passive: true, + attributeConsumingServiceIndex: 123, + authnContext: ['myAuthnContext', 'myAuthnContext2'] + }, + result: { + 'samlp:AuthnRequest': + { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + Version: '2.0', + ProtocolBinding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + AssertionConsumerServiceURL: 'http://localhost:3033/login', + AttributeConsumingServiceIndex: '123', + Destination: 'https://wwwexampleIdp.com/saml', + IsPassive: 'true'}, + 'saml:Issuer': + [ { _: 'http://exampleSp.com/saml', + '$': { 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion' } } ], + 'samlp:NameIDPolicy': + [ { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + Format: 'alternateIdentifier', + AllowCreate: 'true' } } ], + 'samlp:RequestedAuthnContext': + [ { '$': + { 'xmlns:samlp': 'urn:oasis:names:tc:SAML:2.0:protocol', + Comparison: 'exact' }, + 'saml:AuthnContextClassRef': + [ { _: 'myAuthnContext', + '$': { 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion' } }, + { _: 'myAuthnContext2', + '$': { 'xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion' } } ] } ] } } + }, { name: "Config with ProviderName", config: { issuer: 'http://exampleSp.com/saml',