npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

wicked-saml

v0.11.3

Published

node.js SAML SDK for wicked.haufe.io

Downloads

5

Readme

wicked.haufe.io SAML SDK

This library helps implementing Authorization Servers for federating SAML identites (SSO identities) into a wicked.haufe.io OAuth2.0 Implicit Grant Flow API implementation. It assumes your SAML IdP supports the HTTP-POST Binding and encrypted SAML Assertions.

It does the heavy lifting regarding implementing a SAML SP (Service Provider) which talks to the IdP.

You can find more information on wicked.haufe.io here:

Usage

To install the SDK into your node.js application, run

$ npm install wicked-saml --save --save-exact

Please note that you will also need to inject the wicked-sdk when initializing the wicked-saml SDK; check out the wicked-sdk NPM package for more information: npmjs.com/packages/wicked-sdk.

The SDK will be kept downwards-compatible for as long as possible; it will be tried hard to make earlier versions of the SDK compatible with a later release of wicked.haufe.io, so using the --save-exact is a safe bet.

Prerequisites

The wicked-saml package is intended for use with ExpressJS 4.x and wicked.haufe.io. It is not intended for other types of usage.

You SAML IdP needs to support the HTTP-POST-Binding and Encrypted Assertions. In some cases (like OpenAM), assertion encryption has to be explicitly turned on and cannot be part of the metadata.xml. If you receive errors static that EncryptedAssertion lengths is zero but was expected to be one, this may be the mistake.

Example

var wicked = require('wicked-sdk');
var wickedSaml = require('wicked-saml')
var async = require('async'); // another requirement for this sample

async.series([
    callback => wicked.initialize(callback),
    callback => wickedSaml.initialize(wicked, 'your-server-id', callback)
], function (err) {
    if (err)
        throw err; // or do whatever you need
    
    // start server
});

The single most interesting point is the string 'your-server-id' in the above example. It relies on an an Authorization Server being registered in your wicked configuration. The wicked-saml will do the following thing:

  • Retrieve the /auth-server/your-server-id information from the API (that's what the wicked-sdkis needed for)
  • Read out the information in the saml properties and use that to initialize the implementation of the SAML Service Provider with that

More information can be found in the wicked documentation under Authorization Servers.

A sample your-server-id.json file could look like this:

{
  "name": "your-server-id",
  "id": "your-server-id",
  "auth": "none",
  "desc": "Authorization Server for SAML Federation",
  "url": "https://${PORTAL_NETWORK_APIHOST}/auth-server/{{apiId}}?client_id=(your app's client id)",
  "config": {
    "api": {
      "upstream_url": "http://auth-server:3005",
      "request_path": "/auth-server"
    },
    "plugins": [
      {
        "config": {
          "header_name": "Correlation-Id",
          "generator": "uuid"
        },
        "name": "correlation-id"
      }
    ]
  },
  "saml": {
    "spOptions": {
      "entity_id": "https://api.company.com/auth-server/metadata.xml",
      "assert_endpoint": "https://api.company.com/auth-server/assert",
      "nameid_format": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
      "certificate": "-----BEGIN CERTIFICATE-----\nMIIDIDCCA...awot98FReb\n-----END CERTIFICATE-----",
      "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBA...Vy4HpO2KPg==\n-----END RSA PRIVATE KEY-----"
    },
    "idpOptions": {
      "sso_login_url": "https://your-saml-idp.com:443/auth/SSORedirect/metaAlias/idp1",
      "certificates": [
        "-----BEGIN CERTIFICATE-----\nMIICrTCC...g8t2tGs=\n-----END CERTIFICATE-----"
      ]
    }
  }
}

The options which can be used can be found in the documentation of saml2-js, which is the library which is used "under the hood" of the wicked-saml package: npmjs.com/package/saml2-js.

The example properties above (when correctly filled) will work with e.g. OpenAM.

Library description

The following functions are exported by wicked-saml.

wickedSaml.initialize(wicked, serverId, callback)

Initialize the SAML library; calls the wicked API to retrieve information on the Authorization Server registration of the wicked configuration (see above). The serverId has to match the auth-server definition ID.

Pass your wicked-sdk instance to the library here; wicked-saml will use its apiGet function.

Callback signature: function(err) -- Does not return anything but an error, or null if successful.

wickedSaml.metadata()

Returns a function which can be used directly as the metadata.xml end point, when using express.

app.get('/auth-server/metadata.xml', wickedSaml.metadata());

wickedSaml.login(callback)

Create a request identifier and login URL for redirecting to the SAML IdP.

Example:

// Assume /auth-server/:apiId?client_id=3498wzio4e57648576348756345
app.get('/auth-server/:apiId', function (req, res, next) {
    req.session.apiId = req.params.apiId;
    req.session.clientId = req.query.client_id;
    wickedSaml.login(function (err, loginInfo) {
        if (err)
            return next(err);
        req.session.requestId = loginInfo.requestId;
        res.redirect(loginInfo.loginUrl);
    });
});

Note that there is a bunch of validity checking and security measures missing in the above code.

Callback signature': function(err, loginInfo), whereas loginInfo:

loginInfo = {
    loginUrl: 'https://...../idp1',
    requestId: '7hf5irutzerwiutzhw384765h8w47658w4f'
}

Use the loginUrl to redirect to the IdP and store the requestId in your session for checking when you get called back in /assert.

wickedSaml.assert(req, requestId, callback)

Use this function to decrypt a SAML assertion. Call this from the /assert end point you specified in your configuration (spOptions.assert_endpoint):

app.post('/auth-server/assert', function (req, res, next) {
    const requestId = req.session.requestId;
    wickedSaml.assert(req, requestId, function (err, userInfo, samlResponse) {
        if (err)
            return next(err); // More elaborate error handling if needed

        // userInfo will contain "authenticated_userid" property (most of the time)
        // If you need other things, use getAttributeValue() to retrieve from
        // the samlResponse:
        userInfo.authenticated_userid = wickedSaml.getAttributeValue(samlResponse, 'our_company_id');

        // Fill in the other values for use with the Kong Adapter, stored
        // in session (see login())
        userInfo.api_id = req.session.apiId;
        userInfo.client_id = req.session.clientId;

        // In case you need to do some authorization step (this is only authentication),
        // this is the place to do that, e.g. check for licenses for the authenticated
        // user, which could be passed on as OAuth2 scopes:
        userInfo.scope = ['some_scope', 'other_scope'];

        wicked.getRedirectUriWithAccessToken(userInfo, function (err, redirect) {
            if (err)
                return next(err);
            // Yay, done! Redirect back to web app
            res.redirect(redirect.redirect_uri);
        })
    });
});

Callback signature: function(err, userInfo, samlResponse)

The userInfo looks as follows:

userInfo = {
    authenticated_userid: "some-id-we-found"
}

wickedSaml.assert will try to extract these two values (as needed for getRedirectUriWithAccessToken), but cannot guarantee it will work out. Additionally, if you have multiple fields in your SAML response which ends with id, any one will be picked. So it's recommended that you explicitly set those values manually using the samlResponse and the getAttributeValue() function (see below).

wickedSaml.getAttributeNames(samlResponse)

Lists all attribute names of the user tag of the given SAML response (samlRespose). Takes the SAML response from assert() as an argument and returns a string array.

Note: The attribute names will be converted to lower case.

wickedSaml.getAttributeValue(samlResponse, wantedAttribute)

Retrieve the value of an attribute in the samlResponse. The wantedAttribute parameter is not case-sensitive. If the attribute cannot be found, null is returned.

wickedSaml.getConfig()

Returns the configuration object the SAML SDK retrieved from the wicked API (e.g., the auth-saml.json settings from the auth-servers configuration of your API portal).

wickedSaml.getLogoutResponseUrl(inResponseTo, relayState, callback)

To be written.