New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

better-auth-saml-idp

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

better-auth-saml-idp

Unofficial Better Auth plugin: act as a SAML 2.0 Identity Provider (SP- and IdP-initiated SSO, encrypted assertions). Runs on Cloudflare Workers and Node. Includes a CLI.

latest
Source
npmnpm
Version
1.7.0
Version published
Weekly downloads
478
-61.23%
Maintainers
1
Weekly downloads
 
Created
Source

better-auth-saml-idp

Turn your Better Auth server into a SAML 2.0 Identity Provider, and sign your users in to Microsoft 365, Google Workspace, Okta, Salesforce, AWS and other SAML 2.0 service providers. It's a Better Auth plugin, not a separate server: it runs on Cloudflare Workers and Node 22+, with no Java and no native binaries.

CI npm Better Auth Runs on License: MIT

Your app runs Better Auth with this plugin. A service provider such as Microsoft 365 sends the user to it with a SAML request; the user signs in with Better Auth as usual; the plugin sends back a signed SAML assertion, and the user is signed in to the service provider.

Try it: the example IdP (Workers + Hono + D1) signs users in to Microsoft 365, AWS IAM Identity Center, Cloudflare Access, Okta, Auth0 and Salesforce, verified live.

Signing in to Microsoft 365 through the example IdP: Microsoft's sign-in page, for an address on a domain federated to the IdP (blurred), hands the user to the Better Auth sign-in page.

📖 Read the guide for everything this page leaves out: every option, every error code, the security controls, Single Logout, Cloudflare Workers, the CLI and troubleshooting.

Unofficial community plugin. Not affiliated with or endorsed by Better Auth. Stable 1.x on npm, following semantic versioning; the CHANGELOG says what changed in each release and why.

✨ Features

  • 🔐 SAML 2.0 single sign-on: SP-initiated over HTTP-Redirect and HTTP-POST, and IdP-initiated as an opt-in per SP.
  • ✍️ Signed and encrypted: RSA-SHA256 signatures, AES-256-GCM encryption per SP, zero-downtime key rotation, or a key the plugin makes, stores and rotates itself (signing: { managed: true }).
  • 🛡️ Hardened input: every message checked against the OASIS schemas (libxml2 in WebAssembly, on Workers too), defences against XML signature wrapping, and replay protection by a database unique key. Fuzzed on every CI run.
  • 👤 Strict identity: only verified emails get assertions, impersonated and anonymous sessions are refused, and the user and session are re-read right before signing.
  • 🪜 Step-up MFA and your own rules: SPs that ask for more send the user back to sign in again (flows), and a per-SP authorize hook can deny with a reason.
  • 🚪 Single Logout in both directions, and onSessionEnded names the SPs still signed in when a session ends without it (guide).
  • 🗂️ SPs at runtime: an admin API that logs every change, SPs from their metadata, and certificates kept current from a metadata URL (guide).
  • 🏢 Better Auth native: organization-scoped SPs, an IdP identity per organization, admin-plugin permissions, a typed client plugin, and tables from npx auth generate. With @better-auth/sso in the same app it's an identity broker.
  • 🎟️ Assertions for OAuth tokens: an agent can trade an assertion for tokens once, the flow MCP's Enterprise-Managed Authorization uses (guide).
  • 📈 Observability: event callbacks for audit trails and SIEM, and an optional audit-log table (guide).
  • 🧰 A CLI that inspects metadata, decodes captured messages, smoke-tests a deployment and generates keys (guide).
  • 🗄️ Your database and runtime: PostgreSQL, MySQL, MongoDB, SQLite and Cloudflare D1, through Kysely, Drizzle and Prisma, all in CI. Workers and Node run the whole suite; Bun and Deno a smoke test. Benchmarked on Workers and D1.

⚡ Quick Start

npm install better-auth-saml-idp
npx better-auth-saml-idp keygen --cert-out idp.crt --key-out idp.key   # signing key (mode 0600) and certificate

Add the plugin to your Better Auth config, with the key and certificate loaded from your secret store:

import { betterAuth } from "better-auth";
import { samlIdp } from "better-auth-saml-idp";

export const auth = betterAuth({
  // database, emailAndPassword, … as you have them
  plugins: [
    samlIdp({
      entityId: "https://auth.example.com/api/auth/saml2/idp",
      baseURL: "https://auth.example.com",
      loginPage: "/sign-in", // your sign-in page; it sends users back to callbackURL
      signing: {
        privateKey: process.env.SAML_IDP_PRIVATE_KEY!,
        certificate: process.env.SAML_IDP_CERT!,
      },
      serviceProviders: [
        {
          id: "my-app",
          entityId: "<the SP's entity ID>",
          acsUrls: ["<the SP's ACS URL>"],
          attributes: { email: "email" },
        },
      ],
    }),
  ],
});
npx auth migrate   # create the tables (Drizzle or Prisma: npx auth generate, then your migration)

# Check what SPs will see, turn an SP's metadata into a serviceProviders entry, then run the smoke test:
npx better-auth-saml-idp inspect https://auth.example.com
npx better-auth-saml-idp sp-from-metadata https://sp.example.com/saml/metadata --id my-app
npx better-auth-saml-idp smoke https://auth.example.com --sp <the SP's entity ID>

Then give the SP your metadata URL, https://auth.example.com/api/auth/saml2/idp/metadata. The getting started guide walks through each step, including the sign-in page and the defaults to know about.

On Cloudflare Workers, put samlIdp() inside withCloudflare's second argument: a plugins: [...] next to ...withCloudflare(...) replaces the Cloudflare plugin and silently disables its storage validation. Or skip the key step with signing: { managed: true }. See Cloudflare Workers.

Examples: Workers + Hono + D1 (a complete, deployable IdP, the one running the demo), Next.js on Node, and Workers + Neon on Postgres through Hyperdrive.

Requirements

  • better-auth and @better-auth/core >=1.7.8 <1.8.0 (peer dependencies). Better Auth 1.7.7 fixed three security advisories, a critical Magic Link account takeover among them, and 1.7.8 a SAML replay check in @better-auth/sso; see versioning.
  • A real database. Replay protection depends on UNIQUE constraints, which Better Auth's in-memory adapter doesn't enforce.
  • With secondary storage (for example KV): verification: { storeInDatabase: true }. On Workers also rateLimit: { storage: "database" }, better-auth-cloudflare and the nodejs_compat flag.
  • Next.js: serverExternalPackages: ["better-auth-saml-idp"] in next.config. The plugin reads its XSD validator from its own package, which a bundled copy can't.

✅ Verified with

Live tests are run by hand; each has its date and evidence in DECISIONS.md.

Service providerWhat was verifiedSetup guide
Microsoft 365 (Entra ID federation)Sign-in to My Apps with a persistent NameID; Microsoft took the IdP's MFA claim; signing out at Microsoft ended the IdP sessionguide
Google WorkspaceSign-in to Google, with users created and suspended by better-auth-scim-provisioningguide
OktaSigned requests, plain and encrypted assertions, SP stored in the registryguide
Auth0Signed requests over Redirect and POST, encrypted assertionsguide
SalesforceSigned requests required, NameID matched to the Federation ID, Single Logout both waysguide
AWS IAM Identity CenterThe access portal and the AWS console in a permission setguide
Cloudflare AccessSigned requests, encrypted assertions, metadata refresh, key rotationguide
HubSpotGuide written, not yet run liveguide

In CI: Keycloak 26.8 and SimpleSAMLphp 2.5 in real Chromium over HTTPS; node-saml, samlify and @better-auth/sso on Node and workerd, including an identity broker; PostgreSQL 17, MySQL 8.4, MongoDB 8.2, SQLite and D1, through Kysely, Drizzle and Prisma. Testing with other SPs covers the rest.

📚 Documentation

To…Read
Set it up step by stepGetting started
See every optionOptions
Add SPs: in code, from metadata, at runtimeService providers
Map attributes and NameIDs, limit SPs to organizations, write access rulesUsers and access
Choose what's signed, encrypt assertions, rotate keysSigning and encryption, key rotation
Understand the endpoints and flows, IdP-initiated SSO, step-upFlows
Turn on Single LogoutSingle Logout
Give each organization its own IdPMulti-tenant
Provision accounts at the SPs tooSign-in and provisioning
Fix an errorErrors, troubleshooting
Check the security modelSecurity controls, threat model

The guide's index lists every page, including databases, schema, observability, the CLI and @better-auth/sso interop.

📊 How it compares

A feature-by-feature comparison with Shibboleth, SimpleSAMLphp, Keycloak, Zitadel, authentik, Logto, Ory Polis, Entra ID, Okta, Google Workspace and Auth0, with sources: interactive comparison (or docs/comparison.md).

Feature comparison with Shibboleth, SimpleSAMLphp, Keycloak, Zitadel, authentik, Logto, Ory Polis, Entra ID, Okta, Google Workspace and Auth0

🧭 What's next

The 1.x line covers everything on the roadmap derived from a feature comparison with eleven other SAML identity providers (checked 2026-10-15).

Not planned:

  • 🚫 Artifact binding: Of the products compared, only Keycloak supports it fully (Shibboleth and SimpleSAMLphp for responses); no target SP needs it.
  • 🚫 Admin UI in the plugin: Better Auth plugins are headless; hosts build their own UI on the registry API, and the example has a reference page to copy.

What shipped in each release is in the CHANGELOG.

🔒 Security

OpenSSF Scorecard OpenSSF Best Practices Socket CodeQL

  • docs/security.md: the threat model, what your host must do (don't use KV for sessions; leave cookieCache off), and known limitations.
  • DECISIONS.md: every security-relevant design decision, with its tests and mutation proofs.
  • SECURITY.md: how to report a vulnerability privately (not in a public issue), and the supply chain: actions pinned by SHA, CodeQL, dependency audits, OSV-Scanner, OpenSSF Scorecard, and npm provenance and a CycloneDX SBOM with every release.

🧪 Development

pnpm test          # Node (node:sqlite) + workerd (D1/Drizzle, validateSchema on)
pnpm test:wasm     # the libxml2 WASM validator, Node + workerd
pnpm e2e           # Playwright + Chromium: Keycloak, SimpleSAMLphp (Docker) and a node-saml SP
pnpm lint && pnpm typecheck && pnpm pack:check

CI runs these on Node 24 with Better Auth 1.7.8 and the latest 1.7.x, and on Node 22 with 1.7.8, plus a gitleaks scan, the database matrix and the Next.js example. CONTRIBUTING.md has the rest.

License and contributing

MIT © Mark McIntosh. Third-party components (libxml2 in wasm/xsd.wasm, and the OASIS/W3C schemas) are listed in THIRD_PARTY_NOTICES.md.

Contributions are welcome: bug reports, SP interop reports (especially "it works with X"), fixes and features. For anything significant, please open an issue first; see CONTRIBUTING.md. If the plugin is useful to you, a ⭐ helps others find it.

Keywords

better-auth

FAQs

Package last updated on 16 Oct 2026

Related posts