GithubHelp home page GithubHelp logo

passport-saml's Introduction

Node SAML

Build Status npm version code style: prettier codecov DeepScan grade

NPM

This is a SAML 2.0 authentication provider for Node.js.

Sponsors

We gratefully acknowledge support from our sponsors:

If your company benefits from node-saml being secure and up-to-date, consider asking them to sponsor the project at $25/month. See the Github Sponsors page for more sponsorship levels. It's easy to do, appearing as another line-item on the Github bill they already have.

Installation

npm install @node-saml/node-saml

Usage

The examples utilize the Feide OpenIdp identity provider. You need an account there to log in with this. You also need to register your site as a service provider.

Configure strategy

The SAML identity provider will redirect you to the URL provided by the path configuration.

const { SAML } = require("@node-saml/node-saml");

const options = {};
const saml = new SAML(options);

Config parameter details

  • Core

  • callbackUrl: full callbackUrl

  • entryPoint: identity provider entrypoint (is required to be spec-compliant when the request is signed)

  • issuer: issuer string to supply to identity provider

  • audience: expected saml response Audience, defaults to value of Issuer (if false, Audience won't be verified)

  • idpCert: the IDP's public signing certificate used to validate the signatures of the incoming SAML Responses, see Security and signatures

  • privateKey: see Security and signatures.

  • publicCert: the service provider's public signing certificate used to embed in AuthnRequest in order for the IDP to validate the signatures of the incoming SAML Request, see Security and signatures

  • decryptionPvk: optional private key that will be used to attempt to decrypt any encrypted assertions that are received

  • signatureAlgorithm: valid values are 'sha1', 'sha256', or 'sha512'

  • digestAlgorithm: optionally set the digest algorithm used to provide a digest for the signed data object, valid values are 'sha1' (default), 'sha256', or 'sha512'

  • xmlSignatureTransforms: optionally set an array of signature transforms to be used in HTTP-POST signatures. By default this is [ 'http://www.w3.org/2000/09/xmldsig#enveloped-signature', 'http://www.w3.org/2001/10/xml-exc-c14n#' ]

  • Additional SAML behaviors

  • additionalParams: dictionary of additional query params to add to all requests; if an object with this key is passed to authenticate, the dictionary of additional query params will be appended to those present on the returned URL, overriding any specified by initialization options' additional parameters (additionalParams, additionalAuthorizeParams, and additionalLogoutParams)

  • additionalAuthorizeParams: dictionary of additional query params to add to 'authorize' requests

  • identifierFormat: optional name identifier format to request from identity provider (default: urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress)

  • allowCreate: grants permission to the identity provider to create a new subject identifier (default: true)

  • spNameQualifier: optionally specifies that the assertion subject's identifier be returned (or created) in the namespace of another service provider, or in the namespace of an affiliation of service providers

  • wantAssertionsSigned: if true, add WantAssertionsSigned="true" to the metadata, to specify that the IdP should always sign the assertions. It is on by default. Note: either the response or the assertion must be signed even if both are turned off.

  • wantAuthnResponseSigned: if true, require that all incoming authentication response messages be signed at the top level, not just at the assertions. It is on by default. Note: either the response or the assertion must be signed even if both are turned off.

  • 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.

  • maxAssertionAgeMs: Amount of time after which the framework should consider an assertion expired. If the limit imposed by this variable is stricter than the limit imposed by NotOnOrAfter, this limit will be used when determining if an assertion is expired.

  • attributeConsumingServiceIndex: optional AttributeConsumingServiceIndex attribute to add to AuthnRequest to instruct the IDP which attribute set to attach to the response (link)

  • disableRequestedAuthnContext: if truthy, do not request a specific authentication context.

  • 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

  • racComparison: Requested Authentication Context comparison type. Possible values are 'exact','minimum','maximum','better'. Default is 'exact'.

  • 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.

  • passive: if set to true, specifies that the IdP must not visibly take control of the user interface and interact with the user.

  • 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.

  • authnRequestBinding: if set to HTTP-POST, will request authentication from IDP via HTTP POST binding, otherwise defaults to HTTP Redirect

  • disableRequestAcsUrl: if truthy, SAML AuthnRequest from the service provider will not include the optional AssertionConsumerServiceURL. Default is falsy so it is automatically included.

  • generateUniqueId: optional function which will be called to generate unique IDs for SAML requests.

  • scoping: An optional configuration which implements the functionality explained in the SAML spec paragraph "3.4.1.2 Element ". The config object is structured as following:

  • signMetadata: if true, adds a signature to the generated Service Provider metadata. privateKey must be set to use this option.

{
  idpList: [ // optional
    {
      entries: [ // required
        {
          providerId: 'yourProviderId', // required for each entry
          name: 'yourName', // optional
          loc: 'yourLoc', // optional
        }
      ],
      getComplete: 'URI to your complete IDP list', // optional
    },
  ],
  proxyCount: 2, // optional
  requesterId: 'requesterId', // optional
}
  • InResponseTo Validation

  • validateInResponseTo:

    • if "always", then InResponseTo will be validated from incoming SAML responses
    • if "never", then InResponseTo won't be validated
    • if "ifPresent", then InResponseTo will only be validated if present in the incoming SAML response
  • requestIdExpirationPeriodMs: Defines the expiration time when a Request ID generated for a SAML request will not be valid if seen in a SAML response in the InResponseTo field. Default is 8 hours.

  • cacheProvider: Defines the implementation for a cache provider used to store request Ids generated in SAML requests as part of InResponseTo validation. Default is a built-in in-memory cache provider. For details see the 'Cache Provider' section.

  • Issuer Validation

  • idpIssuer: if provided, then the IdP issuer will be validated for incoming Logout Requests/Responses. For ADFS this looks like https://acme_tools.windows.net/deadbeef

  • Passport

  • passReqToCallback: if truthy, req will be passed as the first argument to the verify callback (default: false)

  • name: Optionally, provide a custom name. (default: saml). Useful If you want to instantiate the strategy multiple times with different configurations, allowing users to authenticate against multiple different SAML targets from the same site. You'll need to use a unique set of URLs for each target, and use this custom name when calling passport.authenticate() as well.

  • Logout

  • logoutUrl: base address to call with logout requests (default: entryPoint)

  • additionalLogoutParams: dictionary of additional query params to add to 'logout' requests

  • logoutCallbackUrl: The value with which to populate the Location attribute in the SingleLogoutService elements in the generated service provider metadata.

  • SAML Authn Request Extensions

  • samlAuthnRequestExtensions: Optional, The SAML extension provides a more flexible structure for expressing which combination of Attributes are requested by service providers in comparison to the existing mechanisms, More about extensions. There are many possible values for the samlAuthnRequestExtensions element. It accepts fully customize XMLBuilder type.

// Example
samlAuthnRequestExtensions: {
  "md:RequestedAttribute": {
    "@isRequired": "true",
    "@Name": "LastName",
    "@xmlns:md": "urn:oasis:names:tc:SAML:2.0:metadata"
  },
  vetuma: {
    "@xmlns": "urn:vetuma:SAML:2.0:extensions",
    LG: {
      "#text": "sv",
    },
  },
},
  • SAML Logout Request Extensions
  • samlLogoutRequestExtensions: Optional, The SAML extension provides a more flexible structure for expressing which combination of Attributes are requested by service providers in comparison to the existing mechanisms, More about extensions. There are many possible values for the samlLogoutRequestExtensions element. It accepts fully customize XMLBuilder type.
// Example
samlLogoutRequestExtensions: {
  vetuma: {
    "@xmlns": "urn:vetuma:SAML:2.0:extensions",
    LG: {
      "#text": "sv",
    },
  },
},
  • SAML metadata Extensions
  • metadataContactPerson: Optional, this parameters can be used to include more metadata in the XML generated by generateServiceProviderMetadata. There are many possible values for the metadataContactPerson element. You can check the type definitions for help.
  • metadataOrganization: Optional, this parameters can be used to include more metadata in the XML generated by generateServiceProviderMetadata. There are many possible values for the metadataOrganization element. You can check the type definitions for help.
// Example for metadataContactPerson
metadataContactPerson:  [{
  "@contactType": "support",
  "GivenName": "test",
  "EmailAddress": "test@node-saml",
}],
// ContactPerson is an array because there can be multiple ContactPerson fields

generateServiceProviderMetadata( decryptionCert, publicCert )

As a convenience, the strategy object exposes a generateServiceProviderMetadata method which will generate a service provider metadata document suitable for supplying to an identity provider.

The decryptionCert argument should be a public certificate matching the decryptionPvk and is required if the strategy is configured with a decryptionPvk.

The publicCert argument should be a public certificate matching the privateKey and is required if the strategy is configured with a privateKey. An array of certificates can be provided to support certificate rotation. When supplying an array of certificates, the first entry in the array should match the current privateKey. Additional entries in the array can be used to publish upcoming certificates to IdPs before changing the privateKey.

generateServiceProviderMetadata( params )

The underlying generateServiceProviderMetadata function is also exported directly. This is useful if you want to generate metadata without creating a strategy object.

const { generateServiceProviderMetadata } = require("@node-saml/node-saml");

const metadata = generateServiceProviderMetadata({
  issuer: "https://example.com",
  callbackUrl: "https://example.com/callback",
});

Security and signatures

Node-SAML uses the HTTP Redirect Binding for its AuthnRequests (unless overridden with the authnRequestBinding parameter), and expects to receive the messages back via the HTTP POST binding.

Configuration option signatureAlgorithm

Authentication requests sent by Node-SAML can be signed using RSA signature with SHA1, SHA256 or SHA512 hashing algorithms.

To select hashing algorithm, use:

signatureAlgorithm: 'sha1' // (default, but not recommended anymore these days)
signatureAlgorithm: 'sha256', // (preferred - your IDP should support it, otherwise think about upgrading it)
signatureAlgorithm: 'sha512' // (most secure - check if your IDP supports it)

Configuration option privateKey

To sign authentication requests, private key needs to be provide in the PEM format via the privateKey configuration property. Node-SAML is enforcing RFC7468 stricttextualmsg format for PEM files.

Add it to strategy options like this:

privateKey: fs.readFileSync("./privateKey.pem", "latin1");

Example formats for privateKey field are,

  1. RFC7468 stricttextualmsg formatted PEM:
-----BEGIN PRIVATE KEY-----
<private key contents here delimited at 64 characters per row>
-----END PRIVATE KEY-----

or

-----BEGIN RSA PRIVATE KEY-----
<private key contents here delimited at 64 characters per row>
-----END RSA PRIVATE KEY-----
  1. Alternatively, a single-line or multi-line private key in Base64 format. See example from tests of single line private key.

Configuration option idpCert

It is important to validate the signatures of the incoming SAML Responses. For this, provide the Identity Provider's public X.509 signing certificate(s) or public key(s) in RFC7468 stricttextualmsg PEM format via the idpCert configuration property.

Important, provided public key MUST always be in PEM format!

Add it to options like this:

idpCert: "MIICizCCAfQCCQCY8tKaMc0BMjANBgkqh ... W==";

or

If the Identity Provider has multiple signing certificates or public keys that are valid then the idpCert configuration property can be an array. This can be the case during the rolling from an old key to a new key and responses signed with either key are valid:

idpCert: ["MIICizCCAfQCCQCY8tKaMc0BMjANBgkqh ... W==", "MIIEOTCCAyGgAwIBAgIJAKZgJdKdCdL6M ... g="];

or

The idpCert configuration property can also be a function that receives a callback as argument calls back a possible error and a certificate or array of certificates or a public key or array of public keys. This allows the Identity Provider to be polled for valid certificates or public keys and the new certificate or public key can be used if it is changed:

idpCert: (callback) => {
  callback(null, polledCertificates);
};

Example formats for idpCert field are,

  1. RFC7468 stricttextualmsg formatted PEM:
-----BEGIN CERTIFICATE-----
<certificate contents here delimited at 64 characters per row>
-----END CERTIFICATE-----

or

-----BEGIN PUBLIC KEY-----
<public key contents here delimited at 64 characters per row>
-----END PUBLIC KEY-----
  1. Alternatively, a single-line or multi-line certificate in Base64 format.

TIP: If the certificate is in the binary DER encoding

Convert it to the necessary PEM encoding like this:

openssl x509 -inform der -in my_certificate.cer -out my_certificate.pem

Some identity providers require that the public signing certificate be embedded in AuthnRequest in order for the IDP to verify the request as well as match the subject DN and confirm if the certificate was signed. This can be achieved by passing service provider's public signing certificate in PEM format via the publicCert configuration key. The publicCert should be a public certificate matching the privateKey.

-----BEGIN CERTIFICATE-----
<X.509 certificate contents here delimited at 64 characters per row>
-----END CERTIFICATE-----

Alternativelly a single line X.509 certificate without start/end lines where all rows are joined into single line can be passed:

publicCert: "MIICizCCAfQCCQCY8tKaMc0BMjANBgkqh ... W==";

SAML Response Validation - NotBefore and NotOnOrAfter

If the NotBefore or the NotOnOrAfter attributes are returned in the SAML response, Node-SAML will validate them against the current time +/- a configurable clock skew value. The default for the skew is 0s. This is to account for differences between the clock time on the client (Node server with Node-SAML) and the server (Identity provider).

NotBefore and NotOnOrAfter can be part of either the SubjectConfirmation element, or within in the Assertion/Conditions element in the SAML response.

Subject confirmation validation

When configured (turn validateInResponseTo to always in the Node-SAML config), the InResponseTo attribute will be validated. Validation will succeed if Node-SAML previously generated a SAML request with an id that matches the value of InResponseTo.

Also note that InResponseTo is validated as an attribute of the top level Response element in the SAML response, as well as part of the SubjectConfirmation element.

Previous request id's generated for SAML requests will eventually expire. This is controlled with the requestIdExpirationPeriodMs option passed into the Node-SAML config. The default is 28,800,000 ms (8 hours). Once expired, a subsequent SAML response received with an InResponseTo equal to the expired id will not validate and an error will be returned.

Cache Provider

When InResponseTo validation is turned on, Node SAML will store generated request ids used in SAML requests to the IdP. The implementation of how things are stored, checked to see if they exist, and eventually removed is handled by the configured CacheProvider.

The default implementation is a simple in-memory cache provider. For multiple server/process scenarios, this will not be sufficient as the server/process that generated the request id and stored in memory could be different than the server/process handling the SAML response. The InResponseTo could fail in this case erroneously.

To support this scenario you can create a cache provider that implements the following interface:

interface CacheProvider {
  // Store an item in the cache, using the specified key and value.
  saveAsync(key: string, value: string): Promise<CacheItem | null>;
  // Returns the value of the specified key in the cache
  getAsync(key: string): Promise<string | null>;
  // Removes an item from the cache if the key exists
  removeAsync(key: string): Promise<string | null>;
}

SLO (single logout)

Node-SAML has built in support for SLO including

  • Signature validation
  • IdP initiated and SP initiated logouts
  • Decryption of encrypted name identifiers in IdP initiated logout
  • Redirect and POST SAML Protocol Bindings

ChangeLog

See Changelog

FAQ

Node Support Policy

We only support Long-Term Support versions of Node.

We specifically limit our support to LTS versions of Node, not because this package won't work on other versions, but because we have a limited amount of time, and supporting LTS offers the greatest return on that investment.

It's possible this package will work correctly on newer versions of Node. It may even be possible to use this package on older versions of Node, though that's more unlikely as we'll make every effort to take advantage of features available in the oldest LTS version we support.

As each Node LTS version reaches its end-of-life we will remove that version from the node engines property of our package's package.json file. Removing a Node version is considered a breaking change and will entail the publishing of a new major version of this package. We will not accept any requests to support an end-of-life version of Node. Any merge requests or issues supporting an end-of-life version of Node will be closed.

We will accept code that allows this package to run on newer, non-LTS, versions of Node.

passport-saml's People

Contributors

adalinesimonian avatar alvinward avatar andkrist avatar archinowsk avatar ashimaathri avatar bergie avatar brianhartsock avatar cjbarth avatar dependabot[bot] avatar dnbard avatar eero3 avatar forty avatar gnawhleinad avatar gugu avatar heikkihakkalasc avatar jess-sheneberger avatar josecolella avatar lcalvy avatar lonerifle avatar mans0954 avatar markstos avatar mhassan1 avatar midgleyc avatar pdspicer avatar ploer avatar rob-gijsens avatar stavros-wb avatar tkopczuk avatar walokra avatar xdmnl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

passport-saml's Issues

If no protocol specified, fallback to req.protocol, not to https://

If I only specify a path property and not the callbackUrl, I would expect the protocol to fallback to whatever is used in the request, not https.

For example, when I have my server running on http://localhost:4000 and I specify only the property 'path' to be /callback, I would expect the callbackUrl to be http://localhost:4000/callback, currently it will be https://localhost:4000/callback.

express.get() is login in

Is there possibility to do something like this?

express.get('/, function(req, res){
if(userIsLoginIn){
res.render(โ€ฆ);
}else{
// It do not execute this part
passport.authenticate('saml', { failureRedirect: '/', failureFlash: true }),
function(req, res) {
res.redirect('/');
}
}
});

HTTP POST Binding in passport-saml

Hi,
I've to use passport-saml in HTTP POST Binding. Would you please help me to find a way to add this implementation to this module? Is there a pre-existent module doing this?

Thanks

Adding HTTP headers

Is there a way to add http headers to the request to the Authorization server? For instance, I have a consumer key that needs to be included in the Authorization header.

Different digest value's

I'm trying to authenticate using ADFS and I just hit a problem which I don't really get and thought maybe I could get some help.
So after the the response from IdP comes back I can see it has CipherValue so I debugged and saw that the passport-saml decrypts it.
The problem is that while it decrypts it, it throws an exception at signed-xml.js row 291, when it tries to compare between the digest that it calculated and the digest that was recieved.
Any idea why is this happening?

Thanks in advance!

How to change callbackURL?

Hi Dear

I am trying on your sample passport-saml-example.
I found IE always will be redirect to fixed-url "localhost:3000" after logo on ย Feide openIdP.

I tried to set param "callbackUrl" with full url but no luck.

Could you please tell me how to change hostname&port of callbackURL?
Thanks in advance!

Change in documentation

Additional configs states that setting samlFallback to 'getAuthorizeUrl' will cause a redirect when really it is set samlFallback to 'login-request'

example login form

I'm working on an app that would support multiple authentication mechanisms, including SAML. Passport basics are all working, but SAML is new to me.

Am I correct in assuming passport-saml doesn't actually need a username or a password from a login page? Just displaying a "login with your fav. SSO provider" button should be enough, which is connected to the express route that calls the authentication strategy doing redirect and whatnot.

Couldn't find an example, sorry if this is a stupid question.

Call validation functions in saml.js directly?

I'd like to call the parsing/validation functions in saml.js directly, bypassing the passport-related stuff altogether. Hypothetical example:

var SAML = require("saml.js").SAML;
var SignedXml = require("xml-crypto").SignedXml;

var sig = new SignedXml();
sig.computeSignature(...);

var xml = sig.getSignedXml();
var res = { SAMLResponse: new Buffer(xml).toString("base64") };

var validator = new SAML({ ... });
validator.validatePostResponse(res, function(err, profile) {
  console.log(err, profile);
});

However, passport-saml doesn't export the saml.js module, so there's no way to do this currently. :(

Perhaps we could move saml.js into it's own lightweight Node package, one that is focused purely on parsing/validating SAML? And then make it a dependency of passport-saml?

I realize this might be a bit of a tall order, but I think it makes sense. Thoughts?

Getting to work with Salesforce IDP...

This code works properly as long as I don't supply the cert option to do signature validation. When I add the cert value, I get this (having dug into xml-crypto stuff):

'invalid signature: for uri #_fab050762cee2d1ead90663534fa1ea71351722430313 calculated digest is pBBC3Gm4+/cvi4Q2xu0KaNbk+gA= but the xml to validate supplies digest mxsu5Cr0qZH+6ZBj4SkDJTP0QUA='

Im lost at this point... Any suggestions for how to troubleshoot this?

Suggestions for generating SAML responses?

Any suggestions for tools to generate SAML responses? I'm working on support for InResponseTo validation in the SAML response and need to be able to sign a SAML response, get the public key, etc...

I've come across XmlSecTool

Which seems likes its capable for the job. However it's not clear how you reference a locally generated keystore. Anyone use this? Also anyone have a good base unsigned response to start from for testing purposes? Is this as simple as taking a SAML response I've intercepted and just removing the Signature element and running it through a tool like XmlSecTool?

Any other recommended tools?

thanks.

Cannot use dynamic values for entryPoint and cert

So currently, the lib requires you to specify the entry point on the app load but onelogin requires the url to be dynamic like https://app.onelogin.com/trust/saml2/http-post/sso/{{random_id_generated}}. Thoughts on how to do this currently because I might just be doing it wrong but this seems like a common use case where there are multiple companies.

passport.use(new SamlStrategy({
    issuer: 'someone',
  }, function(profile, done) {
    console.log('Auth with', profile);
    if (!profile.nameID ) {
      return done(new Error('No email found'), null);
    }
    return done(null, null)
  }))
  app.get('/saml/auth', function (req, res, next){
    return passport.authenticate('saml', { 
       entryPoint:  req.company.entryPoint,
       cert:  req.company.cert
    })(req, res, next)
  }, authentication.samlAuth)

gulp test never quits because of InMemoryCacheProvider

I'm unit-testing the integration of passport-saml with gulp and gulp-jasmine. The problem is the test script never quits when any test initalizing passport-saml runs.

With process._getActiveHandles() I traced this to a single 28800000ms timer - this is all I could find out because node-debug gulp is excruciatingly slow - which appears only once in the entire codebase:

if(!options.requestIdExpirationPeriodMs){
    options.requestIdExpirationPeriodMs = 28800000;  // 8 hours
  }

in saml.js#L46

This is used by the InMemoryCacheProvider's cache expiry timer in inmemory-cache-provider.js#L31

setInterval(function(){
        ...
}, this.options.keyExpirationPeriodMs);

Can you recommend a strategy to "fix" testing? I'd prefer not building a new cache provider, because that would leave the builtin one untested. Perhaps exposing the intervalObject returned by setInterval which would allow clearInterval?

Or maybe there's a simpler solution I just can't see it. This freezing of the test - making gulp watch unusable - has been driving me crazy over the past few days.

Validating incoming SAML Responses?

How do you validate incoming SAML responses?

The documentation says to provide the "cert" option, and I'm doing that:

new SamlStrategy(
    {
    path:'/authorization/saml',
    cert:cert
},
function(profile, done){
    //...

, but when I go in and edit the cert by a character, making it invalid, it still works just fine, which suggests that it's not actually validating anything.

I'm testing this by using Okta as the identity provider. Perhaps I'm not setting some Okta option right?

My Okta options:
okta options

Support id attribute variations?

As seen here, xml-crypto allows for an element's id attribute to be specified as "Id" or "ID". But I noticed here and here that passport-saml only supports "ID". Possible to make it support "Id" as well? (Not sure what the implications are, if any)

How to use with SimpleSAMLphp?

Hi,
I have SimpleSAMLphp IdP and SimpleSAMLphp SP.
I assume my node.js app is the SP. So, probably, I don't need SimpleSAMLphp SP?

Could you provide an example how to configure passport-saml to work with SimpleSAMLphp IdP?

Getting 'Invalid Signature' error on SAML Response. XML digests do not match

We're having some issues getting passport-saml setup with an Okta IDP. We are getting a response back from our IDP, but the validation is failing. Our IDP made sure that the signature and digests are done with SHA1. Debugging into the code, (all the way into the XMLCrypto), I'm finding that it is failing when the digests don't match. If we comment out the assertion validation, we are getting all the expected data so I'm pretty sure that SAML response is formatted properly.

I added in a console.log to get the canonXML that is being hashed for the digest, and the output is this:

<ns2:Assertion xmlns:ns2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_73bfe3a75de08316ce83cb05534a91aa2837" IssueInstant="2015-03-16T21:50:22Z" Version="2.0">&#xD;
        <ns2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">REDACTED</ns2:Issuer>&#xD;
        <ns2:Subject>&#xD;
            <ns2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">REDACTED</ns2:NameID>&#xD;
            <ns2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">&#xD;
                <ns2:SubjectConfirmationData NotOnOrAfter="2015-03-16T21:51:51Z" Recipient="https://REDACTED"></ns2:SubjectConfirmationData>&#xD;
            </ns2:SubjectConfirmation>&#xD;
        </ns2:Subject>&#xD;
        <ns2:Conditions NotBefore="2015-03-16T21:49:51Z" NotOnOrAfter="2015-03-16T21:51:51Z">&#xD;
            <ns2:AudienceRestriction>&#xD;
                <ns2:Audience>REDACTED</ns2:Audience>&#xD;
            </ns2:AudienceRestriction>&#xD;
        </ns2:Conditions>&#xD;
        <ns2:AuthnStatement AuthnInstant="2015-03-16T21:22:30Z" SessionIndex="rqRyLqemlvvu9A3OE9la6I1Z8iY=RgRbeg==" SessionNotOnOrAfter="2015-03-16T21:51:51Z">&#xD;
            <ns2:AuthnContext>&#xD;
                <ns2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</ns2:AuthnContextClassRef>&#xD;
            </ns2:AuthnContext>&#xD;
        </ns2:AuthnStatement>&#xD;
        <ns2:AttributeStatement>&#xD;
            <ns2:Attribute Name="FirstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">&#xD;
                <ns2:AttributeValue>REDACTED</ns2:AttributeValue>&#xD;
            </ns2:Attribute>&#xD;
            <ns2:Attribute Name="LastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">&#xD;
                <ns2:AttributeValue>REDACTED</ns2:AttributeValue>&#xD;
            </ns2:Attribute>&#xD;
        </ns2:AttributeStatement>&#xD;
    </ns2:Assertion>

One thing that concerns is me the ' ' line endings, yet in the console we are also getting the line feeds and other whitespace. I know next to nothing about XML canonicalization, but I'm wondering if the CRLFs in the SAML Response are not being properly transformed into the Canon XML for hashing.

Another thing we're wondering is if our configuration on the provider side has anything to do with digest and signature (aside from the cert). For example, all the code examples we've seen have 'passport-saml' as the issuer, but we used our own issuer that we created basically at random and gave to our IDP. We assumed that was just a placeholder for our own issuer.

Unfortunately, my team isn't in charge of the IDP so we don't really have much control over it, and we're also the first in our org to try to use Node.js with SAML, so we're treading new ground with our SAML team to get this working.

Distributed System

Hello Bergie,

Does this module support distributed system authorization?

Thanks.

Make passport-saml work with wso2 is,

I am using passport-saml nodejs module with WSO2is-5.0.0 using SAMLstragegy trying to implementt SSO, I am able to login WSO2is-5.0.0 server, using it, but when I am try to logout , my session still exist, unable to implement logout .

here my SAMLStrategy configuration:

passport.use(new SamlStrategy(
{
    path: '/AssertionConsumer',
    entryPoint: config.idpUrl,
    issuer: config.issuer,
    protocol: config.httpProtocol,
    identifierFormat : config.identifierFormat,
    logoutUrl:'/logout',
    attributeConsumingServiceIndex:1012175983,
   }, function(profile, done){

    console.log('Profile: %j', profile);
    return done(null, profile);
}
  ));

Is there any configuration I miss for wso2is-5.0.0 with passport-saml ?

This is my config data:

config.idpUrl= "https://localhost:9443/samlsso";
config.issuer = "passport-saml";
config.httpProtocol = "http://";
config.identifierFormat =  "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";

I had checked further, starting wso2 in debug mode

log4j.logger.org.wso2.carbon.identity=DEBUG

then using a logout url in route also configure in strategy

logoutUrl:'/logout'

 router.get('/logout',passport.authenticate('saml', { failureRedirect:  '/error', failureFlash: true, samlFallback:'logout-request' }),function(req, res) {

req.logout();
res.redirect('/');

 });

now when I click further in http://localhost:9001/logout

I got this error : Error when processing the authentication request! msg in browser

so I did check in my logs found passport-saml have missing SessionIndex information in LogoutRequest

    <?xml version="1.0"?><samlp:LogoutRequest xmlns:samlp="u
      rn:oasis:names:tc:SAML:2.0:protocol"  xmlns:saml="urn:oasis:names:tc:SAML:2.0:ass
   ertion" ID="_66a8e1407de73b21ed44" Version="2.0" IssueInstant="2015-03-16T16:06:
   41.743Z" Destination="https://localhost:9443/samlsso"><saml:Issuer  xmlns:saml="u
      rn:oasis:names:tc:SAML:2.0:assertion">passport-saml</saml:Issuer><saml:NameID Fo
      rmat="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">admin</saml:NameID
    ></samlp:LogoutRequest>

other wise it should also contain session Index in Logout request

some thing like this for example:

    <saml2p:LogoutRequest Destination="https://identityserver:9443/samlsso"
                  ID="_a81995b837df1caceb991926e2adebe3468559fc"
                  IssueInstant="2015-01-02T22:30:57.569Z"
                   Version="2.0"
                    xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">liferayserver</saml2:Issuer>
 <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
               xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">[email protected]
</saml2:NameID><saml2p:SessionIndex>8d89a03c-aebc-452f-  8ac5-035bda817ec5</saml2p:SessionIndex>
 </saml2p:LogoutRequest>

In wso2 SessionIndex is used for maintaining session, how can I get work passport-saml work with wso2?

Example with logout functionality

As state in the title - I try to logout user using passport-saml. On the node.js side it works fine, but on the side of Identity Provider I'm still logged in. Don't know how correctly pass and use logoffUrl to make sure, that session will be closed also on IP side.

thanx

How to add signature on AuthnRequest

Hello,
I'm trying to add signature on my AuthnRequest.
I've tried to add some configuration to SamlStrategy but nothing appens on AuthnRequest XML.
Do i need to use another module like XML-Crypto ? If yes, how??

This is the configuration for Strategy :

passport: {
            strategy : 'saml',
            saml : {
                entryPoint : 'https://idp.idp.it/samlsso',
                issuer : 'http://111.111.11.111:8000',
                callbackUrl : 'http://111.111.11.111:8000/login/callback',
                identifierFormat: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
                decryptionPvk: fs.readFileSync('./config/saml.key'),
                cert:'MII....Q==',
                validateInResponseTo :true,
                authnContext : 'urn:oasis:names:tc:SAML:2.0:ac:classes:name1',
                protocol: 'http://',
                attributeConsumingServiceIndex:0
            }
        }

This how route the login request, but i don't know how to add signature on it

app.get("/login",
        passport.authenticate(config.passport.strategy,
        {
            successRedirect : "/",
            failureRedirect : "/login",
        })
    );

    app.post('/login/callback',
        passport.authenticate(config.passport.strategy,
            {
                failureRedirect: '/',
                failureFlash: true
            }),
        function(req, res) {
            res.redirect('/');
        }
    );

This is the SAML AuthnRequest xml :

<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                    ID="_6eefdebac845aec3b311"
                    Version="2.0"
                    IssueInstant="2015-03-30T09:47:12.835Z"
                    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
                    AssertionConsumerServiceURL="http://111.111.11.111:8000/login/callback"
                    Destination="https://idp.idp.it/samlsso"
                    >
    <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">http://111.111.11.111:8000</saml:Issuer>
    <samlp:NameIDPolicy xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                        Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
                        AllowCreate="true"
                        />
    <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">urn:oasis:names:tc:SAML:2.0:ac:classes:name1</saml:AuthnContextClassRef>
    </samlp:RequestedAuthnContext>
</samlp:AuthnRequest>

Is there a preferred way to configure Passport-SAML to support dynamic IdPs?

So I'm working on an app that needs to support dynamic IdPs. Upon receiving a request, the database is queried for a config JSON object based on some request parameters. That config object will contain any and all data about the IdP we're trying to work with for SSO. How would I go about configuring passport-saml to run with dynamic configurations? Is there a smarter approach?

The default NameID format causes problems

The nameid-format currently defaults to:

urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress

This can causes problems for those IdPs that do not support this specific format e.g. TestShib.

I believe that the SAML specs indicate default should be:

urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified

I understand that the default can be overidden easily using the identifierFormat however I think it would be better if the default was set to the least restrictive option.

Thanks for maintaining a great bit of kit.

FR: SingleLogout

Add a possibility to wire a single logout callback URL, which would work both in SP-initiiated as well as in IdP-initiated scenario.

Now, only LogoutResponse is processed.

Handling multiple configurations for SAML selected at runtime

I'm working with Okta as a provider for SAML authentication and this repo works wonderfully for one "company" because it only needs one set of configuration parameters. My site however can consume several different companies' logins which we divide by "groups" of logins, meaning it requires different entryPoint, issuer and cert options per group. Ideally I'd like to provide this as part of the call to authenticate because then it can work in real time.

I made a temporary fix for my app, creating a req.saml = { issuer: ..., cert: ..., entryPoint:... } generated by another middleware which seems like the ideal way to pass it in. However I then override the options hash stored in the _saml.options which seems non-ideal because the chance to have a default is lost and opens up to a lot of bugs caused by unintentionally overriding defaults.

I was in a bit of hurry to get it so I haven't gone through the code extensively enough to make a proper pull request with a real solution. Does the above situation seem like something desirable? I can help implement it if needed (not sure when I'll start by myself though).

passport-saml debug

Hey do you have a clue, how to debug passport-saml.
My app:
app.post ('/callback' , passport.authenticate('saml', {failureRedirect: '/', failureFlash: true}), function(req,res){
res.redirect('/');
});
if I do the following:
args.app.post ('/callback' , /*passport.authenticate('saml', {failureRedirect: '/', failureFlash: true}), */ function(req,res){
res.redirect('/');
});
it works totally fine.

It is in a loop and redirects me to the login page again and again. I am looking for a way how to debug it (on linux).

By default initiate a login-request

Currently for getting the lib to work we have to provide the samlFallback property to be login-request, however, I wonder why we should call it fallback.

I believe the default should be login-request (like the older version of the lib was doing).

Signature namespace defined in parent

I'm having an issue where in an Assertion, the namespace of the signature is defined in the Response element.

<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" 
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
...

    <dsig:Signature>      
...
    </dsig:Signature>

So when the validateSignature method extracts the node and converts it to a string, the namespace is lost (and xml-crypto rejects since it queries based on the namespace-uri)

Is this something passport-saml cares about? or is this a bad use of the Signature element/namespace?

Temporarily, I have put a workaround in the validateSignature function for myself:

Instead of simply var signature = signatures[0].toString(); in line 347

  var signature = signatures[0];

  // If Signature doesn't have it's namespace explicitly defined (was defined in parent)
  // it will be lost in the toString(), in this case, add it
  var prefix = signature.prefix;
  var nsAttr = 'xmlns';
  if (prefix) {
    nsAttr += ':' + prefix;
  }
  if (!signature.hasAttribute(nsAttr)) {
    signature.setAttribute(nsAttr, 'http://www.w3.org/2000/09/xmldsig#');
  }

  signature = signature.toString();

Shibboleth update

These are the steps I performed to try to connect our Node.JS app with a Shibboleth IdP

Setup

  1. Register our node app as a Service Provider with the Identity Provider.

I used testshib.org to test our setup. I copied the metadata.xml from a standard SP (running under Apache with mod_shib which in turn talks to shibd) and significantly slimmed it down to

<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" ID="_957cf08a6730ac2e70ce094b8262cdf79ce25120" entityID="https://oae.cam.ac.uk/shibboleth">

  <md:Extensions xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport">
    <alg:SigningMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
  </md:Extensions>

  <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
    <md:KeyDescriptor>
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:KeyName>linux-5z8i.site</ds:KeyName>
        <ds:X509Data>
          <ds:X509SubjectName>CN=linux-5z8i.site</ds:X509SubjectName>
          <ds:X509Certificate>MIIC9DCCAdygAwIBAgIJAJPgjeqQYMniMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNV
BAMTD2xpbnV4LTV6OGkuc2l0ZTAeFw0xMzAyMDgxODQ5MzlaFw0yMzAyMDYxODQ5
MzlaMBoxGDAWBgNVBAMTD2xpbnV4LTV6OGkuc2l0ZTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAOoq83FgLTaOMqFMjsrmKk80S4kDpixN9ajKdBjTD+9f
iOUIr5x8a/xdE8OkDGWZsMTRJzAK3Mj1xY/d5NrGLCyKnb1v5ylLUd41NdpECRhl
GZvonbqrB6usdm+Bs01NOdyerjvhyLovQddtv/eFvi51bwtKr9JhH+H6Nh9Y0u/V
5Sjq+5DWug8QgA4+BHXSxYsKVhrAqxLHNPUCFzIqggF/Ncc4V8To3laH8YotF2uo
AnQKs7qpIvDxpWYnWBIFFZsemozW8IE6gxY/WiAnTOOHULCMe49RUDu12fIyQOwd
FrmsRncU7kMkvgFJpDQj6Hs6gff+4CzlwItB+O5tD7sCAwEAAaM9MDswGgYDVR0R
BBMwEYIPbGludXgtNXo4aS5zaXRlMB0GA1UdDgQWBBStENAKttjMN3gopEzHNHSz
gEurazANBgkqhkiG9w0BAQUFAAOCAQEAYpVMAZkxyf0MQNI0BOrMj4ufvqxF8Phh
kpN4m/qa/Bz741W2oXYWRRuziQemho7LFeSutcj05uL9UoYSGl9Uf+gNJyVNqIwX
HZ4rbetphzrxoQpR8/ykIf8px70Af38bv0v8iNPyyaiW4uAj8b6rUNV2Q1BSyZKk
CqLh5BUnKaw8atgFgMyulqZ/QqPXzMp+MvLMf8eykBtvM85RyNbbtkDOIEmugCFL
eJBy/J977I8UjVG3NaSG6PW1TSt/UumfnDNPKORYDlvpqq/RhJPp1bA3JxZWYaEu
+mgippboa2s6KL7xFO3X7b+Tr8OympTngHY/HzlWsa0G5MR3Op5RSg==
</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
      <md:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
    </md:KeyDescriptor>
    <md:AssertionConsumerService Location="https://oae.cam.ac.uk/api/auth/saml2/callback" index="1" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" />
    <md:AssertionConsumerService Location="https://oae.cam.ac.uk/api/auth/saml2/callback" index="5" Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post" />
  </md:SPSSODescriptor>

</md:EntityDescriptor>

As you can see our SP only supports HTTP-POST and browser-post and a very small subset of signing/encryption mechanismes.

  1. Setup passport-saml
    I used the following node code to register the SAML strategy. (slimmed down for brevity)
    var strategy = new SamlStrategy({
        'path': '/api/auth/saml2/callback',
        'entryPoint': 'https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO',  // Use the redirect option
        'issuer':  'https://oae.cam.ac.uk/shibboleth',  // The entity ID we used to register our SP with
        'cert':  'MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0 ....',  // The public key from the testshib.org IdP
        'privateCert': 'MII... ', // Our SP private key that matches the public one.
        'identifierFormat': null 
    }, function(profile, done) {
          // Verify function
         // get or create user
         // ...
    });
    passport.use('saml2', strategy);

...

//  `server` is the express server.

// Redirect the user to the configured SAML2 compatible IdP. When complete,
// the IdP will redirect the user back to the application at
// /api/auth/saml2/callback
server.get('/api/auth/saml2', function(req, res, next) {
     // Some custom logic which isn't important in this context
     passport.authenticate('saml2')(req, res, _handlePassportError(req, res, next));
});

// The IdP will redirect the user to this URL after approval.
server.post('/api/auth/saml2/callback', function(req, res, next) {
     // Some custom logic which isn't important in this context
    passport.authenticate('saml2', {'successRedirect': '/', 'failureRedirect': '/'})(req, res, next);
});

That's about it regarding setup.

Testing

  1. Browse to /api/auth/saml2
    This gets picked up by the passport-saml strategy and results in the following SAML object
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_2331765beede7e5acda2" Version="2.0" IssueInstant="2013-02-11T17:03:18Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="https://oae.cam.ac.uk/api/auth/saml2/callback"
Destination="https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO">
    <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://oae.cam.ac.uk/shibboleth</saml:Issuer>
    <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">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
    </samlp:RequestedAuthnContext>
</samlp:AuthnRequest>

Which gets succesfully turned into a suitable URL (ie: the XML gets base64'ed and the proper idp URL gets generated)

  1. My browser takes me to the shib IdP where I can log in with on the test accounts
  2. I check the IdP logs and see the incoming request.
  3. The IdP verifies my PW and generates a SAML response which it signs and encrypts and POSTs to my /api/auth/saml2/callback endpoint
    This is the point where passport-saml blows up.
    The XML it receives from the shibboleth IdP looks like
<?xml version="1.0" encoding="UTF-8" ?>
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://oae.cam.ac.uk/api/auth/saml2/callback" ID="_fa84b84c92664434e0844485fbde6326" InResponseTo="_2331765beede7e5acda2" IssueInstant="2013-02-11T17:03:45.590Z" Version="2.0">
    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://idp.testshib.org/idp/shibboleth</saml2:Issuer>
    <saml2p:Status>
        <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></saml2p:Status>
    <saml2:EncryptedAssertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
        <xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="_f10fa6b20292f7cc337ee32e5ecf17ad" Type="http://www.w3.org/2001/04/xmlenc#Element">
            <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" />
            <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <xenc:EncryptedKey Id="_b1e6d0ed4ce24daa0c018b90f715b43d" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
                    <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
                        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
                    </xenc:EncryptionMethod>
                    <ds:KeyInfo>
                        <ds:X509Data>
                            <ds:X509Certificate>MIIC9DCCAdygAwIBAgIJAJPgjeqQYMniMA0GCSqGSIb3DQEBBQUAMBoxGDAWBgNVBAMTD2xpbnV4 LTV6OGkuc2l0ZTAeFw0xMzAyMDgxODQ5MzlaFw0yMzAyMDYxODQ5MzlaMBoxGDAWBgNVBAMTD2xp bnV4LTV6OGkuc2l0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOoq83FgLTaOMqFM jsrmKk80S4kDpixN9ajKdBjTD+9fiOUIr5x8a/xdE8OkDGWZsMTRJzAK3Mj1xY/d5NrGLCyKnb1v
                                5ylLUd41NdpECRhlGZvonbqrB6usdm+Bs01NOdyerjvhyLovQddtv/eFvi51bwtKr9JhH+H6Nh9Y 0u/V5Sjq+5DWug8QgA4+BHXSxYsKVhrAqxLHNPUCFzIqggF/Ncc4V8To3laH8YotF2uoAnQKs7qp IvDxpWYnWBIFFZsemozW8IE6gxY/WiAnTOOHULCMe49RUDu12fIyQOwdFrmsRncU7kMkvgFJpDQj 6Hs6gff+4CzlwItB+O5tD7sCAwEAAaM9MDswGgYDVR0RBBMwEYIPbGludXgtNXo4aS5zaXRlMB0G
                                A1UdDgQWBBStENAKttjMN3gopEzHNHSzgEurazANBgkqhkiG9w0BAQUFAAOCAQEAYpVMAZkxyf0M QNI0BOrMj4ufvqxF8PhhkpN4m/qa/Bz741W2oXYWRRuziQemho7LFeSutcj05uL9UoYSGl9Uf+gN JyVNqIwXHZ4rbetphzrxoQpR8/ykIf8px70Af38bv0v8iNPyyaiW4uAj8b6rUNV2Q1BSyZKkCqLh 5BUnKaw8atgFgMyulqZ/QqPXzMp+MvLMf8eykBtvM85RyNbbtkDOIEmugCFLeJBy/J977I8UjVG3
                                NaSG6PW1TSt/UumfnDNPKORYDlvpqq/RhJPp1bA3JxZWYaEu+mgippboa2s6KL7xFO3X7b+Tr8Oy mpTngHY/HzlWsa0G5MR3Op5RSg==</ds:X509Certificate>
                        </ds:X509Data>
                    </ds:KeyInfo>
                    <xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
                        <xenc:CipherValue>FTEOGQ+iQaVmqnt5KvAKVr4BRGkhT67AuH6ElMgHdwkJewsytpDj2KLkkKjEtkrIC3ldzkpty1KlsT00CPeXUdOh/ucHHJlBZ97wN6zDI4Lv6iefVoZiCF565AeBMPU1AxhwxDQ3JqGWwli0cYmCcNbrthPC3soqlbZRxMObHzzsPtDLgs+VDj0fO794bSJ51x4lrY6GsL0JqSCLLalYtELI45PTt0mvR1/GwEj85EAt2wFb6IU94bUtesNHv3aVG/oKF0kcGfqF+NND2oK1BgGYd2ISICujKeBDSc+EHehmwgH5dTvfKSmAy6A6ilj39eRZDwCERN+SOU2VYqyuRw==</xenc:CipherValue>
                    </xenc:CipherData>
                </xenc:EncryptedKey>
            </ds:KeyInfo>
            <xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
                <xenc:CipherValue>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</xenc:CipherValue>
            </xenc:CipherData>
        </xenc:EncryptedData>
    </saml2:EncryptedAssertion>
</saml2p:Response>

This is the step where SAML blows up (not illogical as it probably doesn't expect encrypted SAML assertions.)

Now, AFAICT the only thing that needs to happen is implement the 'decryption' step. I'm not entirely sure if it's feasible to implement all the supported encryption algorithms, but it might be worth the attempt to try a couple?
I'd be interested in hearing your input.

passport-saml integration with onelogin

Hi
Passport-saml is not redirecting to callback url

module.exports = {
development : {
app : {
name : 'Passport SAML strategy example',
port : process.env.PORT || 8080
},
passport: {
strategy : 'saml',
saml : {
entryPoint : 'https://app.onelogin.com/trust/saml2/http-post/sso/1234',
//issuer : 'https://app.onelogin.com/saml/metadata/1234',
issuer: 'http://54.204.231.239:8080/login/callback',
callbackUrl : 'http://54.204.231.239:8080/login/callback',
cert: "onelogin.pem",
identifierFormat: null,
additionalParams:{'RelayState':null}
}
}
}
}
Please help...

How to prevent replay of SAML responses?

The issue I'm seeing is that the browser (in this case IE9) is storing the 302 request for the /saml/consume route in the browser history (this request contains the SAML response data in the body).

This means that a user hitting the back button, when they hit 'back' to this entry, a request will be sent to the node server and passport-saml module will interpret the SAML response supplied with the request and generate a valid passport session.

This is undesirable as a user can log out (and we delete the passport session), and then simply by hitting the back button, can be logged in again (or maybe logged in as a different user).

I think the solution to this problem is to have support in passport-saml to prevent replaying the SAML response, or supporting the OnBefore or NotOnOrAfter attributes in the response.

Is this kind of support available? Any suggestions on implementing this?

it seems like maybe this problem goes away if both the service provider and IDP are running under SSL - is that assumption correct?

Here's a stackoverflow question with an answer that I think hits on the possible solutions. Can anyone with some expertise in this area confirm that my thinking is correct?

thanks!

Extracting saml.js as its own module

Would you be open to this? I need SAML (but not the passport stuff) for my own nefarious purposes, and am currently approaching it with this plan:

  1. Fork passport-saml, remove everything not directly needed by saml.js
  2. Slim down the requirements (there's a conflict in our codebase with some of the requirements I think)

If you're interested in this (including potentially taking ownership of the resulting module/repo) please let me know. I could even imagine having passport-saml just pull in saml as a dependency?

NOTE: reactivating project, new maintainer

This project has been dormant for a while since Henri has moved on to other things. I just wanted to let everyone know I am now taking over maintenance of the project and will be publishing a new version in npm shortly.

Pull requests welcome. :-)

cheers,

peter

Validating SLO responses

I can generate a SLO request like this:

strategy.logout({
  ...
}, function createdLogoutUrl(error, requestUrl) {
  reply.redirect(requestUrl);
});

The user gets redirected to the SAML IdP which then redirects them back to the URL defined in the Location attribute of the SingleLogoutService element in the SP's metadata file, so far, so good, but the GET redirection from the IdP contains a SAMLResponse as part of the query string.

I can see there's a validatePostResponse method on the SAML class - how would I go about validating a GET response?

AuthnRequest IssueInstant is off by 2 hrs

I see that saml.generateInstant() adds 2 hours to the return of 'getUTCHours'. For this reason the saml authentication fails for two hours every day. Did you have a specific reason why you did that?

InCommon Federation

Hello!

I am trying to implement an authentication mechanism for our web application that uses InCommon Federation / IdP discovery (https://www.incommon.org/federation/). This allows user to select their home institution from a list presented by InCommon discovery service (http://www.incommon.org/federation/discovery.html), and forwarded to individual IdP for authentication, and finally get profile back to my web application - instead of manually having to configure IdP individually with my web app.

I am very new to the idea of SAML federation, but it looks like current implementation of passport-saml doesn't support this; it's missing the idpdisc:DiscoveryResponse in the metadata for one thing.

My questions are ..

  1. Does passport-saml already allow me to use InCommon federation? If so, is there a sample code?
  2. If not, is there any plan of adding support to InCommon federation / IpD discovery service?
  3. If there is no plan, I am thinking about adding such support to passport-saml (in a fork). If I do, is there any chance of merging it to upstream?

Thank you!!

Potential security vulnerability

Hi,
I tried to use passport-saml with okta as an identity provider.
I faced an issue with parsing the response from okta.
In the saml.js, there is this function called SAML.prototype.getElement.
This function gets the saml element from response.
In this the keys used to parse saml tags are 'saml' and 'samlp'.
Okta sends back 'saml2' and 'saml2p'.

So I had to add these keys in the code.
Here's how the method looks now.
Can you add it in the main library?

SAML.prototype.getElement = function (parentElement, elementName) {
if (parentElement['saml:' + elementName]) {
return parentElement['saml:' + elementName];
} else if (parentElement['samlp:'+elementName]) {
return parentElement['samlp:'+elementName];
} else if (parentElement['saml2p:'+elementName]) {
return parentElement['saml2p:'+elementName];
} else if (parentElement['saml2:'+elementName]) {
return parentElement['saml2:'+elementName];
}
return parentElement[elementName];
};

callbackUrl is not working, always redirect back to port 3000, anything wrong?

I want to change port from 3000 to 3001, but when I start using npm start, I can visit localhost:3001, but after I login, it is been redirected back to port 3000.
I tried to update config as this:

saml : {
callbackUrl : 'http://localhost:3001/login/callback',
//path : '/login/callback',
entryPoint : 'https://openidp.feide.no/simplesaml/saml2/idp/SSOService.php',
issuer : 'passport-saml'
}
Useless, still back to port 3000, anything wrong here?

Thanks

"Invalid RSAES-OAEP padding" Error

Here's my SAML Configuration:

var samlStrategy = new saml.Strategy({
    callbackUrl: 'https://myServer.rit.edu/login/callback',
    entryPoint: 'https://shibboleth.main.ad.rit.edu/idp/profile/SAML2/Redirect/SSO',
    issuer: 'https://myServer.rit.edu/shibboleth',
    identifierFormat: null,
    decryptionPvk: fs.readFileSync('/var/www/saml/key.pem', 'utf8'),
    cert: 'MIIDV......'
}, function(profile, done) {
    return done(null, profile);
});

Routes:

app.get('/',
  passport.authenticate('saml', {failureRedirect: '/login/fail'}),
  function(req, res) {
    res.send('Authenticated');
  }
);

app.post('/login/callback',
   passport.authenticate('saml', { failureRedirect: '/login/fail' }),
  function(req, res) {
    res.redirect('/');
  }
);

General error catcher:

app.use(function(err, req, res, next){
    console.log('Express error!');
    console.log("the error: " + JSON.stringify(err));
    next(err);
});

Shibboleth processes the request and a SAMLResponse is present in the header of callback route, /login/callback.

An error is thrown at passport.authenticate('saml', {failureRedirect: '/login/fail'}) in the / route.
Console log:

Express error!
the error: {"message":"Invalid RSAES-OAEP padding."}

Any ideas?

Bug in SAML.prototype.generateInstant

Just a heads up, I don't have a fork so Ill just give you the details.

The method SAML.prototype.generateInstant() has a bug in it - it returns invalid time strings when the current time is within 2 hours of midnight UTC. For example, if it is 23:20 UTC, this method will return a string with the time set to 24:20 (which is not valid). A simple fix is below (does the offset prior to conversion to xml datetime string)

ORG CODE

SAML.prototype.generateInstant = function () {
var date = new Date();
return date.getUTCFullYear() + '-' + ('0' + (date.getUTCMonth()+1)).slice(-2) + '-' + ('0' + date.getUTCDate()).slice(-2) + 'T' + ('0' + (date.getUTCHours()+2)).slice(-2) + ":" + ('0' + date.getUTCMinutes()).slice(-2) + ":" + ('0' + date.getUTCSeconds()).slice(-2) + "Z";
};

FIXED CODE

SAML.prototype.generateInstant = function () {
var currDate = new Date();
var date = new Date();
date.setTime(currDate.getTime() + (2 * 60 * 60 * 1000)) // add 2 hours
return date.getUTCFullYear() + '-' + ('0' + (date.getUTCMonth()+1)).slice(-2) + '-' + ('0' + date.getUTCDate()).slice(-2) + 'T' + ('0' + date.getUTCHours()).slice(-2) + ":" + ('0' + date.getUTCMinutes()).slice(-2) + ":" + ('0' + date.getUTCSeconds()).slice(-2) + "Z";
};

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.