🚨 Shai-Hulud Strikes Again:834 Packages Compromised.Technical Analysis →
Socket
Book a DemoInstallSign in
Socket

@bonniernews/bn-oidc-connector

Package Overview
Dependencies
Maintainers
41
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bonniernews/bn-oidc-connector

Express middleware for Bonnier News OIDC authentication

latest
Source
npmnpm
Version
0.0.20
Version published
Maintainers
41
Created
Source

Bonnier News OIDC Connector

Express middleware for connecting to the Bonnier News OpenID Connect Provider, providing authentication and authorization functionality for Node.js applications.

Table of Contents

  • Installation
  • Quick Start
  • Configuration
  • Middleware
  • Request Context
  • Response Context
  • API Reference
  • Error Handling

Installation

npm install @bonniernews/bn-oidc-connector

Quick Start

import { auth, isAuthenticated, isEntitled } from "@bonniernews/bn-oidc-connector";
import express from "express";

const app = express();

// Initialize OIDC middleware
app.use(auth({
  clientId: "your-client-id",
  issuerBaseURL: new URL("https://bn-login-id-service-lab.bnu.bn.nr"),
  baseURL: new URL("https://your-app-domain.com"),
  scopes: [ "profile", "email", "entitlements", "offline_access" ]
}));

// Protected route requiring authentication
app.get("/profile", isAuthenticated, (req, res) => {
  res.json({ user: req.oidc.user });
});

// Protected route requiring specific entitlements
app.get("/admin", isEntitled([ "admin" ]), (req, res) => {
  res.json({ message: "Admin area" });
});

app.listen(3000);

Configuration

AuthOptions

PropertyTypeRequiredDefaultDescription
clientIdstringâś…-Your OIDC client ID
clientSecretstring❌-Your OIDC client secret (for confidential clients)
issuerBaseURLURLâś…-Base URL of the OIDC provider
baseURLURLâś…-Base URL of your application
loginPathstring❌"/id/login"Path for login endpoint
logoutPathstring❌"/id/logout"Path for logout endpoint
loginCallbackPathstring❌"/id/login/callback"Path for login callback endpoint
logoutCallbackPathstring❌"/id/logout/callback"Path for logout callback endpoint
scopesstring[]❌["openid", "entitlements", "offline_access"]OAuth scopes to request
promptsstring[]❌[]Custom prompts for the authorization request
afterLoginCallbackfunction❌-Custom handler after successful login
afterLogoutCallbackfunction❌-Custom handler after successful logout
cookiesobject❌See belowCookie configuration

The connector uses several cookies to manage authentication state and tokens. The default cookie names are:

cookies: {
  authParams: "bnoidcap",   // Cookie name for auth parameters
  tokens: {
    access: "bnoidcat",     // Cookie name for access token
    refresh: "bnoidcrt",    // Cookie name for refresh token
    id: "bnoidcit",         // Cookie name for ID token
    expiresIn: "bnoidcei"   // Cookie name for token expiry
  },
  logout: "bnoidclo"        // Cookie name for logout state
}

Scopes Configuration

The scopes option determines what information and permissions your application requests from the OIDC provider. Scopes control access to user data and functionality.

scopes: [ "openid", "profile", "email", "entitlements", "offline_access" ]

Available Scopes:

ScopeDescriptionData Included
"openid"Basic OpenID Connect authenticationUser ID (sub claim)
"profile"Access to user's profile informationName (given_name and family_name claims)
"email"Access to user's email addressEmail address and verification status (email and email_verified claims)
"entitlements"Access to user's entitlementsUser entitlements (ent claim)
"external_ids"Access to user's external IDsExternal IDs for Didomi and Google Ads (external_ids claim)
"offline_access"Request refresh token for long-term accessEnables token refresh without re-authentication

Important Notes:

  • You do not need to specify "openid" in your configuration; it will be included automatically
  • "entitlements" is needed if you plan to use isEntitled() middleware
  • "offline_access" is highly recommended for applications to enable automatic token refresh

Prompts Configuration

The prompts option allows you to customize the authentication behavior by specifying OpenID Connect prompt parameters that control how the authorization server interacts with the user.

prompts: [ "login", "consent" ]  // Forces user to re-authenticate and consent

Available Prompts:

PromptDescription
"none"No user interaction. If authentication/consent is required, returns an error instead of prompting the user
"login"Forces the user to re-authenticate, even if they have an active session
"consent"Prompts the user to provide consent for the application's requested scopes again (note: this is currently not in use)
"select_account"Forces the user to confirm or switch accounts if already signed in.
Note: This is currently the only way to log in as a different user if you are already authenticated without requiring a full logout from the provider.

Dynamic Prompts:

You can also specify prompts dynamically during login:

app.get("/secure-login", (req, res) => {
  res.oidc.login(req, res, {
    prompts: [ "login" ],  // Override default prompts for this login
    returnTo: "/sensitive-area"
  });
});

Middleware

The library provides several middleware functions for different authentication and authorization scenarios.

Core Middleware

auth(options: AuthOptions)

The main middleware that initializes the OIDC connector and sets up authentication routes. It should be registered as early as possible in your Express app.

import { auth } from "@bonniernews/bn-oidc-connector";

app.use(auth({
  clientId: "your-client-id",
  issuerBaseURL: new URL("https://bn-login-id-service-lab.bnu.bn.nr"),
  baseURL: new URL("https://your-app.com")
}));

Features:

  • Initializes OIDC configuration and JWKS
  • Sets up authentication routes
  • Adds OIDC context to request/response objects
  • Handles automatic token refresh
  • Processes query parameters for authentication flows

Routes Created:

  • GET /id/login - Initiates login flow
  • GET /id/logout - Initiates logout flow
  • GET /id/login/callback - Handles login callback
  • GET /id/logout/callback - Handles logout callback

Guard Middleware

The library provides two guard middleware functions for protecting routes:

isAuthenticated

Ensures the user is authenticated before accessing a route.

import { isAuthenticated } from "@bonniernews/bn-oidc-connector";

app.get("/protected", isAuthenticated, (req, res) => {
  // User is guaranteed to be authenticated
  res.json({ message: "Hello authenticated user!" });
});

Behavior:

  • Checks req.oidc.isAuthenticated
  • Throws UnauthenticatedError if user is not authenticated
  • Allows request to continue if user is authenticated

Use when: You only need to verify that the user is logged in, without checking specific permissions.

isEntitled(validEntitlements: string[])

Checks if the authenticated user has specific entitlements. Note: This middleware automatically checks for authentication first, so isAuthenticated is not needed when using isEntitled.

import { isEntitled } from "@bonniernews/bn-oidc-connector";

app.get("/admin", isEntitled([ "admin", "super-user" ]), (req, res) => {
  // User is guaranteed to be authenticated AND have at least one of the specified entitlements
  res.json({ message: "Admin access granted" });
});

Parameters:

  • validEntitlements: Array of entitlement strings. User needs at least one.

Behavior:

  • First checks if user is authenticated (throws UnauthenticatedError if not)
  • Returns true if validEntitlements is empty
  • Checks user's entitlements from ID token claims (ent field)
  • Throws UnauthorizedError if user lacks required entitlements

Use when: You need to verify both authentication and specific permissions/roles.

When to Use Which Middleware

// âś… Use isAuthenticated for routes that only require login
app.get("/profile", isAuthenticated, (req, res) => { ... });

// âś… Use isEntitled for routes that require specific permissions
app.get("/admin", isEntitled([ "admin" ]), (req, res) => { ... });

// ❌ Don't combine them - isEntitled already checks authentication
app.get("/admin", isAuthenticated, isEntitled([ "admin" ]), (req, res) => { ... });

// âś… Use isAuthenticated when doing manual entitlement checks
app.get("/dynamic", isAuthenticated, (req, res) => {
  const hasAccess = req.oidc.isEntitled([ "premium" ]);
  // ... conditional logic
});

Request Context

The middleware adds an oidc property to the Express Request object with the following structure:

req.oidc: OidcRequestContext

PropertyTypeDescription
configOidcConfigComplete OIDC configuration including client config, well-known config, and signing keys
accessTokenstring | undefinedCurrent access token
refreshTokenstring | undefinedCurrent refresh token
idTokenstring | undefinedCurrent ID token (JWT)
expiresInnumber | undefinedToken expiration time in seconds
idTokenClaimsRecord<string, any> | undefinedDecoded ID token claims
isAuthenticatedbooleanWhether the user is currently authenticated
userobject | undefinedUser information extracted from ID token
isEntitledfunctionFunction to check user entitlements

User Object

req.oidc.user: {
  id: string;        // User ID (from 'sub' claim)
  email?: string;    // User email (if available in token)
}

isEntitled Function

req.oidc.isEntitled(validEntitlements: string[]): boolean

Check if the current user has any of the specified entitlements.

Example:

app.get("/content", isAuthenticated, (req, res) => {
  if (req.oidc.isEntitled([ "premium", "subscriber" ])) {
    res.json({ content: "Premium content" });
  } else {
    res.json({ content: "Free content" });
  }
});

Response Context

The middleware adds an oidc property to the Express Response object with authentication flow methods:

res.oidc: OidcResponseContext

MethodSignatureDescription
login(req, res, options?) => voidInitiates login flow
loginCallback(req, res) => voidHandles login callback
logout(req, res, options?) => voidInitiates logout flow
logoutCallback(req, res) => voidHandles logout callback
refresh(req, res) => Promise<void>Refreshes tokens

Login Options

type LoginOptions = {
  returnTo?: string;     // URL to redirect after login
  scopes?: string[];     // Additional scopes to request
  prompts?: string[];    // OIDC prompts (e.g., "none", "login", "consent")
  locale?: string;       // UI locale for the login page
}

Logout Options

type LogoutOptions = {
  returnTo?: string;     // URL to redirect after logout
}

Manual Authentication Flow

app.get("/custom-login", (req, res) => {
  res.oidc.login(req, res, {
    returnTo: "/dashboard",
    scopes: [ "profile", "email" ],
    locale: "sv-SE"
  });
});

app.get("/custom-logout", (req, res) => {
  res.oidc.logout(req, res, {
    returnTo: "/goodbye"
  });
});

API Reference

Query Parameters

The middleware automatically handles special query parameters:

idlogin

Triggers automatic login flow:

GET /some-page?idlogin=true
GET /some-page?idlogin=silent  // Uses prompt=none for silent authentication

idrefresh

Triggers token refresh:

GET /some-page?idrefresh=true

This is useful in scenarios where claims or entitlements may have changed during the user's session—such as after a purchase, subscription upgrade, or profile update—allowing the application to refresh tokens and retrieve updated user information without requiring the user to log in again.

Authentication Flow

  • Login: User visits /id/login or a page with ?idlogin=true
  • Authorization: User is redirected to OIDC provider
  • Callback: Provider redirects to /id/login/callback with authorization code
  • Token Exchange: Authorization code is exchanged for tokens
  • Session: Tokens are stored in secure cookies
  • Access: User can access protected resources

Token Management

  • Access tokens are used for API authentication
  • Refresh tokens are used to obtain new access tokens
  • ID tokens contain user identity information
  • Tokens are automatically refreshed when they expire
  • Failed refresh attempts trigger re-authentication

Error Handling

The library defines several error types for different failure scenarios:

Error Types

ErrorDescription
UnauthenticatedErrorUser is not logged in
UnauthorizedErrorUser lacks required permissions
InvalidStateErrorOAuth state parameter mismatch
InvalidIdTokenErrorID token validation failed
RefreshRequestErrorToken refresh failed
TokenRequestErrorToken exchange failed
InitOidcErrorOIDC initialization failed
DiscoveryFailedErrorOIDC discovery endpoint failed

Error Handling Example

import { UnauthenticatedError, UnauthorizedError } from "@bonniernews/bn-oidc-connector";

app.use((err, req, res, next) => {
  if (err instanceof UnauthenticatedError) {
    return res.status(401).json({ error: "Please log in" });
  }

  if (err instanceof UnauthorizedError) {
    return res.status(403).json({ error: "Access denied" });
  }

  // Handle other errors
  next(err);
});

For more information and updates, visit the GitHub repository.

FAQs

Package last updated on 12 Nov 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts