8.使用 OpenId Connect 启用 SSO 支持
对应的官方页面地址
‼️ ‼️ ‼️
SSO 目前仅适用于 :testing 标签的镜像!
当前的稳定版 v1.34.3 不包含 SSO 功能。
‼️ ‼️ ‼️
使用 OpenId Connect 的 SSO
要使用外部身份验证源,您的 SSO 需要支持 OpenID Connect:
OpenID Connect 发现端点需要可用
客户端认证将使用 ID 和 Secret 完成
仍然需要主密码,且不由 SSO 控制(根据您的观点,这可能是一个功能)。这引入了另一种控制谁可以使用密码库的方式,而无需使用邀请或使用 LDAP。
配置
以下配置可用:
SSO_ENABLED:启用 SSOSSO_ONLY:禁用电子邮箱 + 主密码认证SSO_SIGNUPS_MATCH_EMAIL:在 SSO 注册时,如果存在具有相同电子邮件地址的用户,则进行关联(默认true)SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION:允许未知电子邮箱验证状态(默认false)。允许此功能与SSO_SIGNUPS_MATCH_EMAIL结合使用可能会带来账户接管的风险。SSO_AUTHORITY:您的 SSO 的 OpenID Connect Discovery 端点不应包含
/.well-known/openid-configuration部分,且无/尾随$SSO_AUTHORITY/.well-known/openid-configuration 应返回一个 JSON 文档:https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse
SSO_SCOPES:可选,允许在需要时覆盖范围(默认"email profile")SSO_AUTHORIZE_EXTRA_PARAMS:可选,允许在授权重定向时添加额外参数(默认"")SSO_PKCE:为授权代码流程激活 PKCE(默认true)。SSO_AUDIENCE_TRUSTED:可选,用于信任 ID Token 的额外受众的正则表达式(client_id始终受信任)。编写正则表达式时使用单引号:'^$'。SSO_CLIENT_ID:客户端 IDSSO_CLIENT_SECRET:客户端机密SSO_MASTER_PASSWORD_POLICY:可选的主密码策略(不支持enforceOnLogin)。SSO_AUTH_ONLY_NOT_SESSION:启用的话表示仅用于身份验证,不用于会话生命周期SSO_CLIENT_CACHE_EXPIRATION:缓存对发现端点的调用,持续时间(秒),0禁用(默认0);SSO_DEBUG_TOKENS:记录所有令牌以便于调试(默认false,需要设置LOG_LEVEL=debug或LOG_LEVEL=info,vaultwarden::sso=debug)
回调 URL 为: https://your.domain/identity/connect/oidc-signin
账户和电子邮箱处理
使用 SSO 登录时,一个标识符(来自 IdToken 的 {iss}/{sub} 声明)会保存在一个单独的表(sso_users)中。该标识符用于链接到 SSO 提供程序标识符,而无需更改默认用户 uuid。之所以需要这样做,是因为:
存储 SSO 标识对于防止因更改电子邮箱而导致账户被接管非常重要。
我们不能使用标识符作为用户 uuid,因为它太长了(
sub部分最多 255 个字符,参见规范)。我们希望能根据电子邮箱关联现有账户,但仅限于用户首次登录时(由
SSO_SIGNUPS_MATCH_EMAIL控制)。我们需要能够关联现有的存根账户,例如在邀请用户加入组织时创建的账户(只有在用户没有私钥的情况下才能关联)。
此外:
如果提供程序报告电子邮箱
unverified,注册将被阻止。更改电子邮箱需要用户自己完成,因为这需要更新
key。登录时,如果提供程序返回的电子邮箱不是已保存的电子邮箱,系统将向用户发送一封电子邮件,要求他更新电子邮箱。如果设置了
SIGNUPS_DOMAINS_WHITELIST,则会在 SSO 注册和尝试更改电子邮箱时应用。
这意味着,如果需要更改提供程序网址或提供程序本身,必须先删除关联,然后确保 SSO_SIGNUPS_MATCH_EMAIL 已激活,以允许新的关联。
要删除关联(这对 Vaultwarden 用户没有影响):
TRUNCATE TABLE sso_users;对于 SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION
SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION 如果提供程序不发送电子邮件的验证状态(email_verified claim),则需要激活此设置。
如果设置为 SSO_SIGNUPS_MATCH_EMAIL=true(默认值),那么即使用户不控制电子邮箱地址,也可以与现有的非 SSO 账户关联。这样,用户就可以访问敏感信息,但仍需要主密码才能读取密码。
因此,在使用 SSO_ALLOW_UNKNOWN_EMAIL_VERIFICATION 时,建议禁用 SSO_SIGNUPS_MATCH_EMAIL。如果需要关联非 SSO 用户,请尽量在最短时间内同时激活这两个设置。
客户端缓存
默认情况下,客户端缓存是禁用的,因为它会导致签名密钥出现问题。
这意味着每次我们需要与提供程序交互(生成 authorize_url、交换授权代码、刷新令牌)时,都要再次调用发现端点。这种情况并不理想,因此 SSO_CLIENT_CACHE_EXPIRATION 允许您配置一个适合您的提供程序的过期时间。
如果 IdToken 验证失败,客户端缓存就会失效(但您会定期遇到一个倒霉的用户 ^^),这是防止过期配置错误的一种保护措施。
Google 示例(滚动密钥)
以 Goole 为例,在检查发现端点响应报头时,我们可以看到缓存控制的 max-age 被设置为 3600 秒。而 jwk_uri 响应头通常包含一个更大值的 max-age。结合用户反馈,我们可以得出结论:Goole 每周都会更新签名密钥。
缓存过期时间设置过高会导致回报率降低,但使用 600(10 分钟)这样的过期时间应该会带来很多好处。
手动滚动密钥
如果要滚动已使用的密钥,请先添加一个新的密钥,但不要立即开始使用它签名。等待在 SSO_CLIENT_CACHE_EXPIRATION 中配置的延迟时间,然后就可以开始使用它签名了。
正如 Google 示例中提到的,即使不打算滚动密钥,设置过高的值也会导致回报率降低。
Keycloak
默认访问令牌有效期可能只有 5min,请设置更大的值,否则会与同样设置为 5min的 Bitwarden 前端过期检测冲突。
在领域级别:
Realm settings / Tokens / Access Token Lifespan至少设置为10min(使用kcadm.sh时的accessTokenLifespan设置)。Realm settings / Sessions / SSO Session Idle/Max用于刷新令牌的生命周期
或者对于在 Clients / Client details / Advanced / Advanced settings 中的特定客户端,可以找到 Access Token Lifespan 和 Client Session Idle/Max。
服务器配置,无特别的设置:
SSO_AUTHORITY=https://${domain}/realms/${realm_name}SSO_CLIENT_IDSSO_CLIENT_SECRET
测试
如果您想运行 Keycloak 的测试实例,可以使用 Playwright docker-compose。有关使用方法的更多详情,请参阅 README.md。
Auth0
由于以下问题而无法运行 https://github.com/ramosbugs/openidconnect-rs/issues/23(它们似乎不符合规范)。有一个功能标志 (oidc-accept-rfc3339-timestamps) 可以绕过这个问题,但您需要用它来编译服务器。目前还没有计划始终激活该功能或为 Auth0 制作特定的发行版。
Authelia
要获取 refresh_token 以扩展会话,您需要添加 Offline_Access 范围。
配置看起来如下:
SSO_SCOPES="email profile offline_access"
Authentik
默认访问令牌有效期可能只有 5min,请设置更大的值,否则会与同样设置为 5min的 Bitwarden 前端过期检测冲突。
要更改令牌有效期,请转到 Applications / Providers / Edit / Advanced protocol settings。
从 2024.2 版本开始,您需要添加 Offline_Access 范围,并确保在 Applications / Providers / Edit / Advanced protocol settings / Scopes 中选中它(文档)。
服务器配置看起来应如下:
SSO_AUTHORITY=https://${domain}/application/o/${application_name}/:尾部的/很重要SSO_SCOPES="email profile offline_access"SSO_CLIENT_IDSSO_CLIENT_SECRET
疑难解答
Failed to discover OpenID provider:/Failed to parse server response:( 检测 OpenID 提供程序失败 / 解析服务器响应失败):首先确保可以访问添加了
/.well-known/openid-configuration的 Authority 端点。然后检查文件是否返回了
id_token_signing_alg_values_supported: ["RS256"]。如果返回HS256,那么再次选择默认签名密钥应该能解决该问题(步骤)。
Failed to contact token endpoint: Parse(Error ... Invalid JSON web token: found 5 parts:该错误可能是由加密令牌 (JWE) 导致的,请确保未使用加密密钥(步骤)。
Casdoor
自版本 v1.639.0 起应该可以工作(已使用版本 v1.686.0 进行测试)。创建应用程序时,您需要选择 Token format -> JWT-Standard。
然后使用以下内容配置您的服务器:
SSO_AUTHORITY=https://${provider_host}SSO_CLIENT_IDSSO_CLIENT_SECRET
GitLab
使用以下内容在 Gitlab 设置中创建一个应用程序:
redirectURI:https://your.domain/identity/connect/oidc-signinConfidential:truescopes:openid,profile,email
然后使用以下内容配置您的服务器:
SSO_AUTHORITY=https://gitlab.comSSO_CLIENT_IDSSO_CLIENT_SECRET
Google Auth
Google 文档。默认情况下,如果没有额外配置,您将没有 refresh_token,会话时间将限制为 1 小时。
使用以下内容配置您的服务器:
SSO_AUTHORITY=https://accounts.google.comSSO_AUTHORIZE_EXTRA_PARAMS="access_type=offline&prompt=consent"SSO_CLIENT_IDSSO_CLIENT_SECRET
Kanidm
只需设置 SSO_AUTHORITY、SSO_CLIENT_ID 和 SSO_CLIENT_SECRET,无需任何其他特别的设置。
Microsoft Entra ID
在 Entra ID 中按照 Identity | Applications | App registrations 创建「应用程序注册」。
在「应用程序注册」的「概述」中,您需要「目录(租户)ID」以用于
SSO_AUTHORITY变量,「应用程序(客户端)ID」以用于SSO_CLIENT_ID值。在「证书和秘钥」中创建「应用程序秘钥」,您需要「秘钥值」以用于
SSO_CLIENT_SECRET。在「身份验证」中添加 https://warden.example.org/identity/connect/oidc-signin 作为「网页重定向 URI」。
在「API 权限」中,确保在「API / 权限名称」下列出
profile、email和offline_access(offline_access是必需的,否则不会返回refresh_token,请参阅 https://github.com/MicrosoftDocs/azure-docs/issues/17134)。
只有 v2 端点符合 OpenID 规范,请参阅 https://github.com/MicrosoftDocs/azure-docs/issues/38427 和 https://github.com/ramosbugs/openidconnect-rs/issues/122。
您的服务器配置看起来应如下:
SSO_AUTHORITY=https://login.microsoftonline.com/${Directory_ID}/v2.0#租户SSO_SCOPES=openid profile offline_access User.ReadSSO_CLIENT_ID=${Application_ID}#客户端SSO_CLIENT_SECRET=${Secret_Value}
Rauthy
要使用提供程序控制的会话,您需要在运行 Rauthy 时使用 DISABLE_REFRESH_TOKEN_NBF=true,否则服务器在尝试读取尚未生效的 refresh_token 时会失败(即使 access_token 有效,Bitwarden 客户端也会触发刷新。详情参阅 rauthy)。另一种方法是使用 SSO_AUTH_ONLY_NOT_SESSION=true 的默认会话处理方式。
创建客户端时不需要特殊配置。
您的配置看起来应如下:
SSO_AUTHORITY=http://${provider_host}/auth/v1SSO_CLIENT_ID=${Client ID}SSO_CLIENT_SECRET=${Client Secret}SSO_AUTH_ONLY_NOT_SESSION=true,仅在不使用DISABLE_REFRESH_TOKEN_NBF=true运行Rauthy时才需要
Slack
您将需要在https://api.slack.com/apps/中创建一个 App。
看起来返回的 access_token 不是 JWT 格式,并且没有使用它发送到期日期。因此,您需要使用默认的会话生命周期。
您的配置看起来应如下:
SSO_AUTHORITY=https://slack.comSSO_CLIENT_ID=${Application Client ID}SSO_CLIENT_SECRET=${Application Client Secret}SSO_AUTH_ONLY_NOT_SESSION=true
Zitadel
要获取 refresh_token 以扩展会话,您需要添加 Offline_Access 范围。
此外,Zitadel 还在 Id Token 的受众中加入了 Project id 和客户 Client Id。为使验证生效,您需要将 Resource Id 添加为受信任的受众(默认情况下 Client Id 是受信任的)。您可以使用 SSO_AUDIENCE_TRUSTED 配置来控制受信任的受众。
根据 Zitadel#9200,id_token 会传递一个受信任受众列表,其中包括 Project Id。如果最终有许多受信任的 aud 字符串,SSO_AUDIENCE_TRUSTED 可能会变得难以管理。在这种情况下,SSO_AUDIENCE_TRUSTED: '^\d{18}$'(18 是 aud 列表中每个字符串的大小,可能因 Zitadel 实现而异)会对您有所帮助,但单独添加所有审核字符串也是安全的,如 SSO_AUDIENCE_TRUSTED:'^abcd|def|xyz$'。
自 zitadel#721 以来,PKCE 应该可以使用客户端机密。但旧版本可能需要禁用它(SSO_PKCE=false)。
配置看起来应如下:
SSO_AUTHORITY=https://${provider_host}SSO_SCOPES="email profile offline_access"SSO_CLIENT_IDSSO_CLIENT_SECRETSSO_AUDIENCE_TRUSTED='^${Project Id}$'
会话生命周期
会话生命周期取决于刷新令牌和调用 SSO 令牌端点(授权类型:authorization_code)后返回的访问令牌。如果没有返回刷新令牌,会话将仅限于访问令牌的有效期。
令牌不会持久保存在服务器中,而是封装在 JWT 令牌中并返回给应用程序(VW identity/connect/token 端点返回的 refresh_token 和 access_token 值)。请注意,出于与 Web 前端兼容的原因,服务器将始终返回一个 refresh_token,它的存在并不表明 SSO 返回了刷新令牌(但您可以使用 https://jwt.io 对其值进行解码,然后检查 token 字段是否包含任何内容)。
有了刷新令牌,应用程序中的活动就会在访问令牌快过期时(网页客户端为 5 分钟)触发刷新。
此外,在执行某些操作时还会进行令牌检查,如果有刷新令牌,就会执行刷新,否则就会调用用户信息端点来检查访问令牌的有效性。
禁用 SSO 会话处理
如果无法获取 efresh_token 或出于其他原因,可以禁用 SSO 会话处理,以恢复到默认的处理方式。您需要启用 SSO_AUTH_ONLY_NOT_SESSION=true,然后访问令牌的有效期将为 2 小时,刷新令牌的空闲时间为 7 天(可无限期延长)。
调试信息
使用 LOG_LEVEL=debug 运行,您就能看到令牌过期的信息。
桌面客户端
在处理从浏览器(用于 SSO 登录)到应用程序的重定向方面存在一些问题。
Chrome
一些用户报告有说存在问题。
Firefox
在 Windows 系统中,首次登录时会出现一个提示,以确认应启动哪个应用程序(但目前存在一个 bug,即登录后可能会出现空密码库)。
在 Linux 系统上,稍微麻烦些。首先,您需要在 about:config 中添加一些配置:
network.protocol-handler.expose.bitwarden=false
network.protocol-handler.external.bitwarden=true如果您有任何疑问,可以检查 mailto 以查看它是如何配置的。
重定向仍然不起作用,因为与应用程序的关联似乎只能通过链接/点击来完成。您可以用一个虚拟页面来触发它,例如:
data:text/html,<a href="bitwarden:///dummy">Click me to register Bitwarden</a>从现在起,重定向应该可以正常工作了。如果需要更改启动的应用程序,现在可以使用搜索功能在 Settings 中输入 application 进行查找。
最后更新于