Fastify OpenID Connect Authentication

12.0.0 · active · verified Wed Apr 22

fastify-openid-auth is a specialized Fastify plugin designed to streamline OpenID Connect (OIDC) authentication within a Fastify application. Leveraging the robust openid-client library, it offers comprehensive support for both bearer token and cookie-based authentication flows, providing flexibility in how authentication state and tokens are managed. The plugin currently operates at version 12.0.0 as of its latest significant update in late 2025, demonstrating a consistent and active development cadence with several minor and major releases throughout 2024 and 2025. Key differentiators include its deep integration into the Fastify ecosystem by decorating the Fastify instance with dedicated `login`, `verify`, `refresh`, and `logout` handlers, full TypeScript type definitions, and adaptable token reading/writing strategies via headers, cookies, or session storage, making it suitable for a wide range of web application and API authentication scenarios.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart initializes a Fastify server, registers `fastify-openid-auth`, and demonstrates a basic OpenID Connect login and callback flow. It includes placeholder configuration for an OIDC provider and the session management required for cookie-based authentication, showing how to leverage the decorated authentication handlers.

import Fastify from 'fastify';
import openIDAuthPlugin from 'fastify-openid-auth';
import { generators } from 'openid-client'; // Often used with openid-client
// For session management, typically you'd also install and register @fastify/cookie and @fastify/session
// import fastifyCookie from '@fastify/cookie';
// import fastifySession from '@fastify/session';

const fastify = Fastify({ logger: true });

// Dummy OpenID Provider configuration (replace with actual values)
const openidConfig = {
  issuer: 'https://accounts.google.com', // Example, replace with your OIDC provider
  client_id: process.env.OIDC_CLIENT_ID ?? 'your-client-id-from-provider',
  client_secret: process.env.OIDC_CLIENT_SECRET ?? 'your-client-secret-from-provider',
  redirect_uris: ['http://localhost:3000/auth/callback'],
  response_types: ['code']
};

const AUTH_HANDLERS_SYMBOL = Symbol.for('auth-handlers');

// Register cookie and session plugins FIRST if using cookie-based authentication
// fastify.register(fastifyCookie);
// fastify.register(fastifySession, { 
//   secret: process.env.SESSION_SECRET ?? 'a-very-long-and-secure-secret-key-for-session-encryption',
//   cookie: { secure: process.env.NODE_ENV === 'production' }
// });

fastify.register(openIDAuthPlugin, {
  decorator: AUTH_HANDLERS_SYMBOL,
  config: openidConfig,
  login: {
    // Required since v12.0.0 for all handlers that manage OIDC state/tokens via session
    session: {
      get: async (request) => (request as any).session, // Placeholder: assumes @fastify/session is set up
      set: async (request, value) => { (request as any).session = value; }, // Placeholder
      destroy: async (request) => { delete (request as any).session; } // Placeholder
    },
    client_redirect_uri: 'http://localhost:3000/auth/callback',
    parameters: {
      scope: 'openid email profile',
      prompt: 'login'
    },
    code_verifier_generator: generators.codeVerifier, // Recommended for PKCE
    state_generator: generators.random // Recommended for state parameter
  },
  verify: {
    session: {
      get: async (request) => (request as any).session,
      set: async (request, value) => { (request as any).session = value; },
      destroy: async (request) => { delete (request as any).session; }
    }
  }
});

fastify.get('/login', async (request, reply) => {
  const { login } = (fastify as any)[AUTH_HANDLERS_SYMBOL];
  await login(request, reply);
});

fastify.get('/auth/callback', async (request, reply) => {
  const { verify } = (fastify as any)[AUTH_HANDLERS_SYMBOL];
  try {
    const tokens = await verify(request, reply);
    reply.send({ message: 'Login successful!', tokens });
  } catch (error: any) {
    fastify.log.error(error);
    reply.status(401).send({ error: 'Authentication failed', details: error.message });
  }
});

fastify.get('/protected', async (request, reply) => {
  // In a real application, you would add authentication middleware here
  // to check for a valid session or bearer token after 'verify' has run.
  reply.send({ message: 'This is a protected route. Requires prior authentication.' });
});

const start = async () => {
  try {
    await fastify.listen({ port: 3000 });
    fastify.log.info(`Server listening on port ${3000}`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

view raw JSON →