express-openid-connect

repository·master·Indexed 19 days ago

https://github.com/auth0/express-openid-connect

Express.js middleware for protecting web applications using the OpenID Connect (OIDC) protocol. It simplifies Auth0 authentication integration by automatically managing sessions, cookies, and OIDC flows. The library provides tools for protecting routes, handling silent logins, managing access and refresh tokens, and implementing claim-based access control.

Tokens
15.1K
Snippets
48
Records
73
Agent score
66%

What's inside express-openid-connect

  1. Handle session expiry from upstream IdP (IPSIE `session_expiry`)

    master

    When an upstream Identity Provider (IdP) supports the IPSIE SL1 spec, it may include a session_expiry claim in the ID token. This claim is an absolute Unix timestamp (in seconds) marking when the IdP session expires.

    SDK Behavior

    If the session_expiry claim is present, the SDK automatically:

    • Persists the value as sessionExpiresAt (Unix seconds) on the session.
    • Rejects logins with an HTTP 400 if the expiry time is already in the past.
    • Treats the session as expired once sessionExpiresAt is reached (with a 30-second clock skew leeway).
    • Caps the session cookie lifetime at this ceiling.
    • Throws a SessionExpiredError during accessToken.refresh() calls.

    Critical Requirement: Seconds vs Milliseconds

    The session_expiry claim MUST be a Unix timestamp in seconds. If your IdP provides a millisecond timestamp, you must divide it by 1000.

    • If the value is $\ge$ 10,000,000,000 (approx. year 2286), the SDK treats it as "no ceiling".
    • Non-integer, float, zero, or negative values will cause the check to fail open.
  2. Understand the testing tiers and frameworks

    master

    The testing strategy is divided into three main tiers:

    1. Unit Tests: Uses Mocha, Chai (assert), Sinon, and nock. These are located in test/ with the *.tests.js naming convention. They run against a fixture server and require all outbound network calls to be stubbed via nock.
    2. Type Tests: Uses tsd to validate index.d.ts against index.test-d.ts. Run these using npm run test:types.
    3. End-to-end (E2E) Tests: Uses Mocha and Puppeteer to drive runnable apps from the examples/ directory. These tests run against a local oidc-provider (not a live Auth0 tenant) and are located in end-to-end/ with the *.test.js naming convention. These tests are slower and launch a headless browser.
  3. Understand why authenticated users are unexpectedly redirected to login

    master

    If a logged-in user is suddenly redirected to login and req.appSession is null, the session may have expired due to the upstream Identity Provider's (IdP) session_expiry claim (IPSIE SL1).

    The SDK treats this claim as a hard ceiling on the local session lifetime. Once reached, the session is cleared on the next request.

    How to verify: Check if req.appSession.sessionExpiresAt was set during login. Compare that value against the current time to see if the ceiling was reached.

  4. Understand session and cookie compatibility

    master
    The session cookie format is managed by lib/appSession.js and lib/crypto.js, which handle encryption. Be aware that changing the session format, encryption methods, or cookie flags is a breaking change that will invalidate all existing user sessions. Treat any modifications to these files as high-impact changes.
  5. Module and Naming Conventions

    master

    When contributing to or extending the library, follow these conventions:

    • Module System: Use CommonJS exclusively (const x = require('...') and module.exports = { ... }). Do not use ESM import or export statements.
    • File Naming: Use camelCase for filenames, matching the primary export (e.g., appSession.js).
    • Variable/Function Naming: Use camelCase for functions and variables.
    • Class/Error Naming: Use PascalCase for classes and error types (e.g., SessionExpiredError).
    • Middleware Pattern: Middleware factories should be functions that return a standard Express (req, res, next) handler.
  6. Use the mock authorization server

    master
    If you do not provide a .env file, the examples will automatically configure one for you and start a mock authorization server. This allows you to test the application flow without a real Auth0 instance. When using the mock server, you can use any credentials to log in; the username you enter will be reflected in the sub claim of the resulting ID Token.
  7. Note on configuration validation and defaults

    master
    Configuration options are validated using Joi. If you add a new configuration option, you must declare it in the schema in lib/config.js. If an option is not declared in the schema, Joi will silently strip the unknown key, causing the option to be ignored or its default value to be missing.
  8. Use custom session stores and `genid`

    master
    When using custom session stores by overriding the genid configuration, ensure you use a cryptographically strong random value of sufficient size. This prevents session ID collisions and reduces the risk of session hijacking via ID guessing.
  9. Understand how `response_type` affects PKCE and `response_mode` defaults

    master
    The default authentication flow uses id_token (Implicit + Form Post). If you change the response_type configuration to include code, the SDK will apply different PKCE and response_mode validation logic. Do not assume Authorization Code semantics are active unless response_type is explicitly configured to use code.
  10. Protect a route using the default configuration

    master
    By default, the SDK uses the Implicit Flow with Form Post to handle authentication. When a user attempts to access a protected route, they are redirected to the Identity Provider (IdP) to authenticate. After successful authentication, the IdP posts the identity information back to your application.
  11. Configuration and Error Handling Patterns

    master

    The project follows specific patterns for configuration and error management:

    • Config-schema-as-contract: All public options are managed via a joi schema (located in lib/config.js). When adding new options, define them in the schema with a validation rule and a secure default. Avoid reading raw configuration values directly from process.env or other sources elsewhere in the code.
    • Typed Errors: Instead of throwing bare Error objects, use the project's specific error types (e.g., SessionExpiredError from lib/errors.js) or http-errors for generating HTTP responses. Project error types typically include code and status properties.
    • Outbound HTTP: Outbound OIDC HTTP requests are handled via createCustomFetch (lib/client.js), which manages User-Agent and Auth0-Client headers (telemetry can be opted out via enableTelemetry: false).
  12. Require authentication for specific routes

    master

    To allow anonymous access to some routes while protecting others, set authRequired: false in the auth() configuration. Use the requiresAuth() middleware on specific routes to enforce authentication.

    const { auth, requiresAuth } = require('express-openid-connect');
    
    app.use(
      auth({
        authRequired: false,
      }),
    );
    
    // Anyone can access the homepage
    app.get('/', (req, res) => {
      res.send('<a href="/admin">Admin Section</a>');
    });
    
    // requiresAuth checks authentication.
    app.get('/admin', requiresAuth(), (req, res) =>
      res.send(`Hello ${req.oidc.user.sub}, this is the admin section.`),
    );