Skip to content

Email and OAuth2

Email login and OAuth2 providers use runtime settings. Email uses the email group, and OAuth2 providers use oauth2.providers.

Email settings are stored in PostgreSQL and hot-reloaded across cluster nodes:

Terminal window
synctv settings update \
--set email.smtpHost=smtp.example.com \
--set email.smtpPort=465 \
--set 'email.smtpCredentials={"username":"[email protected]","password":"secret"}' \
--set 'email.smtpProxy={"url":"socks5://proxy.example.com:1080"}' \
--set email.useTls=true \
--set email.fromName=SyncTV \
--set email.enabled=true
FieldDefaultMeaning
email.enabledfalseEnable email delivery
email.smtpHostnullSMTP server host
email.smtpPort587SMTP port
email.smtpCredentialsnullOptional SMTP credentials {username, password}
email.smtpProxynullOptional SOCKS5 proxy {url, credentials?}
email.fromEmailnullSender email address; required and validated when email is enabled
email.fromNameSyncTVSender display name
email.useTlstrueWhether 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.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",
"redirectUrl": "https://app.example.com/oauth2/callback"
}
}

Fields:

FieldMeaning
instanceNameProvider instance name
enableSignupWhether first-time login through this provider can create a local account
signupNeedReviewWhether first-time signup should enter the review queue
provider oneof fieldFor example github, google, logto, oidc, or casdoor. Provider-private fields live inside that object
[
{
"instanceName": "github",
"enableSignup": true,
"signupNeedReview": false,
"github": {
"clientId": "github-client-id",
"clientSecret": "github-client-secret",
"redirectUrl": "https://app.example.com/oauth2/callback"
}
}
]
  • 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=false only blocks first-time account creation through that provider. Existing linked OAuth2 logins still work.
  • signupNeedReview=true sends first-time OAuth2 signup into the review flow.

Each provider instance owns its full callback URL through redirectUrl. Different providers can use their own callback path and scheme.

Put it in clientSecret and manage it through runtime settings.