@node-saml/passport-saml

repository·master·Indexed 21 days ago

https://github.com/node-saml/passport-saml

A SAML 2.0 authentication strategy for Passport.js (version 5.1.0) that enables Node.js applications to integrate with Identity Providers such as Okta, Onelogin, and ADFS. It provides the SamlStrategy for single-provider authentication and MultiSamlStrategy for applications supporting multiple Identity Providers via dynamic configuration using getSamlOptions. The library includes utilities for generating Service Provider metadata and supports custom verification callbacks via VerifiedCallback, VerifyWithRequest, and VerifyWithoutRequest.

Tokens
4.3K
Snippets
13
Records
18
Agent score
75%

What's inside @node-saml/passport-saml

  1. Provide the authentication callback route

    master

    You must define a POST route that matches the callbackURL (or callbackUrl) configured in your strategy. This route must be placed after your body-parsing middleware.

    Express v4.x: Use body-parser. Express v5.x: Use express.urlencoded.

    // Express v4 example
    const bodyParser = require("body-parser");
    
    app.post(
      "/login/callback",
      bodyParser.urlencoded({ extended: false }),
      passport.authenticate("saml", {
        failureRedirect: "/",
        failureFlash: true,
      }),
      function (req, res) {
        res.redirect("/");
      },
    );
  2. Use MultiSamlStrategy for multiple SAML providers

    master

    The MultiSamlStrategy class allows you to handle multiple SAML Identity Providers (IdPs) using a single Passport strategy. Instead of a static configuration, it uses a getSamlOptions function to dynamically resolve the specific SAML configuration based on the incoming request (e.g., by looking up a tenant ID or domain in the request URL or body).

    To use it, you must provide a MultiStrategyConfig object that includes a getSamlOptions function. This function is called during the authenticate, logout, and generateServiceProviderMetadata lifecycles to inject provider-specific settings into the underlying SAML service.

    import { MultiSamlStrategy } from '@node-saml/passport-saml';
    
    const strategy = new MultiSamlStrategy({
      // Base configuration shared by all providers
      entryPoint: '', 
      issuer: 'my-app',
      // ... other PassportSamlConfig options
    
      // Dynamic configuration resolver
      getSamlOptions: (req, callback) => {
        const tenantId = req.params.tenantId;
        const providerConfig = lookupConfig(tenantId);
    
        if (providerConfig) {
          // Return the specific SAML options for this provider
          callback(null, {
            entryPoint: providerConfig.ssoUrl,
            issuer: providerConfig.issuer,
            // ...
          });
        } else {
          callback(new Error('Provider not found'));
        }
      }
    }, signonVerify, logoutVerify);
  3. Configure SAML for Active Directory Federation Services (ADFS)

    master

    When using ADFS, ensure a trust is established to your service. A proven configuration includes setting authnContext and identifierFormat as shown below.

    {
      entryPoint: 'https://ad.example.net/adfs/ls/',
      issuer: 'https://your-app.example.net/login/callback',
      callbackUrl: 'https://your-app.example.net/login/callback',
      idpCert: 'MIICizCCAfQCCQCY8tKaMc0BMjANBgkqh ... W==',
      authnContext: ['http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/windows'],
      identifierFormat: null
    }
  4. Generate Service Provider Metadata

    master

    Use generateServiceProviderMetadata(decryptionCert, signingCert) to generate the XML metadata required by your Identity Provider.

    • For SamlStrategy: Use strategy.generateServiceProviderMetadata(decryptionCert, signingCert).
    • For MultiSamlStrategy: Use strategy.generateServiceProviderMetadata(req, decryptionCert, signingCert, next). This requires the request object and a callback to resolve the correct configuration via getSamlOptions.
  5. Configure the SamlStrategy

    master

    The SamlStrategy is the primary class for single-provider SAML authentication. Most configuration options are passed through to the underlying node-saml library.

    Passport-SAML specific parameters:

    • additionalParams: A dictionary of additional query params to add to all requests. If passed to authenticate(), these override the initialization options.
    • passReqToCallback: If true, the req object is passed as the first argument to the verify callback (default: false).
    • name: A custom name for the strategy (default: saml). Use this if you need to instantiate multiple strategies with different configurations.
    const SamlStrategy = require('@node-saml/passport-saml').Strategy;
    
    passport.use(
      new SamlStrategy(
        {
          callbackURL: "/login/callback",
          entryPoint: "https://openidp.feide.no/simplesaml/saml2/idp/SSOService.php",
          issuer: "passport-saml",
          idpCert: "fake cert", // cert must be provided
        },
        function (profile, done) {
          // Sign-on callback: find or create user based on profile
          findByEmail(profile.email, function (err, user) {
            if (err) return done(err);
            return done(null, user);
          });
        },
        function (profile, done) {
          // Logout callback: find user based on profile
          findByNameID(profile.nameID, function (err, user) {
            if (err) return done(err);
            return done(null, user);
          });
        }
      )
    );
  6. Configure MultiSamlStrategy for multiple providers

    master

    Use MultiSamlStrategy when your application needs to support multiple different SAML Identity Providers (IdPs).

    Key features:

    • getSamlOptions: A function called before SAML flows. It receives the request object and a done callback. You must call done(null, configuration) with the specific SAML configuration for the detected provider.
    • Defaults: Options provided during MultiSamlStrategy initialization act as defaults for all providers unless overridden in getSamlOptions.
    • Caching Note: All providers share the same cache (e.g., InMemoryCache) by default. To prevent one provider from validating a response against another provider's request, provide a unique cache provider per SAML provider via getSamlOptions.
    const { MultiSamlStrategy } = require('@node-saml/passport-saml');
    
    passport.use(
      new MultiSamlStrategy(
        {
          passReqToCallback: true,
          getSamlOptions: function (request, done) {
            findProvider(request, function (err, provider) {
              if (err) return done(err);
              // Return the specific configuration for this provider
              return done(null, provider.configuration);
            });
          },
        },
        function (req, profile, done) {
          // Sign-on callback
          findByEmail(profile.email, function (err, user) {
            if (err) return done(err);
            return done(null, user);
          });
        },
        function (profile, done) {
          // Logout callback
          findByNameID(profile.nameID, function (err, user) {
            if (err) return done(err);
            return done(null, user);
          });
        }
      )
    );
  7. Authenticate SAML requests

    master

    To initiate the SAML login flow, use passport.authenticate() with the saml strategy name.

    Adding Query Parameters: You can pass additionalParams to authenticate() to append query string parameters to the redirect URL.

    Handling Fallbacks: Use samlFallback (values: "login-request" or "logout-request") to dictate which request handler to use if the req.query or req.body does not contain standard SAMLRequest or SAMLResponse properties. The default is "login-request".

    // Standard login
    app.get(
      "/login",
      passport.authenticate("saml", { failureRedirect: "/", failureFlash: true }),
      function (req, res) {
        res.redirect("/");
      },
    );
    
    // Login with additional query parameters
    app.get(
      "/login",
      passport.authenticate("saml", {
        additionalParams: { username: "user@domain.com" },
      }),
      function (req, res) {
        res.redirect("/");
      },
    );
  8. Configure MultiSamlStrategy via MultiStrategyConfig

    master

    When instantiating MultiSamlStrategy, the configuration object must satisfy the MultiStrategyConfig type. This type is a combination of PassportSamlConfig (the standard SAML settings) and a required getSamlOptions function.

    Required Property:

    • getSamlOptions: A function with the signature (req: Request, callback: (err: Error | null, samlOptions: any) => void) => void. It must call the callback with either an error or the specific SAML options for the current request.
  9. Configure Multi-Strategy settings

    master

    If you are using multiple SAML providers, you can use MultiStrategyConfig to dynamically resolve options. This type extends StrategyOptions and PassportSamlConfig with a required method:

    • getSamlOptions(req: express.Request, callback: StrategyOptionsCallback): void: A function that takes the current request and returns the appropriate PassportSamlConfig via a callback. This allows you to switch Identity Providers based on request parameters, subdomains, or user input.
    const multiConfig: MultiStrategyConfig = {
      passReqToCallback: true,
      getSamlOptions: (req, callback) => {
        const options = req.query.provider === 'adfs' 
          ? adfsConfig 
          : standardConfig;
        callback(null, options);
      }
    };
  10. Use Strategy and MultiSamlStrategy for SAML authentication

    master

    The @node-saml/passport-saml package provides Strategy for standard SAML authentication and MultiSamlStrategy for handling multiple SAML providers. These classes are designed to be used as Passport.js strategies.

    import { Strategy, MultiSamlStrategy } from '@node-saml/passport-saml';
    
    // Standard SAML Strategy
    const samlStrategy = new Strategy(config, verifyCallback);
    
    // Multi-provider SAML Strategy
    const multiStrategy = new MultiSamlStrategy(multiConfig, verifyCallback);
  11. Generate Service Provider Metadata with MultiSamlStrategy

    master

    Because metadata depends on the specific Identity Provider being used, generateServiceProviderMetadata in MultiSamlStrategy is asynchronous. You must provide a callback function to receive the metadata string.

    Method Signature: generateServiceProviderMetadata(req: Request, decryptionCert: string | null, signingCert: string | string[] | null, callback: (err: Error | null, metadata?: string) => void): void

    strategy.generateServiceProviderMetadata(
      req, 
      decryptionCert, 
      signingCert, 
      (err, metadata) => {
        if (err) return handleErr(err);
        console.log('Generated Metadata:', metadata);
      }
    );