邮件与 OAuth2
配置存储位置
Section titled “配置存储位置”邮件登录和 OAuth2 provider 都通过 runtime settings 配置。邮件使用 email 分组,OAuth2 provider 使用 oauth2.providers。
邮件配置存储在 PostgreSQL,并在集群节点间热更新:
synctv settings update \ --set email.smtpHost=smtp.example.com \ --set email.smtpPort=465 \ --set 'email.smtpCredentials={"username":"synctv@example.com","password":"secret"}' \ --set 'email.smtpProxy={"url":"socks5://proxy.example.com:1080"}' \ --set email.useTls=true \ --set email.fromEmail=synctv@example.com \ --set email.fromName=SyncTV \ --set email.enabled=true| 字段 | 默认值 | 作用 |
|---|---|---|
email.enabled |
false |
启用邮件发送 |
email.smtpHost |
null |
SMTP 服务器地址 |
email.smtpPort |
587 |
SMTP 端口 |
email.smtpCredentials |
null |
可选 SMTP 登录凭据 {username, password} |
email.smtpProxy |
null |
可选 SOCKS5 代理 {url, credentials?} |
email.fromEmail |
null |
发件邮箱,启用邮件时必须是合法地址 |
email.fromName |
SyncTV |
发件人显示名 |
email.useTls |
true |
是否使用 TLS 连接 SMTP |
updateMask 指定需要覆盖的字段。清除 smtpHost、fromEmail、smtpCredentials 或 smtpProxy 时,在 updateMask 中保留对应路径,并在 settings.email 中省略该字段。管理读取接口返回用户名和代理 URL,并省略密码。保留相同用户名且省略 password 时沿用现有密码;新凭据和用户名变更需要提供密码。代理负责解析 SMTP 目标域名。邮件验证码、密码重置、邮件 MFA 都依赖这组 SMTP 配置。独立邮箱登录只服务已有账号;请求验证码接口会返回统一文案,避免泄露邮箱是否已注册。邮件注册是否开放、是否需要审核见 运行时设置。
OAuth2 runtime 配置
Section titled “OAuth2 runtime 配置”oauth2.providers 是 OAuth2ProviderSettings 数组。每个元素的 instanceName 是 provider 实例名,例如 github、logto1、corp_oidc。实例名只能包含 ASCII 字母、数字、_ 和 -。
每个实例使用共享字段加一个 provider oneof 字段:
{ "instanceName": "github", "enableSignup": true, "signupNeedReview": false, "github": { "clientId": "github-client-id", "clientSecret": "github-client-secret" }}字段含义:
| 字段 | 作用 |
|---|---|
instanceName |
provider 实例名 |
enableSignup |
是否允许这个 provider 的首次登录自动创建本地账号 |
signupNeedReview |
首次登录是否进入注册审核 |
| provider oneof 字段 | qq、github、google、microsoft、discord、casdoor、logto、oidc、feishu、gitee、apple。每个对象内写 provider 私有字段 |
常见 provider 示例
Section titled “常见 provider 示例”[ { "instanceName": "github", "enableSignup": true, "signupNeedReview": false, "github": { "clientId": "github-client-id", "clientSecret": "github-client-secret" } }][ { "instanceName": "corp_oidc", "enableSignup": false, "signupNeedReview": false, "oidc": { "clientId": "synctv", "clientSecret": "oidc-client-secret", "issuer": "https://idp.example.com" } }]默认使用 issuer 进行 OIDC discovery。只有在 IdP 没有标准 discovery 文档时才手动配置 authUrl、tokenUrl 和 jwksUrl;userinfoUrl 可选,缺失时会使用已验证的 ID Token claims。
Microsoft provider 额外使用 tenant(默认值为 common),Feishu provider 可选使用 endpoint(默认值为 https://open.feishu.cn)。QQ、Discord 和 Gitee 只需要各自的 clientId 与 clientSecret。
[ { "instanceName": "apple", "enableSignup": true, "signupNeedReview": false, "apple": { "webClientId": "com.example.synctv.web", "webClientSecret": "<Apple Services ID client secret>", "nativeClientId": "org.example.synctv", "nativeClientSecret": "<Apple Bundle ID client secret>" } }]webClientId 是 Apple Services ID,供浏览器授权使用;它必须注册对应的 HTTPS Return URL。nativeClientId 是签名 App 的 Bundle ID,供 iOS 和 Mac App Store 构建的原生 Sign in with Apple 使用;它必须与安装包的 Bundle ID 一致。macOS Developer ID 构建使用浏览器授权流程,因为 Developer ID profile 不携带受限制的原生 Apple entitlement。至少配置一组完整凭据:webClientId 与 webClientSecret 启用浏览器授权,nativeClientId 与 nativeClientSecret 启用原生授权。仅使用一种模式时可以省略另一组。两个 clientSecret 都是服务端凭据,使用 Apple Developer Team、Key ID、私钥和对应 client ID 生成,禁止写入客户端或提交到仓库。
官方 SyncTV 构建使用 org.synctv.app。自托管服务器通常没有官方 Apple Developer Team 的 App ID 私钥和 client secret,因此自托管发行应创建自己的 Apple Developer Team、Bundle ID 和 Sign in with Apple 配置,使用自己的 Bundle ID 重新签名客户端,并将它配置为 nativeClientId。
Apple 配置联动
Section titled “Apple 配置联动”| SyncTV 配置 | Apple Developer 配置 | 客户端构建联动 |
|---|---|---|
webClientId |
Services ID,以及 Services ID 下的 HTTPS Return URL | redirectUrl 必须使用已登记地址,并通过 oauth2.allowedRedirectUrls 校验 |
nativeClientId |
App ID(Bundle ID),启用 Sign in with Apple | 必须等于签名包的 Bundle ID,并匹配 Apple capability |
webClientSecret / nativeClientSecret |
使用同一 Team 的 Key ID、Sign in with Apple 私钥和对应 client_id 生成 |
只放在服务端;JWT sub 分别匹配对应 client ID |
Apple 官方配置入口:Web Sign in with Apple、App Sign in with Apple、生成 client secret。Passkey 的 RP ID、AASA 和 SYNCTV_PASSKEY_RP_IDS 属于独立配置;SYNCTV_OAUTH2_APP_LINK_ORIGIN 只影响浏览器 OAuth 回调。
- 运行时 settings 修改后,OAuth2 服务会按新配置重建 provider map。
- 缺失某个实例名,就等于这个入口不可用。
enableSignup=false只影响首次 OAuth2 建号,不影响已绑定账号登录。signupNeedReview=true会把首次 OAuth2 注册送入审核流程。
回调地址与密钥
Section titled “回调地址与密钥”浏览器授权回调
Section titled “浏览器授权回调”浏览器授权请求传入 redirectUrl,服务端使用 oauth2.allowedRedirectUrls 校验 HTTPS 回调地址,loopback 回调单独放行。客户端应在每次授权开始时生成或选择当前回调地址,并在第三方平台注册相同地址。
原生授权请求省略 redirectUrl,并传入 native=true。Apple provider 通过 supportedModes 声明原生能力;iOS 和 Mac App Store 构建通过系统 Sign in with Apple 返回 authorization code,客户端随后把 code 和 state 交给 SyncTV 服务端交换。macOS Developer ID 构建使用浏览器授权流程。服务端会拒绝 provider 未声明的模式。
Apple 原生登录不使用 /.well-known/apple-app-site-association。该文件属于 Apple Universal Links、浏览器 OAuth 回调关联和 Passkey 关联。
clientSecret 应该放哪里
Section titled “clientSecret 应该放哪里”所有 provider secret 都放在 provider 配置中,并通过 runtime settings 管理。Apple 使用 webClientSecret 和 nativeClientSecret 两个独立 secret。