Search K
OIDC / 单点登录
SnapOtter 支持使用 OpenID Connect(OIDC)进行单点登录。用户可以使用 Keycloak、Authentik 或 Google 等外部身份提供商登录,作为本地用户名/密码认证的替代方案(或与之并存)。
快速开始
将这些环境变量添加到你的 docker-compose.yml 中:
yaml
services:
SnapOtter:
image: snapotter/snapotter:latest
environment:
EXTERNAL_URL: "https://photos.example.com"
OIDC_ENABLED: "true"
OIDC_ISSUER_URL: "https://auth.example.com/realms/myrealm"
OIDC_CLIENT_ID: "snapotter"
OIDC_CLIENT_SECRET: "your-secret-here"你的提供商的重定向 URI 始终为:
${EXTERNAL_URL}/api/auth/oidc/callback例如,如果 EXTERNAL_URL 是 https://photos.example.com,请将你的提供商的重定向 URI 配置为 https://photos.example.com/api/auth/oidc/callback。
配置参考
| 变量 | 默认值 | 说明 |
|---|---|---|
OIDC_ENABLED | false | 启用 OIDC 登录。登录页面上会出现一个“使用 SSO 登录”按钮。 |
OIDC_ISSUER_URL | 提供商的 issuer URL。必须支持 OIDC 发现(/.well-known/openid-configuration)。 | |
OIDC_CLIENT_ID | 在你的提供商处注册的 OAuth 客户端 ID。 | |
OIDC_CLIENT_SECRET | OAuth 客户端密钥。 | |
OIDC_SCOPES | openid profile email | 要请求的作用域列表,以空格分隔。 |
OIDC_AUTO_CREATE_USERS | true | 在首次 OIDC 登录时自动创建本地用户账户。 |
OIDC_DEFAULT_ROLE | user | 分配给自动创建的 OIDC 用户的角色。为 admin、editor 或 user 之一。 |
OIDC_AUTO_LINK_USERS | false | 如果电子邮件地址匹配,则将 OIDC 身份链接到现有的本地用户。 |
OIDC_PROVIDER_NAME | 显示在登录按钮上的名称(例如“Keycloak”、“Google”)。如果为空,按钮显示“SSO”。 | |
OIDC_CLOCK_TOLERANCE | 30 | 令牌验证时允许的时钟偏差容差(秒)。 |
OIDC_USERNAME_CLAIM | preferred_username | 用作新账户用户名的 ID 令牌声明。 |
EXTERNAL_URL | SnapOtter 可访问的公共 URL。OIDC 构建正确的重定向 URI 时需要此项。 | |
COOKIE_SECRET | 自动生成 | 用于签名会话 cookie 的密钥。运行多个副本时请显式设置此项。 |
提供商指南
Keycloak
- 创建一个新 realm(或使用现有的 realm)。
- 进入 Clients 并创建一个新 client:
- Client ID:
snapotter - Client authentication:On(保密型)
- Authentication flow:Standard flow(授权码)
- Client ID:
- 在该 client 的 Settings 选项卡下,将 Valid redirect URIs 设置为你的回调 URL(例如
https://photos.example.com/api/auth/oidc/callback)。 - 从 Credentials 选项卡复制 Client secret。
- 将
OIDC_ISSUER_URL设置为https://keycloak.example.com/realms/your-realm。
Authentik
- 在管理界面中,进入 Applications > Providers 并创建一个新的 OAuth2/OpenID Provider。
- Client type:Confidential
- Redirect URIs:你的回调 URL
- Signing key:选择现有密钥或创建一个
- 创建一个 Application 并将其链接到该 provider。
- 从 provider 设置中复制 Client ID 和 Client Secret。
- 将
OIDC_ISSUER_URL设置为https://authentik.example.com/application/o/snapotter/(末尾的斜杠很重要)。
Google
- 进入 Google Cloud Console。
- 创建一个项目(或选择现有项目)。
- 导航到 APIs & Services > OAuth consent screen 并进行配置。
- 进入 APIs & Services > Credentials 并创建一个 OAuth 2.0 Client ID:
- Application type:Web application
- Authorized redirect URIs:你的回调 URL
- 复制 Client ID 和 Client secret。
- 将
OIDC_ISSUER_URL设置为https://accounts.google.com。 - 将
OIDC_USERNAME_CLAIM设置为email(Google 不提供preferred_username)。
用户预置
自动创建
当 OIDC_AUTO_CREATE_USERS 为 true(默认)时,会在某人首次通过 OIDC 登录时创建一个本地用户账户。用户名取自 OIDC_USERNAME_CLAIM 指定的声明,角色设置为 OIDC_DEFAULT_ROLE。
如果发生用户名冲突,会追加一个数字后缀(例如 jane 变为 jane_2)。
自动链接
当 OIDC_AUTO_LINK_USERS 为 true 时,如果电子邮件地址匹配,SnapOtter 会将 OIDC 身份链接到现有的本地账户。当你已预先创建用户账户,并希望他们在不丢失数据的情况下开始使用 SSO 时,这很有用。
WARNING
只有在你信任 OIDC 提供商会验证电子邮件地址的情况下,才启用自动链接。未经验证的电子邮件可能会让某人接管另一个用户的账户。
禁用本地登录
OIDC 不会禁用本地用户名/密码登录。两种方式都仍然可用。如果 OIDC 提供商无法访问,管理员仍可使用本地凭据登录。
自签名证书
如果你的 OIDC 提供商使用自签名或私有 CA 证书,请将 CA 证书包挂载到容器中,并将 NODE_EXTRA_CA_CERTS 指向它:
yaml
services:
SnapOtter:
image: snapotter/snapotter:latest
volumes:
- ./my-ca.pem:/etc/ssl/certs/custom-ca.pem:ro
environment:
NODE_EXTRA_CA_CERTS: /etc/ssl/certs/custom-ca.pem
OIDC_ENABLED: "true"
OIDC_ISSUER_URL: "https://auth.internal.example.com/realms/myrealm"
OIDC_CLIENT_ID: "snapotter"
OIDC_CLIENT_SECRET: "your-secret-here"DANGER
请勿设置 NODE_TLS_REJECT_UNAUTHORIZED=0。这会禁用所有 TLS 验证,存在安全风险。
故障排查
重定向 URI 不匹配
最常见的错误。检查你的提供商所期望的内容与 SnapOtter 发送的内容之间是否存在以下差异:
http与https之间,scheme 必须完全一致- 末尾斜杠,有些提供商对此很严格
- 端口号,如果是非标准端口,请包含端口
- 路径,必须是
/api/auth/oidc/callback
请再次核对 EXTERNAL_URL。它必须与用户在浏览器中输入的 URL 一致。
UNABLE_TO_VERIFY_LEAF_SIGNATURE
OIDC 提供商使用了 Node.js 不信任的证书。请参阅上文的自签名证书。
时钟偏差错误
如果你的服务器时钟与 OIDC 提供商时钟不同步,令牌验证可能会失败。增大 OIDC_CLOCK_TOLERANCE(默认为 30 秒)。更好的办法是在两台机器上都运行 NTP。
“OIDC 提供商无法访问”
SnapOtter 会在启动时和登录期间获取提供商的发现文档。请检查:
- 从 Docker 容器内部进行的 DNS 解析(
docker exec snapotter nslookup auth.example.com) - 容器与提供商之间的防火墙规则
OIDC_ISSUER_URL的值,它必须能从服务器访问,而不仅仅是从你的浏览器访问
缺少声明
如果登录后用户名或电子邮件为空,你的提供商可能没有返回预期的声明。请验证:
- 在
OIDC_SCOPES中配置的作用域包含profile和email - 提供商已配置为在 ID 令牌中包含
OIDC_USERNAME_CLAIM指定的声明 - 有些提供商需要显式的 mapper/scope 配置才能释放声明
