OAuth providers
IdentSphere ships built-in support for two social-OAuth providers: Google and GitHub. Both implement OAuth 2.1 authorization-code flow.
Configuration
Set the appropriate client ID + secret in AppConfig. Missing client IDs
mean the corresponding provider's endpoint returns 404.
AppConfig {
oauth_google_client_id: Some("123-abc.apps.googleusercontent.com".into()),
oauth_google_client_secret: Some(std::env::var("GOOGLE_SECRET").unwrap()),
oauth_github_client_id: Some("Iv1.deadbeef".into()),
oauth_github_client_secret: Some(std::env::var("GH_SECRET").unwrap()),
..Default::default()
}
Redirect URI to register
Whatever provider you configure, register this exact callback URL in their OAuth app settings:
{public_base_url}/v1/auth/oauth/{provider}/callback
For public_base_url = https://auth.example.com and provider google:
https://auth.example.com/v1/auth/oauth/google/callback
Flow
[browser] [provider]
│ │
│ GET /v1/auth/oauth/google/start? │
│ redirect_to=/dashboard │
│ ──────────────────────────────────────► │ (307 redirect)
│ │
│ GET https://accounts.google.com/... │
│ ───────────────────────────────────────►│
│ │
│ user consents │
│ │
│ GET /v1/auth/oauth/google/callback? │
│ code=...&state=... │
│ ◄────────────────────────────────────── │
│ │
│ → IdentSphere exchanges code for tokens │
│ → IdentSphere looks up / creates user │
│ → IdentSphere sets session cookies │
│ → 307 redirect to /dashboard │
Identity matching
Provider-returned email is the linking key:
- Existing
users.emailmatch → sign that user in to their existing org. - No match → create a fresh org with the user as owner, mark
email_verified = true.
Google: the email is sourced from the ID token's email claim. The SDK
validates the ID token against Google's JWKS.
GitHub: the email is sourced from /user, falling back to the primary
verified address in /user/emails.
CSRF / state
A 32-byte random state token is minted at /start and persisted in
session_cache under IdentSphere:oauth:state:{token} with a 10-minute TTL.
The callback compares; missing or expired state is 400.
Open-redirect defense
The redirect_to query parameter is validated:
- Must start with
/. - Must NOT start with
//(protocol-relative URL hijack). - Must NOT be an absolute URL.
If validation fails, the start endpoint returns 400.
Provider errors
If the provider redirects back with ?error=access_denied (user clicked
"deny"), IdentSphere redirects back to your redirect_to with
?oauth_error=access_denied. Your frontend reads oauth_error from the
URL to show the user a message.
Adding more providers
Apple, Microsoft, etc. are not in v0.1. The OAuth handler is structured to make adding more providers straightforward — file an issue if you need one soon.
Audit
Every successful callback emits an audit entry:
auth.oauth.google.linkedfor Googleauth.oauth.github.linkedfor GitHub
Metadata includes existing_user: true/false.