express-openid-connect and oidc-provider are both essential libraries for working with OpenID Connect (OIDC) in Node.js, but they serve opposite ends of the authentication flow. express-openid-connect acts as a client (Relying Party) that integrates login functionality into an existing web application using an external identity provider. oidc-provider acts as a server (OpenID Provider) that issues tokens and manages user identities, effectively allowing you to build your own authentication service like Auth0 or Okta.
Both express-openid-connect and oidc-provider are mature libraries for handling OpenID Connect (OIDC) in Node.js environments, but they solve fundamentally different problems. One is designed to consume authentication, while the other is designed to provide it. Understanding this distinction is critical for architecting secure systems.
express-openid-connect acts as a Relying Party (RP).
// express-openid-connect: Configuring the client
const { auth } = require('express-openid-connect');
app.use(auth({
auth0: {
domain: 'your-tenant.auth0.com',
clientId: 'your-client-id'
},
baseURL: 'http://localhost:3000',
secret: 'long-secret-string'
}));
oidc-provider acts as an OpenID Provider (OP).
// oidc-provider: Configuring the server
const Provider = require('oidc-provider');
const provider = new Provider('http://localhost:3000', {
findAccount: async (ctx, id) => {
// Logic to find user in your database
return { accountId: id };
},
scopes: ['openid', 'profile', 'email']
});
app.use(provider.callback);
express-openid-connect favors convention over configuration.
// express-openid-connect: Minimal setup
app.use(auth({
issuerBaseURL: 'https://your-idp.com',
baseURL: 'https://your-app.com',
clientID: 'client-id',
secret: 'secret'
}));
// Routes are protected automatically with authRequired
oidc-provider requires explicit definition of almost every behavior.
// oidc-provider: Explicit interaction policy
const policy = Provider.interactionPolicy.base();
policy.add(new Provider.interactionPolicy.Prompt({
name: 'login',
requestable: true
}));
const provider = new Provider('http://localhost:3000', {
interactions: { policy }
});
express-openid-connect provides middleware to guard your routes.
// express-openid-connect: Protecting a route
app.get('/dashboard', authRequired, (req, res) => {
res.send(`Hello ${req.oidc.user.name}`);
});
app.get('/login', (req, res) => {
res.oidc.login({ returnTo: '/dashboard' });
});
oidc-provider mounts to your app to handle incoming auth requests.
// oidc-provider: Mounting the provider
// In your Express app
app.use('/oidc', provider.callback);
// You still need to build your own login form UI
app.get('/login', (req, res) => {
res.send('<form>...</form>'); // Custom UI implementation
});
express-openid-connect manages the user session for your app.
req.oidc.user without extra code.// express-openid-connect: Accessing session
app.get('/profile', authRequired, (req, res) => {
// Session is automatically populated
const user = req.oidc.user;
res.json(user);
});
oidc-provider expects you to manage sessions for the Identity Server.
findAccount and session persistence logic.// oidc-provider: Implementing account lookup
const config = {
findAccount: async (ctx, id, token) => {
// You must fetch from your DB
const user = await db.users.findById(id);
return {
accountId: user.id,
claims: () => ({ sub: user.id, name: user.name })
};
}
};
While their roles differ, both libraries share underlying standards and dependencies.
openid, profile, and email.// Both support standard scopes
// express-openid-connect config
{ scopes: ['openid', 'profile'] }
// oidc-provider config
{ scopes: ['openid', 'profile'] }
// Both integrate with Express
const express = require('express');
const app = express();
// Works with either package
app.use(express.json());
// express-openid-connect: Secure cookies
{ session: { cookie: { secure: true } } }
// oidc-provider: Secure cookies
{ cookies: { secure: true } }
// express-openid-connect: After login hook
{ afterCallback: (req, res, session, state) => {
session.myCustomClaim = 'value';
}}
// oidc-provider: Extra claims
{ claims: { extra: ['myCustomClaim'] } }
// Both follow semantic versioning
// Check package.json for latest versions
| Feature | Shared by Both |
|---|---|
| Protocol | π OpenID Connect & OAuth 2.0 |
| Runtime | π Node.js & Express |
| Security | π‘οΈ HTTPS & Encryption support |
| Extensibility | π§ Custom claims & hooks |
| Maintenance | π¦ Active community support |
| Feature | express-openid-connect | oidc-provider |
|---|---|---|
| Role | π§βπ» Client (Relying Party) | π’ Server (Identity Provider) |
| Setup Time | β‘ Minutes (Hosted IdP) | π’ Days/Weeks (Self-hosted) |
| User Storage | π« External (Auth0, etc.) | πΎ Your Database |
| UI Control | π¨ Hosted Login Pages | ποΈ Fully Custom Login UI |
| Responsibility | β Low (Vendor manages security) | β οΈ High (You manage security) |
express-openid-connect is like renting a secure apartment π’βeverything is maintained for you, and you just move in. Ideal for startups, SaaS products, and teams that want to ship features without becoming security experts.
oidc-provider is like building your own house from the ground up ποΈβyou control every brick, but you are responsible for the roof not leaking. Best for enterprises with strict compliance needs, legacy user databases, or those building an identity platform as a product.
Final Thought: Do not build your own identity server unless you have to. Start with express-openid-connect and a managed provider. Only reach for oidc-provider when business requirements demand full ownership of the authentication flow.
Choose express-openid-connect if you are building a standard web application and need to add user login quickly using an existing identity provider like Auth0, Okta, or Azure AD. It handles the complex OIDC callback exchanges and session management for you, letting you focus on your app's features rather than security protocols. This is the ideal starting point for most teams who do not need to manage user credentials directly.
Choose oidc-provider if you need to build your own identity server to issue tokens for multiple applications or devices. It is the right choice when you require full control over user storage, login UI, token issuance rules, and compliance with specific OIDC certification requirements. Use this only if you have the security expertise to maintain an authentication server, as it shifts the burden of safety onto your team.

π Documentation - π Getting Started - π» API Reference - π¬ Feedback
This library supports the following tooling versions:
^10.19.0 || >=12.0.0Using npm in your project directory, run the following command:
npm install express-openid-connect
Follow our Secure Local Development guide to ensure that applications using this library are running over secure channels (HTTPS URLs). Applications using this library without HTTPS may experience "invalid state" errors.
Create a Regular Web Application in the Auth0 Dashboard.
If you're using an existing application, verify that you have configured the following settings in your Regular Web Application:
- Click on the "Settings" tab of your application's page.
- Ensure that "Authentication Methods" setting in the "Credentials" tab is set to "None"
- Scroll down and click on the "Show Advanced Settings" link.
- Under "Advanced Settings", click on the "OAuth" tab.
- Ensure that "JsonWebToken Signature Algorithm" is set to
RS256and that "OIDC Conformant" is enabled. Next, configure the following URLs for your application under the "Application URIs" section of the "Settings" page:
http://localhost:3000http://localhost:3000These URLs should reflect the origins that your application is running on. Allowed Callback URLs may also include a path, depending on where you're handling the callback (see below).
Take note of the Client ID and Domain values under the "Basic Information" section. You'll need these values in the next step.
The library needs issuerBaseURL, baseURL, clientID and secret to request and accept authentication. These can be configured with environmental variables:
ISSUER_BASE_URL=https://YOUR_DOMAIN
CLIENT_ID=YOUR_CLIENT_ID
BASE_URL=https://YOUR_APPLICATION_ROOT_URL
SECRET=LONG_RANDOM_VALUE
... or in the library initialization:
// index.js
const { auth } = require('express-openid-connect');
app.use(
auth({
issuerBaseURL: 'https://YOUR_DOMAIN',
baseURL: 'https://YOUR_APPLICATION_ROOT_URL',
clientID: 'YOUR_CLIENT_ID',
secret: 'LONG_RANDOM_STRING',
idpLogout: true,
})
);
With this basic configuration, your application will require authentication for all routes and store the user identity in an encrypted and signed cookie.
Errors raised by this library are handled by the default Express error handler which, in the interests of security, does not include the stack trace or error message in the production environment. If you write your own error handler, you should not render the error message or the OAuth error/error_description properties without using a templating engine that will properly escape them first.
To write your own error handler, see the Express documentation on writing Custom error handlers.
For other comprehensive examples such as route-specific authentication, custom application session handling, requesting and using access tokens for external APIs, and more, see the EXAMPLES.md document.
See the examples for route-specific authentication, custom application session handling, requesting and using access tokens for external APIs, and more.
genidIf you create your own session id when using Custom Session Stores by overriding the genid configuration, you must use a suitable cryptographically strong random value of sufficient size to prevent collisions and reduce the ability to hijack a session by guessing the session ID.
Explore the express-openid-connect API.
Provided middleware:
We appreciate feedback and contribution to this repo! Before you get started, please see the following:
To provide feedback or report a bug, please raise an issue on our issue tracker.
Please do not report security vulnerabilities on the public GitHub issue tracker. The Responsible Disclosure Program details the procedure for disclosing security issues.
Auth0 is an easy to implement, adaptable authentication and authorization platform. To learn more checkout Why Auth0?
This project is licensed under the MIT license. See the LICENSE file for more info.