2.0.5 • Published 2 years ago

passport-ci-oidc v2.0.5

Weekly downloads
8,963
License
MIT
Repository
-
Last release
2 years ago

No Maintenance Intended

passport-ci-oidc

Deprecated Notice:

This module is no longer maintained and will be removed soon. Please use other OpenID Connect standard compliant modules (for example, openid-client.)

This module provides the passport strategy for authenticating specifically with the IBM Security Verify (ISV) service.

Install

You may install the package using npm install command:

npm install passport-ci-oidc

Uninstall

To uninstall passport-ci-oidc from your system, use the npm uninstall command:

npm uninstall passport-ci-oidc

Or just delete the passport-ci-oidc directory.

Change History

  • 2.0.5
    • update node-jose to 2.1.0 to address a security advisory and bug fix.
  • 2.0.4
    • update node-jose to 2.0.0 to address a security advisory and bug fix.
  • 2.0.3
    • Published in NPM.

Usage

Example

Below is a simple example of what is used to configure and use the strategy.

Note 1: https://www.discoveryendpoint.com/someendpointpathhere is a sample discovery endpoint url.

Note 2: https://1.2.3.4:443/auth/sso/callback is a sample callback url.

const clientId = 'clientID';
const clientSecret = 'clientSecret';
const discoveryUrl = 'https://www.discoveryendpoint.com/someendpointpathhere';
const callbackUrl = 'https://1.2.3.4:443/auth/sso/callback';

const OpenIDConnectStrategy = require('passport-ci-oidc').IDaaSOIDCStrategy;
const Strategy = new OpenIDConnectStrategy({
                discoveryURL : discoveryUrl,
                clientID : clientId,
                scope : 'email',
                response_type : 'code',
                clientSecret : clientSecret,
                callbackURL : callbackUrl,
                skipUserProfile : true
      },
      (iss, sub, profile, accessToken, refreshToken, params, done) => {
        process.nextTick(() => {
            profile.accessToken = accessToken;
            profile.refreshToken = refreshToken;
            done(null, profile);
        });
      }
);

passport.use(Strategy); 

app.get('/auth/sso/callback', (req,res,next) => {
    const redirect_url = req.session.originalUrl;
    passport.authenticate('openidconnect', {
            successRedirect: redirect_url,
            failureRedirect: '/failure',
    })(req,res,next);
});

app.get('/failure', (req, res) => res.send('login failed'));
app.get('/login', passport.authenticate('openidconnect', {})); 

const ensureAuthenticated = (req, res, next) => {
  if(!req.isAuthenticated()) {
    req.session.originalUrl = req.originalUrl;
    res.redirect('/login');
  } else {
    return next();
  }
}

app.get('/hello', ensureAuthenticated, (req, res) => res.send('Hello, '+ req.user['id'] + '!'));

Configure Strategy

The strategy authenticates users using the Cloud Identity (CI) service, which includes various credentials required by the strategy, most of which are already provided by the service. Included are the client id, client secret, and discovery endpoint.

const clientId = PUT_CLIENT_ID_HERE;
const clientSecret = PUT_CLIENT_SECRET_HERE;
const discoveryUrl = PUT_DISCOVERY_ENDPOINT_URL_HERE;
const callbackUrl = PUT_CALLBACK_URL_HERE;

const OpenIDConnectStrategy = require('passport-ci-oidc').IDaaSOIDCStrategy;
const Strategy = new OpenIDConnectStrategy({
              discoveryURL : discoveryUrl,
              clientID : clientId,
              scope : 'email',
              response_type : 'code',
              clientSecret : clientSecret,
              callbackURL : callbackUrl,
              skipUserProfile : true
    },
    // This is the verify callback
    (iss, sub, profile, accessToken, refreshToken, params, done) => {
        process.nextTick(()  => {
            profile.accessToken = accessToken;
            profile.refreshToken = refreshToken;
            done(null, profile);
        });
    }
);

passport.use(Strategy);

Including Local Certificates

There is an additional option in the strategy configuration, namely using specified local certificates. This option is optional as RSASSA (RS) and ECDSA (ES) based algorithms requiring the PEM encoded public key/signing certificate should be provided through the JWKS endpoint obtained from the Discovery endpoint.

However, if the user wishes to provide the certificate manually for verification instead, this can be done by enabling this option and adding the said signing certificate as a local certificate, as explained below.

You can configure the strategy to specify one or more local certificates that you want to use when requesting the access token, and subsequently the ID token. To specify a local certificate: a. Set the attribute addCAcert to true. By default, the attribute is false. b. Set the attribute CACertPathList to provide a path list to your certificates. Note that if addCAcert is false, CACertPathList is ignored.

For example:

...
issuer: issuerId,
addCACert: true,
CACertPathList: [‘/example.crt’, ‘/example2.cer’]},
...

Include your certificates in the application directory hierarchy so that they can be read when the strategy is created. When listing your certificates, specify the location relative to the application directory. For example, if your certificate example1.crt is in the application directory, list it as ‘/example1.crt’. If it is located within a subdirectory of the application directory, such as ssl, list it as ‘/ssl/example1.crt’

Callback URL

The callback URL is a requirement for the strategy.

The callback URL is the URL for the application that consumes the authentication tokens and retrieves the user profile. For example: https://1.2.3.4:443/auth/sso/callback

Code for the callback function is also required to specify what the app does after a user logs in. Using the example mentioned above, if the callback URL is https://1.2.3.4:443/auth/sso/callback, ensure that the callback URI you specify for app.get is auth/sso/callback.

The path to the resource that was originally requested is stored in the req.session.originalUrl property.

The following example shows a callback function that redirects users to the page they originally requested before they logged in. If the login fails, users are directed to the /failure page.

app.get('/auth/sso/callback', (req,res,next) => {
    const redirectUrl = req.session.originalUrl;
    passport.authenticate('openidconnect', {
            successRedirect: redirectUrl,
            failureRedirect: '/failure',
    })(req,res,next);
});

app.get('/failure', (req, res) => res.send('login failed'));

Verify Callback

The strategy requires a verify callback, which accepts various types of parameters.

By default, these are possible parameters for the verify callback:

  • function (iss, sub, profile, jwtClaims, accessToken, refreshToken, params, done)
  • function (iss, sub, profile, accessToken, refreshToken, params, done)
  • function (iss, sub, profile, accessToken, refreshToken, done)
  • function (iss, sub, profile, done)
  • function (iss, sub, done)

There is an optional attribute called passReqToCallback that can be added to the strategy in order to pass the request to the verify callback. This can be done by adding it in the strategy:

...
skipUserProfile : true,
issuer : issuerId,
passReqToCallback : true},
...

Doing so will result in the same types of callbacks listed above, except each callback will be appended with the request in front:

  • function (req, iss, sub, profile, jwtClaims, accessToken, refreshToken, params, done)
  • function (req, iss, sub, profile, accessToken, refreshToken, params, done)
  • function (req, iss, sub, profile, accessToken, refreshToken, done)
  • function (req, iss, sub, profile, done)
  • function (req, iss, sub, done)

ensureAuthenticated() and /login route

The ensureAuthenticated() method and /login route are also required in the app.

ensureAuthenticated() checks if the user is already authenticated. If not, the method then stores the current url as the original url that calls the authentication request. It then redirects to the /login route, which will then start the authentication process using the configured strategy.

app.get('/login', passport.authenticate('openidconnect', {})); 

const ensureAuthenticated = (req, res, next) => {
  if(!req.isAuthenticated()) {
      req.session.originalUrl = req.originalUrl;
      res.redirect('/login');
  } else {
    return next();
  }
}

To use ensureAuthenticated(), please include the method in the route to your app, such as the following test example:

app.get('/hello', ensureAuthenticated, (req, res) => res.send('Hello, '+ req.user['id'] + '!'));

Test Sample

Note: Be sure that your Bluemix SSO service has been properly set up.

To test to make sure your app works properly with the Bluemix Single Sign-On service, include the following code in your app:

app.get('/hello', ensureAuthenticated, (req, res) => res.send('Hello, '+ req.user['id'] + '!'));

After including this code, try going to the /hello route for your app after it has been successfully deployed to Bluemix.

For example: https://myapp.mybluemix.net/hello

Contact Email

Please use this email for contact if you have questions: Security_SSO_Operations@wwpdl.vnet.ibm.com

Contributors

License

MIT License

Copyright (c) 2013 Jared Hanson

Copyright (c) 2010-2012 Ciaran Jessup

Copyright (c) 2019, 2020, 2021, 2022 IBM

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.