OAuth Flow
Complete step-by-step breakdown of the SVA OAuth 2.0 authorization code flow.
Flow Overview
┌─────────┐ ┌──────────────┐ ┌─────────────┐ ┌──────────────┐
│ App │────────▶│ SVA OAuth │────────▶│ SVA Client │────────▶│ SVA Server │
│ │ │ Provider │ │ (Consent) │ │ (Attest) │
└─────────┘ └──────────────┘ └─────────────┘ └──────────────┘
Step-by-Step Flow
Step 1: Authorization Request
Location: Your App → SVA OAuth Provider
Action: User clicks "Sign In with SVA" or accesses a protected view
# Your app generates authorization URL
auth_url, code_verifier = client.get_authorization_url()
# Redirects to: https://auth.getsva.com/oauth/authorize?client_id=...&redirect_uri=...&scope=...&code_challenge=...&state=...
What Happens:
- App generates PKCE parameters (code_verifier, code_challenge)
- App generates state parameter for CSRF protection
- App redirects user to SVA OAuth authorization endpoint
- SVA OAuth validates client_id and redirect_uri
- SVA OAuth creates OAuthAuthorizationRequest record
- SVA OAuth redirects to consent screen
Key Parameters:
client_id: Your OAuth app client IDredirect_uri: Your callback URLresponse_type:codescope: Requested scopes (e.g.,openid email profile)code_challenge: PKCE code challengecode_challenge_method:S256state: CSRF protection token
Step 2: Consent Screen
Location: SVA Client (React App)
Action: User reviews and approves requested scopes
What Happens:
- Consent page loads with
auth_request_idfrom URL - Frontend fetches auth request details from SVA Server
- SVA Server proxies request to SVA OAuth with service token
- Consent page displays:
- Application name, logo, description
- Requested scopes with descriptions
- User can toggle individual scopes
- User unlocks vault if needed (master key or passkey)
- User clicks "Approve and Continue"
Key Components:
/consent?auth_request_id={uuid}- Consent page URLGET /api/internal/oauth/requests/{id}/- Fetch request details- User's encrypted vault data (decrypted client-side)
Step 3: Data Attestation
Location: SVA Client → SVA Server
Action: SVA Core issues signed data token
What Happens:
- Frontend decrypts user data from vault
- Frontend builds claims object from approved scopes:
{
"email": "[email protected]",
"name": "John Doe",
"phone": "+1234567890"
} - Frontend calls SVA Server attestation endpoint:
POST /api/internal/attest-data/
{
"auth_request_id": "...",
"user_id": "...",
"audience": "client_id",
"claims": {...}
} - SVA Server validates:
- User session token
- Auth request exists and is pending
- Claims match requested scopes
- SVA Server generates signed data token (JWT):
{
"sub": "user_id",
"aud": "client_id",
"auth_request_id": "...",
"claims": {...},
"exp": 1234567890,
"iat": 1234567890
} - SVA Server returns data token
Key Endpoints:
POST /api/internal/attest-data/- Data attestation endpoint- Data token signed with
DATA_TOKEN_SECRET - Token expires in 5 minutes (default)
Step 4: Consent Completion
Location: SVA Client → SVA Server → SVA OAuth
Action: Notify OAuth provider that consent is complete
What Happens:
- Frontend calls SVA Server:
POST /api/internal/oauth/requests/{auth_request_id}/complete/
{
"approved_scopes": ["email", "profile"],
"data_token": "..."
} - SVA Server proxies to SVA OAuth:
POST /api/auth/internal/consent-complete/
Headers: {
"X-Service-Token": "shared-secret"
}
Body: {
"auth_request_id": "...",
"user_id": "...",
"approved_scopes": [...],
"data_token": "..."
} - SVA OAuth validates:
- Service token
- Data token signature
- Data token audience matches client_id
- Data token subject matches user_id
- Auth request is still pending
- SVA OAuth creates OAuthAuthorizationCode:
- Generates unique authorization code
- Links to auth request
- Stores approved scopes
- Stores data token
- Expires in 10 minutes
- SVA OAuth updates auth request status to
approved - SVA OAuth returns redirect URL with authorization code
Key Components:
- Authorization code (one-time use, expires in 10 minutes)
- Data token stored with authorization code
- Redirect URL:
{redirect_uri}?code={code}&state={state}
Step 5: Token Exchange
Location: Your App → SVA OAuth Provider
Action: Exchange authorization code for tokens
What Happens:
- User's browser redirects to your callback URL:
https://yourapp.com/oauth/callback/?code={code}&state={state} - Your app validates state parameter (CSRF protection)
- Your app exchanges code for tokens:
POST https://auth.getsva.com/oauth/token/
{
"grant_type": "authorization_code",
"code": "...",
"redirect_uri": "...",
"client_id": "...",
"client_secret": "...",
"code_verifier": "..."
} - SVA OAuth validates:
- Client credentials
- Authorization code exists and not used
- Authorization code not expired
- Redirect URI matches
- PKCE code verifier (if used)
- SVA OAuth creates tokens:
- Access token (expires in 1 hour)
- Refresh token (expires in 30 days)
- Includes data token from authorization code
- SVA OAuth returns tokens:
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "...",
"scope": "openid email profile",
"data_token": "..."
} - Your app stores tokens in session
Key Components:
- Access token for API calls
- Refresh token for token renewal
- Data token containing user claims (stateless!)
Step 6: Access User Data
Location: Your App
Action: Decode user data from data token
What Happens:
- Your app retrieves data token from session
- Your app decodes and verifies data token:
claims = client.get_blocks_data(data_token)
# Or using utility:
claims = get_sva_claims(request) - Your app accesses user data:
email = claims.get('email')
name = claims.get('name')
phone = claims.get('phone')
Key Benefits:
- No API call to
/userinfoneeded - Data available immediately
- Cryptographically verified
- Stateless design
Token Lifecycle
Authorization Code
- Lifetime: 10 minutes
- Usage: One-time use
- Storage: SVA OAuth database
Data Token
- Lifetime: 5 minutes (default)
- Usage: Included in token response
- Storage: Your app session
Access Token
- Lifetime: 1 hour
- Usage: API authentication
- Storage: Your app session
Refresh Token
- Lifetime: 30 days
- Usage: Token renewal
- Storage: Your app session
Security Features
PKCE (Proof Key for Code Exchange)
- Prevents authorization code interception
- Code verifier generated client-side
- Code challenge sent in authorization request
- Code verifier verified in token exchange
State Parameter
- CSRF protection
- Random token generated per flow
- Stored in session
- Verified in callback
Data Token Verification
- Cryptographic signature verification
- Expiration validation
- Audience validation (client_id)
- Subject validation (user_id)
Error Handling
Common Errors
invalid_client
- Invalid client_id or client_secret
- Solution: Verify credentials
redirect_uri_mismatch
- Redirect URI doesn't match registered URI
- Solution: Ensure exact match (including protocol, domain, port, path)
invalid_grant
- Authorization code invalid, expired, or already used
- Solution: Request new authorization
invalid_token
- Data token invalid or expired
- Solution: Request new authorization
Next Steps
- Learn about Data Tokens architecture
- Understand Service Communication
- Read about Zero-Knowledge design