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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fluojs/passport

v2.0.2

Published

Strategy-agnostic authentication guard wiring and strategy registry for Fluo.

Readme

@fluojs/passport

Node.js support is >=24.0.0 <27. See Node.js support and migration before upgrading.

Strategy-agnostic auth execution layer for fluo. It routes any AuthStrategy through a generic AuthGuard into the request context, populating requestContext.principal.

Table of Contents

Installation

npm install @fluojs/passport

When to Use

  • When you need to protect routes with authentication and authorization (RBAC/Scopes).
  • When using multiple auth strategies (e.g., JWT, Cookies, API Keys) in the same application.
  • When you need a bridge to existing Passport.js strategies.
  • When implementing refresh token rotation or account-linking policies.

Quick Start

1. Register Modules

Use the built-in bearer JWT preset and register it with PassportModule.forRoot(...).

import { Module } from '@fluojs/core';
import { JwtModule } from '@fluojs/jwt';
import {
  BEARER_JWT_STRATEGY_NAME,
  BearerJwtStrategy,
  createBearerJwtStrategyRegistration,
  PassportModule,
} from '@fluojs/passport';

@Module({
  imports: [
    JwtModule.forRoot({
      algorithms: ['HS256'],
      audience: 'my-app',
      issuer: 'my-api',
      secret: 'your-secure-secret',
    }),
    PassportModule.forRoot(
      { defaultStrategy: BEARER_JWT_STRATEGY_NAME },
      [createBearerJwtStrategyRegistration()],
    ),
  ],
  providers: [BearerJwtStrategy],
})
export class AuthModule {}

BearerJwtStrategy is the first-party strategy preset for Authorization: Bearer <token> credentials. JWT-based passport strategies require both pieces of module wiring: JwtModule.forRoot(...) registers DefaultJwtVerifier, the providers entry exposes BearerJwtStrategy to that verifier through DI, and PassportModule.forRoot(...) registers the named strategy that @UseAuth('jwt') resolves. Returning the DefaultJwtVerifier.verifyAccessToken(...) result unchanged preserves the normalized principal contract (subject, claims, issuer, audience, roles, and scopes) that AuthGuard writes to requestContext.principal.

2. Protect Routes

Use @UseAuth() to enforce authentication and @RequireScopes() to enforce authorization.

import { Controller, Get, type RequestContext } from '@fluojs/http';
import { UseAuth, RequireScopes } from '@fluojs/passport';

@Controller('/profile')
export class ProfileController {
  @Get('/')
  @UseAuth('jwt')
  @RequireScopes('profile:read')
  async getProfile(input: never, ctx: RequestContext) {
    return { user: ctx.principal };
  }
}

Common Patterns

Bearer JWT Preset

Use the built-in BearerJwtStrategy when your app authenticates Authorization: Bearer <token> requests; no application-owned parsing code is required.

import { Module } from '@fluojs/core';
import { JwtModule } from '@fluojs/jwt';
import {
  BEARER_JWT_STRATEGY_NAME,
  BearerJwtStrategy,
  createBearerJwtStrategyRegistration,
  PassportModule,
} from '@fluojs/passport';

@Module({
  imports: [
    JwtModule.forRoot({
      algorithms: ['HS256'],
      audience: 'my-app',
      issuer: 'my-api',
      secret: 'your-secure-secret',
    }),
    PassportModule.forRoot(
      { defaultStrategy: BEARER_JWT_STRATEGY_NAME },
      [createBearerJwtStrategyRegistration()],
    ),
  ],
  providers: [BearerJwtStrategy],
})
export class AuthModule {}

Register BearerJwtStrategy as a provider in the same application module that imports PassportModule and JwtModule; JwtModule remains the owner of verification configuration, and the preset does not introduce a combined JWT/Passport module facade. The preset registers under the stable strategy name BEARER_JWT_STRATEGY_NAME ('jwt'), so protected routes keep using @UseAuth('jwt'), and createBearerJwtStrategyRegistration() returns the matching AuthStrategyRegistration.

Credential extraction is strict. The credential must match RFC 6750 b64token: one or more ASCII letters, digits, -, ., _, ~, +, or /, followed only by optional trailing = padding. It rejects ?, internal =, Unicode, whitespace, and control characters before verification. A missing or empty Authorization header throws AuthenticationRequiredError, and when the adapter surfaces an array-valued Authorization header only the first entry is read. A wrong-scheme or malformed header throws AuthenticationFailedError; the scheme match is case-insensitive and uses one or more ASCII spaces. Missing and malformed credentials serialize the bare WWW-Authenticate: Bearer challenge. Verification failures keep DefaultJwtVerifier error causality: expired credentials throw AuthenticationExpiredError, and JwtInvalidTokenError credentials throw AuthenticationFailedError, each preserving the original JWT error as cause and serializing WWW-Authenticate: Bearer error="invalid_token". JwtConfigurationError and verifier-provider infrastructure failures propagate unchanged instead of becoming credential failures. AuthGuard maps only credential failures to the canonical 401 Unauthorized response, and scope mismatches still produce 403 Forbidden.

BearerJwtStrategy returns the normalized JwtPrincipal from DefaultJwtVerifier.verifyAccessToken(...) unchanged, preserving subject, claims, issuer, audience, roles, and scopes.

A custom AuthStrategy remains the extension point for token revocation, account-state checks, alternate extraction, and application-owned policy.

Passport.js Bridge

Easily adapt any standard Passport.js strategy (like passport-google-oauth20) to work with fluo's DI and async lifecycle.

const googleBridge = createPassportJsStrategyBridge('google', GoogleStrategy, {
  mapPrincipal: ({ user }) => ({ subject: user.id, claims: user }),
});

createPassportJsStrategyBridge(...) is an intentionally documented manual-composition compatibility helper. It returns the provider bundle and matching AuthStrategyRegistration needed by PassportModule.forRoot(...); applications should register the bridge providers in the same module that imports PassportModule and pass googleBridge.strategy to the strategy registry:

@Module({
  imports: [
    PassportModule.forRoot({ defaultStrategy: 'google' }, [googleBridge.strategy]),
  ],
  providers: [GoogleStrategy, ...googleBridge.providers],
})
export class AuthModule {}

This bridge helper is the official exception to the module-facade rule for Passport.js adapters because third-party strategy instances must be bound as providers before AuthGuard can execute them. It does not replace PassportModule, @UseAuth(...), or AuthGuard as the application-facing authentication surface.

The bridge settles each Passport.js strategy execution exactly once. A strategy must call one of the bound Passport actions (success, fail, redirect, pass, or error); promise rejections, promise completion without an action, and callback-style executions that exceed the bounded action timeout become authentication failures instead of leaving the request unresolved. The bridge never consumes an authenticate() return value: only a bound action settles the request. Any AuthStrategyResult with handled: true is fully terminal after the strategy commits a response, even if it also includes a principal; AuthGuard skips principal validation, scope checks, requestContext.principal assignment, and the protected handler. Custom mapPrincipal functions must return a valid fluo Principal with a non-empty subject and object claims.

The bridge calls authenticate(request, options) with the active platform adapter's raw host request when one exists, and with the normalized fluo request otherwise. It never creates a Passport-initialized host request, so strategies that depend on Passport middleware augmentation (request.logIn, request.user, session state) or on adapter-specific request fields need the compatibility checklist in NestJS → fluo Migration Map before cutover.

actionTimeoutMs defaults to 30_000 milliseconds and must be a non-negative finite number. Set it to 0 to schedule timeout settlement on the next timer turn. Negative, NaN, and infinite values throw RangeError when the bridge strategy is constructed instead of disabling the settlement bound.

Application shutdown cancels every in-flight bridge execution, clears its action timeout, and rejects the pending authentication instead of retaining request state through the configured timeout. The bridge participates in the ordinary application lifecycle, so this cancellation runs when the application or application context is closed.

Cookie Auth Preset

Use CookieAuthModule.forRoot(...) when your app authenticates requests from HTTP cookies.

import { Module } from '@fluojs/core';
import { JwtModule } from '@fluojs/jwt';
import { CookieAuthModule } from '@fluojs/passport';

@Module({
  imports: [
    CookieAuthModule.forRoot({
      accessTokenCookieName: 'session_access',
      refreshTokenCookieName: 'session_refresh',
      cookieOptions: { path: '/sessions' },
    }),
    JwtModule.forRoot({
      algorithms: ['HS256'],
      global: true,
      secret: 'your-secure-secret',
    }),
  ],
})
export class AuthModule {}

Import CookieAuthModule.forRoot(...) and JwtModule.forRoot(...) together for cookie-auth support. CookieAuthModule owns one PassportModule registry that registers CookieAuthStrategy, CookieManager, and AuthGuard. To combine cookie auth with bearer or a Passport.js bridge, pass the other named registrations as the third forRoot argument instead of importing a sibling PassportModule.forRoot(...); keep the additional strategy providers application-owned. CookieAuthModule and JwtModule are sibling imports, so set the documented global: true JWT option to make DefaultJwtVerifier visible when the cookie strategy resolves.

Pass cookie names once to CookieAuthModule.forRoot(...): the same configuration supplies CookieAuthStrategy's credential reader and CookieManager's writer and clearer. For a separately owned object outside DI, use CookieManager.create(config) with the same CookieManagerConfig. See the cookie preset migration for removed manual helpers.

CookieAuthStrategy preserves the normalized JWT principal contract from @fluojs/jwt, including subject, claims, issuer, audience, roles, and scopes.

Cookie access tokens must be non-empty strings. Missing cookies can resolve to { authenticated: false } only when requireAccessToken: false; malformed present cookie values always fail authentication before JWT verification.

Cookie verification failures keep their documented classification: expired access tokens raise AuthenticationExpiredError, invalid access tokens raise AuthenticationFailedError, and missing or malformed access-token cookies raise AuthenticationRequiredError. The originating @fluojs/jwt error is preserved as the cause, while AuthGuard still answers HTTP 401 for every variant.

CookieManagerConfig.cookieOptions accepts SetCookieOptions. Its accessTokenTtlSeconds and refreshTokenTtlSeconds fields supply the default Max-Age for the matching token cookie when the positional TTL argument is omitted; an explicit positional TTL wins, then the matching configured TTL, then cookieOptions.maxAge. maxAge is measured in seconds and is forwarded to portable HTTP maxAgeSeconds, not the millisecond convention used by some host frameworks. Auth cookies default to Path=/, Secure, HttpOnly, and SameSite=Strict, with no default domain or lifetime; general @fluojs/http setCookie(...) does not add those auth defaults.

CookieManager appends access-token and refresh-token Set-Cookie values without overwriting cookies that were already placed on the response, even when the underlying adapter stores the existing header with different casing such as set-cookie. It uses the portable HTTP serializer, so cookie values are percent-encoded before they are emitted and malformed cookie names or attributes fail validation instead of emitting invalid headers.

Protected routes must keep using @UseAuth(...). If you configure requireAccessToken: false, a missing cookie resolves to an explicit unauthenticated result instead of an anonymous principal, so protected routes still reject the request.

Use @UseOptionalAuth(...) only on routes that intentionally support both signed-in and guest callers:

import { Controller, Get, type RequestContext } from '@fluojs/http';
import { UseOptionalAuth } from '@fluojs/passport';

@Controller('/session')
export class SessionController {
  @Get('/')
  @UseOptionalAuth('cookie')
  getSession(_input: never, ctx: RequestContext) {
    return { subject: ctx.principal?.subject ?? null };
  }
}

Refresh Token Lifecycle

The package provides a built-in RefreshTokenStrategy plus the RefreshTokenModule and RefreshTokenService contract for secure token rotation and revocation.

import { Module } from '@fluojs/core';
import { Controller, Post, type RequestContext } from '@fluojs/http';
import { JwtModule } from '@fluojs/jwt';
import {
  PassportModule,
  REFRESH_TOKEN_STRATEGY_NAME,
  RefreshTokenModule,
  RefreshTokenStrategy,
  UseAuth,
} from '@fluojs/passport';

@Controller('/auth')
export class AuthController {
  @Post('/refresh')
  @UseAuth('refresh-token')
  async refresh(input: never, ctx: RequestContext) {
    return ctx.principal; // Contains new token pair
  }
}

@Module({
  controllers: [AuthController],
  imports: [
    JwtModule.forRoot({
      algorithms: ['HS256'],
      global: true,
      refreshToken: {
        expiresInSeconds: 604800,
        rotation: true,
        secret: 'your-refresh-token-secret',
        store: 'memory', // Use an application-owned atomic durable store in production.
      },
      secret: 'your-access-token-secret',
    }),
    RefreshTokenModule.forRoot(),
    PassportModule.forRoot(
      { defaultStrategy: REFRESH_TOKEN_STRATEGY_NAME },
      [{ name: REFRESH_TOKEN_STRATEGY_NAME, token: RefreshTokenStrategy }],
    ),
  ],
})
export class AuthModule {}

Import JwtModule.forRoot(...), RefreshTokenModule.forRoot(), and PassportModule.forRoot(...) together. JwtModule is the sole owner of refresh-token crypto, expiry, rotation, and store configuration. The global: true option makes its exported RefreshTokenService visible to the sibling RefreshTokenModule, whose default registration aliases that service rather than constructing another service or store. The explicit 'memory' store is development-only opt-in; production must provide one application-owned atomic RefreshTokenStore. RefreshTokenStrategy is the default endpoint recipe and PassportModule registers its @UseAuth('refresh-token') name.

RefreshTokenStrategy extracts body.refreshToken before Authorization: Bearer <token>; a present malformed body value fails rather than falling back to the header. Missing credentials become AuthenticationRequiredError, malformed credentials and JWT reuse/invalidity become AuthenticationFailedError, and expired JWT refresh tokens become AuthenticationExpiredError.

A successful exchange resolves ctx.principal to the RefreshTokenPrincipal shape: the rotated pair is nested under claims.accessToken and claims.refreshToken, with the verified subject at the top level. The separate exported RefreshTokenAuthResult type describes the application-facing exchange payload a refresh endpoint returns to clients.

RefreshTokenStrategy reads tokens from body.refreshToken, Authorization: Bearer ..., or x-refresh-token; malformed non-string tokens fail authentication. After rotation, it verifies the returned access token through DefaultJwtVerifier and uses its normalized subject. A custom RefreshTokenServicePort may own refresh state, but it must be registered alongside a globally visible JwtModule configuration and return access tokens that configuration accepts; it is not a non-JWT exchange path.

Account Linking and Status

Use createConservativeAccountLinkPolicy(...) and resolveAccountLinking(...) to model identity-link decisions. The default conservative policy links one unambiguous existing account link or user-confirmed matches, reports multiple existing links as conflicts, and otherwise creates, skips, rejects, or reports conflicts deterministically.

createPassportPlatformStatusSnapshot(...) and createPassportPlatformDiagnosticIssues(...) expose readiness/health diagnostics for registered strategies, default strategy configuration, presets, and refresh-token store readiness.

Public API Overview

Decorators

  • @UseAuth(strategyName): Attaches AuthGuard and sets the active strategy.
  • @UseOptionalAuth(strategyName): Attaches AuthGuard but allows routes without scopes to continue when the strategy reports missing credentials.
  • @RequireScopes(...scopes): Enforces specific scope requirements.

Module and Guard Entry Points

  • PassportModule: Module entry point for passport strategy wiring.
  • AuthGuard: The HTTP guard that executes the strategy chain and enforces required scopes.
  • PassportModuleOptions, AuthStrategyRegistration, AuthStrategyRegistry, AuthGuardContract: Strategy registry and guard wiring contracts.

Strategy Contracts and Errors

  • AuthStrategy: The contract for implementing custom authentication logic.
  • AuthRequirement: Route-level metadata for the selected strategy, optional-auth mode, and required scopes.
  • AuthStrategyResult, AuthOptionalResult, AuthHandledResult: Strategy return variants for principals, intentionally missing credentials, and fully handled responses.
  • AuthStrategyResolutionError, AuthenticationRequiredError, AuthenticationFailedError, AuthenticationExpiredError: Public errors used by guards and strategy adapters for registry misses, missing credentials, invalid credentials, and expired credentials. isPassportError(...) validates their versioned owner contract across compatible duplicate copies; guards preserve the same HTTP 401 classification without accepting plain code-string lookalikes.

Metadata and Scope Helpers

  • defineAuthRequirement(...), getOwnAuthRequirement(...), getAuthRequirement(...): Public helpers for reading and writing auth requirement metadata when integrating custom decorators or tooling with AuthGuard.
  • Scope requirements are normally authored with @RequireScopes(...); lower-level scope normalization helpers remain internal and are not part of the package root export.

Bearer JWT Preset

  • BearerJwtStrategy, BEARER_JWT_STRATEGY_NAME: Built-in bearer credential strategy and its stable registration name ('jwt').
  • Bearer helper: createBearerJwtStrategyRegistration.

Cookie Auth Preset

  • CookieAuthModule: Module entry point for the built-in cookie-auth preset.
  • CookieAuthStrategy, COOKIE_AUTH_STRATEGY_NAME, COOKIE_AUTH_OPTIONS, DEFAULT_COOKIE_AUTH_OPTIONS, DEFAULT_COOKIE_OPTIONS: Cookie strategy wiring tokens, preset defaults, and response-cookie defaults.
  • CookieAuthOptions, CookieManagerConfig, CookieOptions, SetCookieOptions: Cookie strategy and response cookie configuration types. CookieManagerConfig.cookieOptions accepts SetCookieOptions, whose per-token TTL fields become default cookie Max-Age values.
  • CookieManager: Class-token-preserving utility for setting and clearing HttpOnly access/refresh token cookies. Use CookieManager.create(config) for separately owned instances.
  • normalizeCookieAuthOptions: Low-level cookie option normalization helper.

Refresh Token Preset

  • RefreshTokenModule: Module entry point for the built-in refresh-token preset.
  • RefreshTokenStrategy, REFRESH_TOKEN_STRATEGY_NAME, REFRESH_TOKEN_SERVICE: Refresh-token strategy and service alias wiring.
  • RefreshTokenServicePort, RefreshTokenInput, RefreshTokenAuthResult, RefreshTokenPrincipal: Custom refresh-service port, exchange payload shapes, and the principal shape resolved onto ctx.principal after a successful exchange. Custom services still require a globally visible JwtModule verifier and must return an access token it accepts. The JWT-owned concrete RefreshTokenService is imported from @fluojs/jwt.
  • Refresh helpers: createRefreshTokenStrategyRegistration.

Refresh ownership migration

Move every refresh secret, expiresInSeconds, rotation, and store value into JwtModule.forRoot({ global: true, refreshToken: ... }), then replace RefreshTokenModule.forRoot(JwtRefreshTokenAdapter) with RefreshTokenModule.forRoot(). Remove imports of JwtRefreshTokenAdapter, REFRESH_TOKEN_MODULE_OPTIONS, and RefreshTokenModuleOptions. Consumers that used Passport's former structural RefreshTokenService type should use RefreshTokenService from @fluojs/jwt for the canonical path. A custom RefreshTokenServicePort remains supported only with a globally visible JwtModule verifier and JWT access tokens that it accepts.

Passport.js Bridge

  • createPassportJsStrategyBridge(...): Compatibility helper that adapts Passport.js strategies to fluo AuthStrategy and returns providers plus the matching strategy registration for PassportModule.forRoot(...).
  • PassportJsAuthStrategy, PassportJsStrategyLike, PassportJsPrincipalMapperInput, PassportJsPrincipalMapper, PassportJsAuthStrategyOptions, PassportJsStrategyBridge: Bridge strategy, mapper, configuration, and provider bundle contracts.

Account Linking

  • ACCOUNT_LINKING_POLICY: DI token for registering an account-linking policy implementation.
  • createConservativeAccountLinkPolicy(...), resolveAccountLinking(...): Conservative default policy and resolver for identity-link decisions.
  • AccountIdentity, AccountLinkCandidate, AccountLinkAttempt, AccountLinkContext, AccountLinkPolicy, AccountLinkPolicyDecision, AccountLinkingOptions, AccountLinkingResolution: Account-linking input, policy, and result contracts.
  • AccountLinkConflictError, AccountLinkRejectedError: Errors raised for ambiguous or rejected account-linking attempts.

Status and Diagnostics

  • createPassportPlatformStatusSnapshot(...): Creates a runtime platform snapshot for strategy registry, preset readiness, ownership, and telemetry labels.
  • createPassportPlatformDiagnosticIssues(...): Emits diagnostic issues for empty registries, missing default strategies, cookie preset readiness, and refresh-token backing store readiness.
  • PassportPlatformStatusSnapshot, PassportStatusAdapterInput: Status helper input/output contracts.

UseOptionalAuth only bypasses missing credentials when no scopes are required; scoped routes still need a principal. AuthHandledResult with handled: true is terminal only after the strategy commits the response, including results that also carry a principal. Passport.js bridge redirect() commits the response and skips the protected handler, while pass() and strategy completion without a Passport action are authentication failures. Refresh-token backing store status and diagnostic surfaces redact secret-like reason strings before exposing readiness, health, details, or diagnostic causes.

Related Packages

  • @fluojs/jwt: The underlying token core for JWT-based strategies.
  • @fluojs/http: Provides the routing and guard infrastructure.

Example Sources

  • packages/passport/src/guard.test.ts: Guard execution and scope enforcement patterns.
  • packages/passport/src/adapters/passport-js.ts: Implementation of the Passport.js bridge.
  • packages/passport/src/bearer/bearer-jwt.ts: Built-in bearer JWT strategy preset.

Compatible service copies

AuthGuard supports ordinary class injection across compatible same-realm copies, including the UseAuth/guard path; strategy registries remain isolated.