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

@financial-times/okta-express-middleware

v1.0.7

Published

FT okta express middleware to verify jwt

Downloads

7,601

Readme

Okta Express Middleware

CircleCI

This middleware builds on top of Okta's middleware and JWT verifier in order to provide a fully capable but simple to integrate OIDC authentication to Express based apps. It will verify the access token and ID token (including expiry). If either of these are not valid it will either start the refresh token flow or the authorization code flow.

You should be using cookie-session or express-session. Our recommendation is to use cookie-session unless the application is already utilising express-session.

Why use this instead of Okta's library

Okta's library never checks the expiration of the tokens inside the session. Access tokens are valid for an hour and refresh tokens are valid for 12 hours so the cookie that underpins the cookie-session has a TTL of 12 hours. When you have a cookie with an expired access token and valid refresh token, Okta's library lets you in without initiating refresh token flow flow. You could also exploit cookie-session by modifying the cookie TTL in browser. In this exploit scenario, Okta's library lets you in with an expired access token and expired refresh token. In express-session, the TTL of the session could be increased with each request.

We built this library on top of Okta's library and added functionality to verify tokens in the session on every request.

Prerequisites

The application will require a pair of client id and client secret. Both are obtained when adding the app configuration to Okta. Please follow the Integration Guide to add your application as an OAuth 2.0 app, also referred to as web_oidc within the config.

How to use

Install the library

npm install @financial-times/okta-express-middleware

Import the class

const OktaMiddleware = require('@financial-times/okta-express-middleware');

Set up cookie-session or express-session.

The syntax for specifying cookie properties (e.g. maxAge, httpOnly etc) is different for express-session and cookie-session. Please ensure you are using the right syntax.

In the examples below, SuperSecretValueHere must be a randomly generated, securely stored key e.g. generate a UUID and store in vault with your other environment variables.

Cookie Session

cookie-session is a simple session store for keeping small amounts of session data.

const session = require('cookie-session');

app.use(session({
  secret: 'SuperSecretValueHere',
  maxAge: 12 * 3600 * 1000, // 12 hours
  httpOnly: true,
  secure: true
}));

If you are using Heroku or your server is not directly serving requests over a TLS connection (e.g. an EC2 instance serving http traffic and fronted by an ELB which terminates TLS), add the below so the cookie's secure flag is set correctly. Ref:Express JS Behind Proxies

app.enable('trust proxy');

Express Session

express-session uses a data store like Redis for storing larger sessions. It might be more complex to set up.

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

app.use(session({
  secret: 'SuperSecretValueHere',
  cookie: {
    maxAge: 12 * 3600 * 1000, // 12 hours
    httpOnly: true,
    secure: true
  }
}));

Instantiate the class

const okta = new OktaMiddleware({
  client_id: 'dummyClientId',
  client_secret: 'dummyClientSecret',
  issuer: 'https://foo.okta.com/dummy-issuer',
  appBaseUrl: 'https://foo.ft.com',
  scope: 'openid name offline_access',
});

Use the router and the two middleware functions:

app.use(okta.router);
app.use(okta.ensureAuthenticated());
app.use(okta.verifyJwts());

Routes declared before this block are not protected by Okta and routes declared after the block are protected by Okta.

// This route is not protected by Okta
app.get('/__gtg', (req, res) => {
  res.send('Good to go 👍');
});

app.use(okta.router);
app.use(okta.ensureAuthenticated());
app.use(okta.verifyJwts());

// This route is protected by Okta
app.use('/', landing);

If static assets used in your application do not contain secure information, we recommend you declare static assets before calling the middleware block. This minimizes Okta login attempts during refresh token flow.

// Static assets are not protected by Okta
app.use(express.static('public'));

app.use(okta.router);
app.use(okta.ensureAuthenticated());
app.use(okta.verifyJwts());

Wait for express-middleware to initialise

The middleware must retrieve some information about your client before starting the server. You must wait until it is ready before starting your server.

The onReady() method in the OktaMiddleware class has been introduced for this purpose.

okta.onReady(() => {
  app.listen(port, () => console.log(`The app has started!`));
});

There is also an optional onError() method for error handling. Errors could happen during the startup process or during logout.

okta.onError((err) => {
  // An error has happened
});

Express Router

Using the express router with the OktaMiddleware requires the following additional configuration:

  • Add a path declaration to the cookie session configuration
app.use(session({
	...
	path: '/path-for-router-module',
}));
  • Configure the express router to use the OktaMiddleware and cookie-session
app.use(okta.router); // keep 'okta.router' as 'app.use'
router.use(cookieParser());
router.use(okta.ensureAuthenticated());
router.use(okta.verifyJwts());
  • Finally load the express router module into the app after the OktaMiddleware configuration
app.use('/path-for-router-module', router);

Config Options

Required

Optional

  • routes.loginCallback.path - Where Okta will callback to after the user has authenticated. Default: /authorization-code/callback

    if appBaseUrl is https://foo.ft.com, Okta express middleware constructs the redirect URL as https://foo.ft.com/authorization-code/callback. This exact URL needs to be defined as the redirect URI(s) in your Okta Application. If you instead prefer the redirect URL to be a different value e.g. https://foo.ft.com/callback, you have to override the login callback route by providing /callback as the value for routes.loginCallback.path; and update the redirect URI in your Okta application.

  • routes.login.path - Where in your application the middleware will redirect to if the user is unauthenticated. Default: /login

Scopes

Scopes allow you to request additional information about the user (See Okta's blog post)

These are the scopes our Okta servers currently support:

  • (REQUIRED) openid - Returns an ID Token and Access Token from Okta.
  • (RECOMMENDED) offline_access - Returns a Refresh Token from Okta which will be automatically used by the middleware. See Okta's blog post
  • name - Returns the user's first and last name in req.userContext.
  • email - Returns the user's email address in req.userContext.
  • groups - This scope returns the AD and Okta groups the user is a member of in req.userContext. This only returns groups that have been assigned to the application.

Examples

app.use(okta.verifyJwts());

Or

app.get('/behindOkta', okta.verifyJwts(), (req, res) => {
  res.send('Authenticated');
});

If you do not want the user to be redirected to Okta when they are not logged in, you can do...

app.use(okta.verifyJwts({redirect: false}));

Or

app.get('/behindOkta', okta.verifyJwts({redirect: false}), (req, res) => {
  res.send('Authenticated');
});

To obtain user info, you can extract it from req.userContext For example, to obtain the user email (only when using email scope, see above):

app.get('/behindOkta', okta.verifyJwts(), (req, res) => {
  res.send('user email is: ' + req.userContext.userinfo.email);
});

Also see an example at test/app.js

Development

  1. Create a branch in this repository, clone it, and make your changes.
  2. In your application or our handy test application, run npm install git+ssh://[email protected]/Financial-Times/okta-express-middleware.git#{INSERT_BRANCH_NAME_HERE}.
  3. Check that your changes do what you want them to do.
  4. Add tests to test/app.test.js
  5. Raise a PR.

We also have some tests that can be run with npm run test or npm run coverage.

Manual regression testing

End to end regression testing is useful to test external changes such as dependency version updates or backend (Okta) changes.

These are the steps to test the functionality end to end:

  1. Copy .env.example to .env file and update it with values of Node example app in ft-dev.oktapreview.com
  2. Run npm install
  3. Run npm start
  4. Open Incognito Mode in your browser and go to http://localhost:3000
  5. Log in using Okta/GSuite
  6. If you see Authenticated as... on the page then the initial login is successful
  7. Refresh the page after the initial access token expires as indicated on the page
  8. If you see New access token was obtained using refresh token then refresh token flow is successful too