使用 Okta 的 SAML 单点登录

一份使用 Okta 集成 SAML 单点登录(SSO)到 Better Auth 的指南

本指南将引导你使用身份提供商(IdP)设置 SAML 单点登录(SSO),以 Okta 为示例。有关高级配置细节及完整 API 参考,请查阅 SSO 插件文档

什么是 SAML?

SAML(安全断言标记语言)是一种基于 XML 的标准,用于在身份提供商(IdP)(例如 Okta、Azure AD、OneLogin)与服务提供商(SP)(此处为 Better Auth)之间交换身份验证和授权数据。

在该配置中:

  • IdP(Okta):验证用户身份并发送有关其身份的断言。
  • SP(Better Auth):验证断言并将用户登录。

第一步:在 Okta 中创建 SAML 应用

  1. 登录你的 Okta 管理控制台

  2. 导航到 Applications > Applications

  3. 点击“创建应用集成”

  4. 选择“ SAML 2.0 ”作为登录方式

  5. 配置以下设置:

    • 单点登录 URL:你的 Better Auth 回调端点(例如 http://localhost:3000/api/auth/sso/saml2/callback/sso)。注意:sso 是你的 providerId
    • 受众 URI(SP 实体 ID):你的 Better Auth 元数据 URL(例如 http://localhost:3000/api/auth/sso/saml2/sp/metadata
    • Name ID 格式:电子邮件地址或你选择的任意格式。
  6. 下载 IdP 元数据 XML 文件和证书

IdP 发起的 SSO:如果你希望用户从 Okta 仪表盘访问你的应用,请确保 单点登录 URL 指向回调端点(/api/auth/sso/saml2/callback/{providerId})。Better Auth 会自动处理 SP 发起和 IdP 发起的流程。

第二步:配置 Better Auth

以下是在开发环境中为 Okta 配置的示例:

const ssoConfig = {
  defaultSSO: [{
    domain: "localhost:3000", // 你的域名
    providerId: "sso",
    samlConfig: {
      // 服务提供商配置
      issuer: "http://localhost:3000/api/auth/sso/saml2/sp/metadata",
      entryPoint: "https://trial-1076874.okta.com/app/trial-1076874_samltest_1/exktofb0a62hqLAUL697/sso/saml",
      callbackUrl: "/dashboard", // 成功认证后的重定向地址
      
      // 身份提供商配置
      idpMetadata: {
        entityID: "https://trial-1076874.okta.com/app/exktofb0a62hqLAUL697/sso/saml/metadata",
        singleSignOnService: [{
          Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
          Location: "https://trial-1076874.okta.com/app/trial-1076874_samltest_1/exktofb0a62hqLAUL697/sso/saml"
        }],
      },
      cert: `-----BEGIN CERTIFICATE-----
MIIDqjCCApKgAwIBAgIGAZhVGMeUMA0GCSqGSIb3DQEBCwUAMIGVMQswCQYDVQQGEwJVUzETMBEG
...
[你的 Okta 证书]
...
-----END CERTIFICATE-----`,
      
      // 服务提供商元数据
      spMetadata: {
        metadata: `<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" 
          entityID="http://localhost:3000/api/sso/saml2/sp/metadata">
          ...
          [你的 SP 元数据 XML]
          ...
        </md:EntityDescriptor>`
      }
    }
  }]
}

第三步:多个默认提供商(可选)

你可以为不同域配置多个 SAML 提供商:

const ssoConfig = {
  defaultSSO: [
    {
      domain: "company.com",
      providerId: "company-okta",
      samlConfig: {
        // company.com 的 Okta SAML 配置
      }
    },
    {
      domain: "partner.com", 
      providerId: "partner-adfs",
      samlConfig: {
        // partner.com 的 ADFS SAML 配置
      }
    },
    {
      domain: "contractor.org",
      providerId: "contractor-azure",
      samlConfig: {
        // contractor.org 的 Azure AD SAML 配置
      }
    }
  ]
}

显式:登录时直接传递 providerId。 域名回退:基于用户邮箱域名匹配。例如 user@company.com → 匹配 company-okta 提供商。

第四步:启动登录

你可以通过三种方式启动 SSO 流程:

1. 通过 providerId 显式指定(推荐):

// 显式指定使用哪个提供商
await authClient.signIn.sso({
  providerId: "company-okta",
  callbackURL: "/dashboard"
});

2. 根据邮箱域名匹配:

// 根据邮箱域名自动匹配提供商
await authClient.signIn.sso({
  email: "user@company.com",
  callbackURL: "/dashboard"
});

3. 指定域名进行匹配:

// 显式指定域名进行匹配
await authClient.signIn.sso({
  domain: "partner.com",
  callbackURL: "/dashboard"
});

重要说明

  • DummyIDP 只应在开发和测试环境使用
  • 请勿在生产环境中使用示例证书
  • 示例中使用了 localhost:3000,请根据环境调整 URL
  • 生产环境应始终使用正式的 IdP 提供商,如 Okta、Azure AD 或 OneLogin
  • SAML 配置中的 callbackUrl 应指向你应用的目标地址(如 /dashboard),而非回调路由本身

第五步:动态注册 SAML 提供商

要实现动态注册,请通过 API 注册 SAML 提供商。详细注册说明请参阅 SSO 插件文档

注册示例:

await authClient.sso.register({
  providerId: "okta-prod",
  issuer: "https://your-domain.com",
  domain: "your-domain.com",
  samlConfig: {
    // 你的生产环境 SAML 配置
  }
});

额外资源