Email and OAuth2
Where Settings Are Stored
Section titled “Where Settings Are Stored”Email login and OAuth2 providers use runtime settings. Email uses the email group, and OAuth2 providers use oauth2.providers.
Email configuration
Section titled “Email configuration”Email settings are stored in PostgreSQL and hot-reloaded across cluster nodes:
synctv settings update \ --set email.smtpHost=smtp.example.com \ --set email.smtpPort=465 \ --set 'email.smtpProxy={"url":"socks5://proxy.example.com:1080"}' \ --set email.useTls=true \ --set email.fromName=SyncTV \ --set email.enabled=true| Field | Default | Meaning |
|---|---|---|
email.enabled |
false |
Enable email delivery |
email.smtpHost |
null |
SMTP server host |
email.smtpPort |
587 |
SMTP port |
email.smtpCredentials |
null |
Optional SMTP credentials {username, password} |
email.smtpProxy |
null |
Optional SOCKS5 proxy {url, credentials?} |
email.fromEmail |
null |
Sender email address; required and validated when email is enabled |
email.fromName |
SyncTV |
Sender display name |
email.useTls |
true |
Whether SMTP uses TLS |
updateMask identifies the fields to replace. To clear smtpHost, fromEmail, smtpCredentials, or smtpProxy, keep its path in updateMask and omit the field from settings.email. Management reads return usernames and the proxy URL while omitting passwords. Omitting password preserves it when the username is unchanged; new credentials and username changes require a password. The proxy resolves the SMTP target hostname. Email verification codes, password reset, and email MFA depend on these SMTP settings. Standalone email login only serves existing accounts; the code request API intentionally returns a generic message to avoid account enumeration. Email signup and review policy live in Runtime Settings.
OAuth2 runtime configuration
Section titled “OAuth2 runtime configuration”oauth2.providers is an array of OAuth2ProviderSettings objects. Each element has an instanceName such as github, logto1, or corp_oidc. Instance names may contain only ASCII letters, digits, _, and -.
Every instance uses shared fields plus one provider oneof field:
{ "instanceName": "github", "enableSignup": true, "signupNeedReview": false, "github": { "clientId": "github-client-id", "clientSecret": "github-client-secret" }}Fields:
| Field | Meaning |
|---|---|
instanceName |
Provider instance name |
enableSignup |
Whether first-time login through this provider can create a local account |
signupNeedReview |
Whether first-time signup should enter the review queue |
| provider oneof field | qq, github, google, microsoft, discord, casdoor, logto, oidc, feishu, gitee, or apple; provider-private fields live inside that object |
Common examples
Section titled “Common examples”[ { "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" } }]By default, SyncTV uses the issuer for OIDC discovery. Only configure authUrl, tokenUrl, and jwksUrl manually when the IdP does not expose a standard discovery document; userinfoUrl is optional, and verified ID Token claims are used when it is absent.
The Microsoft provider also accepts tenant (default common). The Feishu provider accepts an optional endpoint (default https://open.feishu.cn). QQ, Discord, and Gitee each require their own clientId and 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 is an Apple Services ID for browser authorization and must have the HTTPS Return URL registered. nativeClientId is the signed app’s Bundle ID for native Sign in with Apple on iOS and Mac App Store builds; it must match the installed app’s Bundle ID. macOS Developer ID builds use browser authorization because Developer ID profiles do not carry the restricted native Apple entitlement. Configure at least one complete pair: webClientId plus webClientSecret enables browser authorization, while nativeClientId plus nativeClientSecret enables native authorization. The unused pair can be omitted for a single-mode deployment. Both clientSecret values are server credentials generated with the Apple Developer Team, Key ID, private key, and corresponding client ID. Keep them on the server and out of the client or repository.
The official SyncTV build uses org.synctv.app. A self-hosted operator normally does not have the official Apple Developer Team’s App ID private key and client secret. A self-hosted Apple distribution should create its own Apple Developer Team, Bundle ID, and Sign in with Apple configuration, sign a client with that Bundle ID, and configure the same value as nativeClientId.
Apple configuration relationships
Section titled “Apple configuration relationships”| SyncTV setting | Apple Developer configuration | Client build relationship |
|---|---|---|
webClientId |
Services ID and its HTTPS Return URL entries | redirectUrl must use a registered URL and pass oauth2.allowedRedirectUrls |
nativeClientId |
App ID (Bundle ID) with Sign in with Apple enabled | Must equal the signed app Bundle ID and its Apple capability |
webClientSecret / nativeClientSecret |
Generated with the Team, Key ID, Sign in with Apple private key, and the matching client_id |
Keep them on the server; each JWT sub matches its client ID |
Apple configuration links: Web Sign in with Apple, App Sign in with Apple, and client secret generation. Passkey RP IDs, AASA, and SYNCTV_PASSKEY_RP_IDS are independent; SYNCTV_OAUTH2_APP_LINK_ORIGIN only affects browser OAuth callbacks.
Runtime behavior
Section titled “Runtime behavior”- When runtime settings change, the OAuth2 service rebuilds its provider map from the new value.
- Missing instance names mean the login entry point is unavailable.
enableSignup=falseonly blocks first-time account creation through that provider. Existing linked OAuth2 logins still work.signupNeedReview=truesends first-time OAuth2 signup into the review flow.
Redirect URLs and Secrets
Section titled “Redirect URLs and Secrets”Browser authorization callbacks
Section titled “Browser authorization callbacks”Browser authorization requests pass redirectUrl; the server validates HTTPS callbacks against oauth2.allowedRedirectUrls, while loopback callbacks are handled separately. Register the same callback URL with the third-party provider.
Native authorization requests omit redirectUrl and pass native=true. The Apple provider advertises native support through supportedModes; iOS and Mac App Store builds use Apple’s system Sign in with Apple flow and return an authorization code to the client, which exchanges it with the SyncTV server. macOS Developer ID builds use browser authorization. The server rejects modes that the provider has not advertised.
Native Apple login does not use /.well-known/apple-app-site-association. That document is used for Apple Universal Links, browser OAuth callback association, and passkey association.
Where should provider secrets go
Section titled “Where should provider secrets go”Put provider secrets in the provider configuration and manage them through runtime settings. Apple uses separate webClientSecret and nativeClientSecret values.