Google OAuth
Initiate Google Login
Redirects the user to Google’s OAuth consent screen. Query Parameters:- Automatically includes
scope: ['profile', 'email']
Google OAuth Callback
Handled automatically by the OAuth provider after user grants consent. Should not be called directly. Failure Redirect: On authentication failure, redirects to:/login?error=oauth_failed
GitHub OAuth
Initiate GitHub Login
Redirects the user to GitHub’s OAuth authorization screen. Query Parameters:- Automatically includes
scope: ['user:email']
GitHub OAuth Callback
Handled automatically by the OAuth provider after user authorizes. Should not be called directly. Failure Redirect: On authentication failure, redirects to:/login?error=oauth_failed
OAuth Flow
1. User Initiates OAuth
User clicks “Sign in with Google” or “Sign in with GitHub” button, which redirects to:GET /api/auth/googlefor GoogleGET /api/auth/githubfor GitHub
2. Provider Authentication
User is redirected to the OAuth provider (Google/GitHub) to:- Authenticate with their provider account
- Grant permissions (profile, email)
3. Callback Processing
Provider redirects back to callback URL with authorization code:GET /api/auth/google/callback?code=...GET /api/auth/github/callback?code=...
- Exchanges code for access token
- Fetches user profile from provider
- Creates or links account
- Generates session tokens
- Redirects to frontend with tokens
4. Account Creation or Linking
Three scenarios: A. New User (No Existing Account)- Creates new user with
oauth_providerandoauth_id - Email is automatically verified
- No password is set (OAuth-only account)
- Redirects to
/setup-pinto configure PIN
- Links OAuth to existing account
- Preserves existing password
- Updates
oauth_provider,oauth_id, andavatar_url - Redirects to
/unlockif encryption keys exist, otherwise/setup-pin
- Updates OAuth information (avatar, etc.)
- Redirects to
/unlockor/setup-pinbased on encryption status
5. Final Redirect
User is redirected to frontend auth callback page with tokens:- Stores tokens as httpOnly cookies
- Redirects to the specified path (
/setup-pinor/unlock)
Response (via Redirect)
The OAuth callback redirects to the frontend with URL parameters:JWT access token (15 minute expiry)
JWT refresh token (8 hour expiry)
CSRF token for authenticated requests
Path to redirect user after setting cookies:
/setup-pin: New users or users without encryption/unlock: Existing users with encryption keys
OAuth Profile Structure
The backend extracts the following information from OAuth providers:Google Profile Mapping
id: Google user IDemail: Primary email fromprofile.emails[0].valuename: Display name fromprofile.displayNameavatar: Profile photo fromprofile.photos[0].value
GitHub Profile Mapping
id: GitHub user IDemail: Primary email fromprofile.emails[0].value- Falls back to
{username}@github.localif email not available
- Falls back to
name: Display name or usernameavatar: Profile photo fromprofile.photos[0].value
PIN Setup Requirement
OAuth users must set up a PIN after their first login:- OAuth login succeeds
- User is redirected to
/setup-pin?csrf={token} - User creates a numeric PIN (6-8 digits)
- PIN is used for:
- Unlocking the app
- Client-side encryption/decryption
- Transaction authorization
Example OAuth Flow (Google)
Error Handling
Invalid OAuth Profile
If the OAuth provider doesn’t return a valid email:OAuth Provider Failure
If OAuth authentication fails at the provider level:Account Linking Rules
| Scenario | Email Match | Has Password | Action |
|---|---|---|---|
| New user | No | N/A | Create OAuth account |
| Existing user | Yes | Yes | Link OAuth, keep password |
| OAuth-only user | Yes | No | Update OAuth info |
Security Considerations
- No Session Tokens: OAuth uses session-less authentication via Passport.js (
session: false) - Email Verification: OAuth emails are automatically verified (trusted providers)
- Token Transfer: Tokens passed via URL params to frontend auth-callback page (secure over HTTPS)
- CSRF Protection: CSRF token required for all subsequent authenticated requests
- Provider Trust: Only Google and GitHub are supported as trusted OAuth providers
- No Password: OAuth-only accounts don’t have a password (can’t use
/api/auth/login)
Supported Providers
| Provider | Strategy | Scopes | Callback URL |
|---|---|---|---|
passport-google-oauth20 | profile, email | /api/auth/google/callback | |
| GitHub | passport-github2 | user:email | /api/auth/github/callback |
Configuration
OAuth providers are configured via environment variables:Implementation Details
- Passport.js: Uses Passport strategies for OAuth providers
- Account Repository:
UserRepository.createOAuth()creates OAuth users - Account Linking:
UserRepository.linkOAuth()links OAuth to existing accounts - Token Generation: Same JWT tokens as email/password login
- Avatar Storage: OAuth avatar URLs stored in
avatar_urlfield
Differences from Email/Password Login
| Feature | OAuth | Email/Password |
|---|---|---|
| Email Verification | Automatic | Manual (email link) |
| Password | Not set | Required |
| PIN Setup | Required after first login | Optional |
| Login Endpoint | Provider redirect | POST /api/auth/login |
| Account Linking | Automatic if email matches | N/A |
Source Code
OAuth Routes:backend/routes/auth/oauth-routes.ts
OAuth Controller: backend/controllers/auth/oauth-controller.ts:12
OAuth Config: backend/config/oauth.ts:13
Google Strategy: backend/config/oauth.ts:23
GitHub Strategy: backend/config/oauth.ts:49