Vertz ships factory functions for OAuth providers. Configure your credentials, add the provider to createAuth(), and the framework handles the full flow: authorization redirect, callback handling, PKCE, token exchange, and account linking.
Setup
This generates two routes per provider:
Provider configuration
All providers share the same config interface:
Default scopes
| Provider | Default scopes | Email trust |
|---|
| Google | openid, email, profile | Trusted (verified by OIDC) |
| GitHub | read:user, user:email | Untrusted |
| Discord | identify, email | Untrusted |
Account linking
When a user signs in via OAuth, the system follows a three-path resolution:
- Existing link found — The provider account is already linked to a user. Sign them in.
- Trusted email match — The provider verifies the email (Google OIDC). If a user with that email exists, auto-link the provider account and sign in.
- No match — Create a new user account and link the provider account.
GitHub and Discord don’t verify email ownership at the OAuth level, so they never auto-link to
existing accounts. Each GitHub/Discord sign-in creates a new account unless a link already exists
in the OAuthAccountStore.
Required configuration
Encryption key
OAuth state is stored in an encrypted cookie (AES-256-GCM). You must provide an encryption key:
Generate one:
OAuthAccountStore
The account store tracks which provider accounts are linked to which users:
For development, use the built-in in-memory store:
For production, implement the interface backed by your database.
Redirect configuration
Control where users land after OAuth:
Security
| Protection | How |
|---|
| PKCE | S256 code challenge for Google and Discord (GitHub doesn’t support PKCE) |
| State cookie | AES-256-GCM encrypted, prevents CSRF on the callback |
| OIDC nonce | Validated in Google ID tokens to prevent replay attacks |
| Rate limiting | 10 OAuth initiations per 5 minutes per IP |
| Email validation | Empty emails rejected before user creation |
Environment variables
Use createEnv() to validate OAuth credentials at startup:
OAuth-only users
Users created via OAuth have a null password hash. If they attempt email/password sign-in, the auth system runs a timing-safe dummy bcrypt comparison — same response time, always fails. This prevents user enumeration via the sign-in endpoint.