Okta Node.js OIDC Middleware
raw JSON →The `@okta/oidc-middleware` package provides an OpenID Connect middleware for Express.js applications, simplifying the integration of Okta's authorization code flow. It handles redirecting users to Okta for authentication, processing the callback, and establishing a local session to store user context. The library currently maintains a stable major version series of 2.x, with the latest release being 4.2.0, following semantic versioning and Okta's library version policy. A key differentiator is its seamless integration with Express, managing OIDC complexities like token exchange and session maintenance, while relying on `express-session` for local session storage. It's designed to quickly enable secure authentication for Node.js web applications, abstracting away much of the underlying OIDC protocol details.
Common errors
error Error: secret must be set for session middleware ↓
express-session. Use environment variables to manage it: secret: process.env.SESSION_SECRET. error TypeError: Cannot read properties of undefined (reading 'userContext') ↓
app.use(oidc.router) is called, and that express-session middleware is registered *before* oidc.router. Verify that the user has successfully logged in via Okta and completed the OIDC redirect flow. error No KID specified ↓
error Error: Not found: /login (or any configured OIDC endpoint) ↓
app.use(oidc.router) is called in your Express application. Check your appBaseUrl configuration to ensure it matches your application's base URL and redirect URIs in Okta. Warnings
breaking The `@okta/jwt-verifier` dependency (v2.0.0 and higher) will now throw an error "No KID specified" if a JWT token lacks a 'kid' (Key ID) header. This is a breaking change for applications that receive tokens without 'kid' headers. ↓
deprecated Versions 0.x and 1.x of `@okta/oidc-middleware` are considered deprecated or retired and should not be used in new projects or production environments due to potential security vulnerabilities and lack of maintenance. ↓
gotcha The default `MemoryStore` provided by `express-session` is explicitly not designed for production use. Using it in production can lead to session loss on server restarts, scaling issues, and potential security vulnerabilities. ↓
gotcha Older versions of `@okta/jwt-verifier` and its sub-dependencies, such as `jwks-rsa`, have contained security vulnerabilities that were addressed in later patches. Running outdated versions can expose your application to known exploits. ↓
breaking The `onSessionExpired` behavior in `@okta/okta-react` (part of the same monorepo) was removed in version 3.0.4. While not directly `oidc-middleware`, it indicates a pattern of changes in how session expiration is handled across Okta libraries. Developers should review their session management strategy. ↓
Install
npm install okta-oidc-middleware yarn add okta-oidc-middleware pnpm add okta-oidc-middleware Imports
- ExpressOIDC wrong
const ExpressOIDC = require('@okta/oidc-middleware').ExpressOIDC;correctconst { ExpressOIDC } = require('@okta/oidc-middleware'); - ExpressOIDC wrong
import ExpressOIDC from '@okta/oidc-middleware';correctimport { ExpressOIDC } from '@okta/oidc-middleware'; - OktaOIDCAuthOptions
import type { OktaOIDCAuthOptions } from '@okta/oidc-middleware';
Quickstart
const express = require('express');
const session = require('express-session');
const { ExpressOIDC } = require('@okta/oidc-middleware');
const app = express();
const oidc = new ExpressOIDC({
issuer: process.env.OKTA_ISSUER || 'https://{yourOktaDomain}/oauth2/default',
client_id: process.env.OKTA_CLIENT_ID || '{clientId}',
client_secret: process.env.OKTA_CLIENT_SECRET || '{clientSecret}',
appBaseUrl: process.env.OKTA_APP_BASE_URL || 'http://localhost:8080',
scope: 'openid profile'
});
app.use(session({
secret: process.env.SESSION_SECRET || 'a-very-long-random-string-that-you-should-change',
resave: false,
saveUninitialized: false
// For production, replace MemoryStore with a persistent store (e.g., connect-redis)
}));
app.use(oidc.router);
app.get('/', (req, res) => {
if (req.userContext) {
res.send(`
Hello ${req.userContext.userinfo.name}!
<form method="POST" action="/logout">
<button type="submit">Logout</button>
</form>
`);
} else {
res.send('Please <a href="/login">login</a>');
}
});
app.get('/protected', oidc.ensureAuthenticated(), (req, res) => {
res.send('This is a protected page. Welcome, ' + req.userContext.userinfo.name);
});
const port = process.env.PORT || 8080;
oidc.on('ready', () => {
app.listen(port, () => console.log(`App has started on port ${port}`));
});
oidc.on('error', err => {
console.error('OIDC error: ', err);
});