passport-curity
TypeScript icon, indicating that this package has built-in type declarations

1.1.1 • Public • Published

passport-curity

Quality Availability

passport-curity is a strategy for the passport library which enables an effortless integration with the Curity Identity Server.

It covers different scenarios of authenticating a user with the Curity Identity Server using the Open ID Connect protocol. If you're looking for an option to secure your endpoints in an Express API app with JWTs have a look at this library: express-oauth-jwt.

Installation

You can install the strategy with npm.

npm install passport-curity

Usage

In order to properly use the strategy you need to provide it with an openid connect client, the parameters that should be used with the authorization request as well as a verify callback, which you can use to process the incoming tokens and profile information. (See passport documentation to read more about the verify callback.)

Configure the client

You can use a convenience method discoverAndCreateClient to quickly setup the oidc client needed by this strategy. The method returns a Promise.

discoverAndCreateClient({
    issuerUrl: 'https://example.com',
    clientID: "my-client",
    clientSecret: "S3cret!",
    redirectUris: ["http://localhost:3000/callback"],
    responseTypes: ['code', 'token']
}).then((client) => {
    // Create strategy, configure and start the Express app.
});
  • issuerUrl is the URL to your instance of the Curity Identity Server. This address is used to access the .well-known endpoints exposed by the Curity Identity Server in order to obtain all the necessary configuration of the different OAuth and OpenID Connect endpoints.
  • clientID, clientSecret and redirectUris should be filled with data used by your client registered with the Curity Identity Server.
  • responseTypes parameters tells the client which authorization flows will be used by the strategy. This parameter is optional and defaults to ["code"].

If you rather have more control on creating the client have a look at the openid-client documentation to check the available options.

The verify callback

Each passport strategy accepts a verify callback which can be used to properly handle incoming authorization credentials in the Express app. E.g. create a new user account, save tokens to a database, etc. The verify callback used by the passport-curity strategy has the following signature:

function(accessToken, refreshToken, profile, callback) {
    callback(null, profile); // The callback function needs to be called from the verify callback.
}
  • accessToken and refreshToken are strings containing the respective tokens, or null if the given token was not returned by the given flow.
  • profile contains a map of all the claims present in the ID token. If the ID token was not returned in the response the strategy can fall back to requesting this data from the userinfo endpoint. See Customization to check how this can be enabled.

Authorization code flow

Here's a minimal example showing passport configured with the Curity strategy using the authorization code flow:

const express = require('express');

const app = express();
const passport = require('passport');
const CurityStrategy = require('passport-curity').Strategy;
const discoverAndCreateClient = require('passport-curity').discoverAndCreateClient;

const expressSession = require('express-session');

const session = {
    secret: "someSecret",
    cookie: {},
    resave: false,
    saveUninitialized: false
  };

(async () => {
    // Create the oidc client
    const client = await discoverAndCreateClient({
        issuerUrl: 'https://example.com',
        clientID: "my-client",
        clientSecret: "S3cret!",
        redirectUris: ["http://localhost:3000/callback"]
    });

    // Create the Curity Strategy object
    const strategy = new CurityStrategy({
        client,
        params: {
            scope: "openid"
        }
    }, function(accessToken, refreshToken, profile, cb) {
        cb(null, profile);
    });

    // Initialize the passport middleware
    passport.use(strategy);

    passport.serializeUser((user, done) => {
        done(null, user);
    });

    passport.deserializeUser((user, done) => {
        done(null, user);
    });

    app.use(expressSession(session));

    app.use(passport.initialize());
    app.use(passport.session());

    // The /authorize endpoint initializes the authorization process
    app.get('/authorize', passport.authenticate('curity'));

    // The callback endpoint is where the Curity Identity Server redirects the user back after positive authentication
    app.get('/callback', passport.authenticate('curity', { failureRedirect: '/failed', failureMessage: true }), function(req, res) {
        res.redirect('/');
    });

    // When a user authenticates and authorizes your app you will be able to access their data in the request object.
    app.get('/', function(req, res) {
        res.json({ profile: req.user });
    });

    app.get('/failed', function(req, res) {
        res.json({ message: "Auth failed: " + req.session.messages[0] });
    });

    app.listen(3000, function() {
        console.log("Server started on port 3000");
    });
})();

Implicit flow

In order to use the implicit flow instead of the code flow you need to change a few lines in the example above:

  1. Configure the client with appropriate response types:
const client = await discoverAndCreateClient({
        issuerUrl: 'https://example.com',
        clientID: "my-client",
        clientSecret: "S3cret!",
        redirectUris: ["http://localhost:3000/callback"],
        responseTypes: ["token"]
    });

Note: If you only need the ID token you can instead use the response type id_token.

  1. Set the response_mode for the strategy to form_post so that the tokens are posted back to the callback endpoint, not sent in the fragment part of the url.
const strategy = new CurityStrategy({
        client,
        params: {
            scope: "openid",
            response_mode: "form_post"
        }
    }, function(accessToken, refreshToken, profile, cb) {
        ...
    });

Note: If you configure the client with more response types then you have to add a response_type parameter to the params map.

  1. Configure the callback endpoint to accept POST requests and to properly parse the body of the request:
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));

app.post('/auth/example/callback', passport.authenticate('curity', { failureRedirect: '/failed', failureMessage: true}), function(req, res) {
        res.redirect('/');
    });

Obtaining both: ID token and access token

It's possible to obtain both the ID token and the access token in one request in an implicit flow. To do that just provide both response types together as one value:

const client = await discoverAndCreateClient({
        issuerUrl: 'https://example.com',
        clientID: "my-client",
        clientSecret: "S3cret!",
        redirectUris: ["http://localhost:3000/callback"],
        responseTypes: ["token id_token"]
    });

Customization

userinfo fallback

The user profile data will normally be decoded from the ID token. However, if the ID token is not present in the response the strategy can fall back to requesting the userinfo endpoint of the Curity Identity Server. By default this option is disabled. If you want to enable it add fallbackToUserInfoRequest: true to the strategy options:

const strategy = new CurityStrategy({
    client,
    params: {
        scope: "openid"
    },
    fallbackToUserInfoRequest: true
}, function(accessToken, refreshToken, profile, cb) {
    ...
});

Setting authorization parameters

The params map in the Curity strategy options is a map of the parameters that are sent together with the authorization request. E.g. you can set the prompt parameter to use the feature as described in the OpenID Connect protocol. So if you add to the params map prompt: "login", then you will always see a login page regardless of the user being logged in or not.

Another popular parameter you can use is the scope parameter, where you can provide a string with space-delimited scope tokens - the ones that your app need in order to properly access the user's resources. E.g. scope: "openid profile email".

Below is the list of all the parameters that can be set for requests made by this strategy:

  • acr_values
  • audience
  • claims.id_token
  • claims.userinfo
  • claims_locales
  • client_id
  • code_challenge_method
  • code_challenge
  • display
  • id_token_hint
  • login_hint
  • max_age
  • nonce
  • prompt
  • redirect_uri
  • registration
  • request_uri
  • request
  • resource
  • response_mode
  • response_type
  • scope
  • ui_locales

Other customization

The Curity passport strategy is based on the openid-client library written by Filip Skokan. If you need some more customization and fine-grained tuning you can have a look the openid-client documentation.

Questions and Support

For questions and support, contact Curity AB:

Curity AB

info@curity.io

https://curity.io

Copyright (C) 2020 Curity AB.

Package Sidebar

Install

npm i passport-curity

Weekly Downloads

113

Version

1.1.1

License

Apache-2.0

Unpacked Size

30.9 kB

Total Files

8

Last publish

Collaborators

  • d.lindau
  • parasjoshi24
  • surencurity
  • curity-admin
  • anestos
  • mtrojanowski
  • ideskog