External Login Providers
Chatto can consume external OAuth and OIDC providers for sign-in. Providers are configured in chatto.toml as repeated [[auth.providers]] tables, and the login page renders one button per configured provider.
Supported providers
Section titled “Supported providers”| Type | Use for |
|---|---|
oidc | Any standards-compatible OIDC provider |
github | GitHub OAuth apps |
gitlab | GitLab OAuth applications |
google | Google OAuth clients |
discord | Discord OAuth2 applications |
Other provider types are not exposed as curated providers yet. Use oidc for standards-compatible OIDC providers.
Provider IDs
Section titled “Provider IDs”Every provider needs a stable id. The ID appears in login URLs and is stored in external identity links, so changing it after users link accounts will strand those links.
Use short, URL-safe IDs that describe the local provider slot:
id = "chatto-hub"id = "github-main"id = "staff-google"Provider IDs must not contain spaces, /, ?, or #.
Callback URLs
Section titled “Callback URLs”Register this callback URL with each external provider:
https://chat.example.com/auth/providers/{providerID}/callbackFor example, a provider with id = "github-main" on https://chat.example.com uses:
https://chat.example.com/auth/providers/github-main/callbackChatto derives the callback from webserver.url, so set that value to the public HTTPS origin of your server.
For older OIDC client registrations, Chatto also serves /auth/oidc and /auth/oidc/callback as compatibility aliases for the configured OIDC provider. New registrations should use the provider-specific callback URL.
Configuration
Section titled “Configuration”[webserver]url = "https://chat.example.com"
[[auth.providers]]id = "chatto-hub"type = "oidc"label = "Chatto Hub"issuer_url = "https://hub.chatto.dev"client_id = "..."client_secret = "..."issuer_url must be an absolute http or https URL. Chatto uses OIDC discovery from that issuer and verifies the ID token before matching the external identity.
For container platforms, configure providers with counted environment variables. Each provider uses CHATTO_AUTH_PROVIDERS_<index>_<field>, with indexes starting at 0 and no gaps:
CHATTO_AUTH_PROVIDERS_0_ID=chatto-hubCHATTO_AUTH_PROVIDERS_0_TYPE=oidcCHATTO_AUTH_PROVIDERS_0_LABEL="Chatto Hub"CHATTO_AUTH_PROVIDERS_0_ISSUER_URL=https://hub.chatto.devCHATTO_AUTH_PROVIDERS_0_CLIENT_ID=...CHATTO_AUTH_PROVIDERS_0_CLIENT_SECRET=...
CHATTO_AUTH_PROVIDERS_1_ID=github-mainCHATTO_AUTH_PROVIDERS_1_TYPE=githubCHATTO_AUTH_PROVIDERS_1_LABEL=GitHubCHATTO_AUTH_PROVIDERS_1_CLIENT_ID=...CHATTO_AUTH_PROVIDERS_1_CLIENT_SECRET=...Supported fields are ID, TYPE, LABEL, CLIENT_ID, CLIENT_SECRET, ISSUER_URL, SCOPES, REQUEST_EMAIL, and PROVIDER_OPTIONS_<KEY>. SCOPES is comma-separated, for example CHATTO_AUTH_PROVIDERS_0_SCOPES=openid,profile,groups.
Any CHATTO_AUTH_PROVIDERS_<index>_* variables replace providers from chatto.toml.
Chatto also accepts the older single-provider OIDC variables for compatibility:
CHATTO_AUTH_OIDC_ENABLED=trueCHATTO_AUTH_OIDC_ISSUER_URL=https://hub.chatto.devCHATTO_AUTH_OIDC_CLIENT_ID=...CHATTO_AUTH_OIDC_CLIENT_SECRET=...# CHATTO_AUTH_OIDC_LABEL="Chatto Hub"Those legacy variables create one oidc provider with id = "oidc". Do not combine counted CHATTO_AUTH_PROVIDERS_<index>_* variables with CHATTO_AUTH_OIDC_ENABLED.
[webserver]url = "https://chat.example.com"
[[auth.providers]]id = "github-main"type = "github"label = "GitHub"client_id = "..."client_secret = "..."[webserver]url = "https://chat.example.com"
[[auth.providers]]id = "gitlab-main"type = "gitlab"label = "GitLab"client_id = "..."client_secret = "..."[webserver]url = "https://chat.example.com"
[[auth.providers]]id = "staff-google"type = "google"label = "Google"client_id = "..."client_secret = "..."[webserver]url = "https://chat.example.com"
[[auth.providers]]id = "discord-main"type = "discord"label = "Discord"client_id = "..."client_secret = "..."Scopes
Section titled “Scopes”Chatto requests provider-specific default scopes:
| Type | Default scopes |
|---|---|
oidc | openid, profile, email |
github | read:user, user:email |
gitlab | read_user |
google | openid, profile, email |
discord | identify, email |
You can override scopes per provider:
[[auth.providers]]id = "chatto-hub"type = "oidc"issuer_url = "https://id.example.com"client_id = "..."client_secret = "..."scopes = ["openid", "profile", "groups"]For OIDC providers, Chatto always includes openid even if you omit it from a custom scope list.
Set request_email = false to omit the default email scope for providers where Chatto adds one automatically.
Account matching
Section titled “Account matching”Chatto matches external logins by provider identity, not by email address.
- OIDC providers are matched by verified issuer URL plus subject.
- OAuth-only providers are matched by the configured provider ID plus provider user ID.
- Email claims are treated as profile data and are not used to pick an account.
If a provider callback returns an identity that is not linked to a Chatto account, login is rejected until a future account-linking or account-creation flow explicitly links that identity.
Discovery fields
Section titled “Discovery fields”Public clients can discover configured providers in two places:
- GraphQL
Server.authProvidersreturns provider IDs, types, labels, and login URLs. GET /api/serverreturns the same metadata inauthProviders.
Older clients may still read /api/server.authMethods. That legacy field stays method-oriented, so OIDC appears as oidc even when the configured provider ID is something like chatto-hub.