如何配置 SSO 提供方

页面摘要: 通过在 Strapi 管理面板中配置 SSO 提供方,注册 OAuth/OIDC 应用,将凭据添加到 /config/admin 的 auth.providers 中,并设置回调 URL 以启用额外的登录方式。

(Enterprise 计划) (SSO)

单点登录(SSO) 允许你为 Strapi 管理面板配置额外的登录和注册方式。

WARNING
  • 要在你的应用上配置 SSO,你需要一个 (Enterprise 计划) 套餐或 (SSO) 附加组件。
  • 确保 Strapi 是你可以通过你的提供方访问的应用之一。例如,对于 Microsoft (Azure) Active Directory,你必须首先请具有相应权限的人将 Strapi 添加到允许的应用列表中。请参阅你的提供方文档以了解更多相关信息。
WARNING
  • 目前无法将唯一的 SSO 提供方关联到用于 Strapi 账户的电子邮件地址,这意味着对 Strapi 账户的访问不能限制为仅一个 SSO 提供方。有关此问题的更多信息及解决方法,请参阅专门的 GitHub issue。
  • 使用 SSO 时,目前无法将管理面板和后端部署在完全不相关、彼此独立的域名上。

访问配置

SSO 配置位于 /config/admin 文件中。

提供方的配置应作为 提供方配置 的数组写入管理面板的 auth.providers 路径中:

JavaScript


module.exports = ({ env }) => ({
  // ...
  auth: {
    providers: [], // 提供方的配置位于此处
  },
});

TypeScript


export default ({ env }) => ({
  // ...
  auth: {
    providers: [], // 提供方的配置位于此处
  },
});

设置提供方配置

以下文档的某些部分假定此前已在 Strapi 和你的身份提供方中完成某些步骤。如果跳过这些步骤,登录按钮可能会出现在 Strapi 登录页面上,但流程会失败并显示重定向错误或 "invalid client" 错误。在继续阅读文档其余部分之前,请确保遵循清单中的所有步骤。

  •  在 Strapi 中启用 SSO 在管理面板中转到 Global settings > Single Sign-On 并配置该功能(例如切换自动注册并选择默认角色)。
  •  在你的身份提供方中注册 Strapi 在提供方的仪表板(例如 Azure AD、Okta、Google、GitHub)中,为 Strapi 创建一个新的 OAuth/OIDC 应用。复制提供方生成的客户端 ID 和客户端密钥。
  •  将 Strapi 回调 URL 添加到提供方 将提供方配置中的重定向/回调 URL 设置为 {"strapi.admin.services.passport.getStrategyCallbackURL('<provider_uid>')"} 生成的值(例如,如果 UID 是 google,则为 /admin/connect/google)。提供方必须接受此 URL,否则登录将被阻止。
  •  向 Strapi 提供凭据 将客户端 ID 和客户端密钥作为环境变量添加(例如 GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET),以便它们可以在 {"/config/admin.js|ts"} 中读取。
  •  在代码中配置提供方 导入提供方的 Passport 策略并将其添加到 auth.providers。
  •  重新构建并重启 Strapi 运行 yarn build && yarn develop 或 npm run build && npm run develop,以便新提供方出现在登录页面上。如果管理面板是单独托管的,还请确保 url 设置与已部署的管理面板 URL 匹配(参见主机、端口和路径。

提供方的配置是一个使用以下属性构建的 JavaScript 对象:

NameRequiredTypeDescription
uidYesString策略的 UID。它必须与策略的名称匹配。
displayNameYesString登录页面上用于引用该提供方的名称。
createStrategyYesFunction一个工厂,将构建并返回一个用于你的提供方的新 passport 策略。以 strapi 实例作为参数。
iconNoString一个图片 URL。如果指定,它将替换登录页面上的 displayName。
NOTE

uid 属性是每个策略的唯一标识符,通常可以在策略的包中找到。如果你不确定它指的是什么,请联系该策略的维护者。

默认情况下,Strapi 安全策略不允许从外部 URL 加载图片,因此在管理面板的登录屏幕上不会显示提供方 logo,除非通过中间件配置添加安全例外,如下例所示:

JavaScript

module.exports = [
  // ...
  {
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          'connect-src': ["'self'", 'https:'],
          'img-src': [
            "'self'",
            'data:',
            'blob:',
            'dl.airtable.com',
            'www.okta.com', // 提供方 logo 的基础 URL
          ],
          'media-src': [
            "'self'",
            'data:',
            'blob:',
            'dl.airtable.com',
            'www.okta.com', // 提供方 logo 的基础 URL
          ],
          upgradeInsecureRequests: null,
        },
      },
    },
  },
  // ...
]

TypeScript

export default [
  // ...
  {
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          'connect-src': ["'self'", 'https:'],
          'img-src': [
            "'self'",
            'data:',
            'blob:',
            'dl.airtable.com',
            'www.okta.com', // 提供方 logo 的基础 URL
          ],
          'media-src': [
            "'self'",
            'data:',
            'blob:',
            'dl.airtable.com',
            'www.okta.com', // 提供方 logo 的基础 URL
          ],
          upgradeInsecureRequests: null,
        },
      },
    },
  },
  // ...
]

当将管理面板部署到不同位置或不同子域名时,需要进行额外配置以设置 cookie 的公共域名。这是为了确保 cookie 在域之间共享所必需的。

JavaScript

module.exports = ({ env }) => ({
  auth: {
    domain: env("ADMIN_SSO_DOMAIN", ".test.example.com"),
    providers: [
      // ...
    ],
  },
  url: env("ADMIN_URL", "http://admin.test.example.com"),
  // ...
});

TypeScript

export default ({ env }) => ({
  auth: {
    domain: env("ADMIN_SSO_DOMAIN", ".test.example.com"),
    providers: [
      // ...
    ],
  },
  url: env("ADMIN_URL", "http://admin.test.example.com"),
  // ...
});

createStrategy 工厂

passport 策略通常通过使用 2 个参数实例化来构建:配置对象和验证函数。

配置对象

配置对象取决于策略的需求,但通常要求提供一个回调 URL,以便在提供方一侧建立连接后重定向到该 URL。

可以使用 getStrategyCallbackURL 方法为你的提供方生成特定的回调 URL。此 URL 也需要写在提供方一侧,以允许从中重定向。

回调 URL 的格式如下:/admin/connect/<provider_uid>。

TIP

strapi.admin.services.passport.getStrategyCallbackURL 是一个 Strapi 辅助函数,可用于获取特定提供方的回调 URL。它以提供方名称作为参数并返回 URL。

如有需要,你还可以在此处放置 OAuth2 应用的客户端 ID 和密钥。

验证函数

验证函数在此处用作中间件,允许你对从提供方 API 返回的数据进行转换和额外处理。

此函数始终以 done 方法作为最后一个参数,该方法用于将所需数据传递给 Strapi 的 SSO 层。

其签名如下:void done(error: any, data: object);,并遵循以下规则:

  • 如果 error 未设为 null,则发送的数据将被忽略,控制器将抛出错误。
  • 如果 SSO 的自动注册功能被禁用,则 data 对象只需要由 email 属性组成。
  • 如果 SSO 的自动注册功能已启用,则你需要在 data 对象中定义(除了 email 之外)username 属性,或同时定义 firstname 和 lastname。

添加提供方

添加新的提供方意味着为你的管理员添加新的登录方式。

Strapi 使用 Passport.js,它支持大量提供方。因此,任何不需要额外自定义数据的有效 passport 策略都应该可以与 Strapi 一起使用。

WARNING

ldapauth 等策略无法开箱即用,因为它们需要从管理面板发送额外的数据。 如果你想向应用添加 LDAP 提供方,你将需要编写一个 自定义策略。 你也可以使用 Okta 和 Auth0 等服务作为桥接服务。

配置提供方

要配置提供方,请遵循以下流程:

  1. 确保将你的策略导入到管理面板配置文件中,可以从已安装的包或本地文件导入。
  2. 向管理面板配置中的 auth.providers 数组添加一个与上述格式匹配的新项。
  3. 使用 yarn build && yarn develop 或 npm run build && npm run develop 重新构建并重启你的应用。该提供方应出现在你的管理面板登录页面上。

提供方配置示例

以下示例展示了如何为最常见的提供方配置 SSO:

  • Google — 了解如何配置 Google 与 Strapi 的 SSO。
  • GitHub — 了解如何配置 GitHub 与 Strapi 的 SSO。
  • Discord — 了解如何配置 Discord 与 Strapi 的 SSO。
  • Microsoft — 了解如何配置 Microsoft 与 Strapi 的 SSO。
  • Keycloak — 了解如何配置 Keycloak 与 Strapi 的 SSO。
  • Okta — 了解如何配置 Okta 与 Strapi 的 SSO。

执行高级自定义

管理面板 URL

如果管理面板所在的主机/端口与 Strapi 服务器不同,则需要更新管理面板 URL:更新/config/admin 文件中的 url 键。

自定义逻辑

在某些场景中,你会希望为连接工作流编写额外的逻辑,例如:

  • 限制特定域名的连接和注册
  • 在尝试连接时触发操作
  • 添加分析

最简单的方法是将代码插入到你的策略的验证函数中。

例如,如果你只想允许使用官方 strapi.io 电子邮件地址的人,可以按如下方式实例化你的策略:

JavaScript


const strategyInstance = new Strategy(configuration, ({ email, username }, done) => {
  // 如果电子邮件以 @strapi.io 结尾
  if (email.endsWith('@strapi.io')) {
    // 则我们使用提供方给出的数据继续
    return done(null, { email, username });
  }

  // 否则,我们通过向 done 函数发送错误来继续
  done(new Error('Forbidden email address'));
});

TypeScript


const strategyInstance = new Strategy(configuration, ({ email, username }, done) => {
  // 如果电子邮件以 @strapi.io 结尾
  if (email.endsWith('@strapi.io')) {
    // 则我们使用提供方给出的数据继续
    return done(null, { email, username });
  }

  // 否则,我们通过向 done 函数发送错误来继续
  done(new Error('Forbidden email address'));
});

身份验证事件

SSO 功能添加了一个新的身份验证事件:onSSOAutoRegistration。

每当用户使用 SSO 添加的自动注册功能创建时,就会触发此事件。 它包含已创建的用户(event.user)和用于注册提供方(event.provider)。

JavaScript


module.exports = () => ({
    auth: {
      // ...
      events: {
        onConnectionSuccess(e) {},
        onConnectionError(e) {},
        // ...
        onSSOAutoRegistration(e) {
          const { user, provider } = e;

          console.log(
            `A new user (${user.id}) has been automatically registered using ${provider}`
          );
        },
      },
    },
});

TypeScript


export default () => ({
    auth: {
      // ...
      events: {
        onConnectionSuccess(e) {},
        onConnectionError(e) {},
        // ...
        onSSOAutoRegistration(e) {
          const { user, provider } = e;

          console.log(
            `A new user (${user.id}) has been automatically registered using ${provider}`
          );
        },
      },
    },
});