Skip to content

Setting Up Authentication

Eric Fitzgerald edited this page Jan 24, 2026 · 2 revisions

Setting Up Authentication

This guide covers configuring OAuth authentication for TMI with Google, GitHub, and Microsoft identity providers.

Overview

TMI uses OAuth 2.0 for authentication with support for:

  • Google OAuth - Google accounts and Google Workspace
  • GitHub OAuth - GitHub personal and organization accounts
  • Microsoft OAuth - Microsoft accounts (personal, work, school)

The authentication flow:

  1. User clicks login button in web app
  2. Web app redirects to OAuth provider
  3. User authenticates with provider
  4. Provider redirects back to web app with authorization code
  5. Web app sends code to TMI server
  6. TMI server exchanges code for user info
  7. TMI server issues JWT tokens
  8. Web app uses JWT for API requests

Architecture

OAuth Flow Diagram

[User Browser]
     ↓ (1) Click login
[TMI Web App]
     ↓ (2) Redirect to provider
[OAuth Provider (Google/GitHub/Microsoft)]
     ↓ (3) User authenticates
[OAuth Provider]
     ↓ (4) Redirect with auth code
[TMI Web App]
     ↓ (5) Send auth code
[TMI Server]
     ↓ (6) Exchange code for user info
[OAuth Provider]
     ↓ (7) Return JWT tokens
[TMI Web App]
     ↓ (8) Use JWT for API calls
[TMI Server]

Components

  • Web Application: Initiates OAuth flow, handles callback
  • TMI Server: Exchanges authorization codes, issues JWT tokens
  • OAuth Provider: Authenticates users, provides user information

Prerequisites

Before setting up authentication:

  • TMI server deployed and accessible
  • TMI web application deployed (for production redirect URIs)
  • Domain names configured (for production)
  • SSL/TLS certificates (HTTPS required for OAuth in production)

Google OAuth Setup

Create OAuth Application

  1. Go to Google Cloud Console:

  2. Enable APIs:

    • Go to "APIs & Services" → "Library"
    • Search for "Google+ API" and enable it
  3. Create OAuth Credentials:

    • Go to "APIs & Services" → "Credentials"
    • Click "Create Credentials" → "OAuth client ID"
    • Choose "Web application"
  4. Configure OAuth Consent Screen (if not done):

  5. Configure Application:

    • Name: TMI Production

    • Authorized JavaScript origins:

      • https://tmi.example.com (your web app domain)
      • http://localhost:4200 (for development)
    • Authorized redirect URIs:

      • https://tmi.example.com/oauth2/callback (production)
      • http://localhost:4200/oauth2/callback (development)
  6. Get Credentials:

    • Copy Client ID and Client Secret
    • Save for configuration

Google OAuth Configuration

For TMI Server

Environment Variables:

OAUTH_PROVIDERS_GOOGLE_ENABLED=true
OAUTH_PROVIDERS_GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
OAUTH_PROVIDERS_GOOGLE_CLIENT_SECRET=your-google-client-secret

Configuration File (config-production.yml):

auth:
  oauth:
    callback_url: "https://api.tmi.example.com/oauth2/callback"
    providers:
      google:
        id: "google"
        name: "Google"
        enabled: true
        icon: "fa-brands fa-google"
        client_id: "${OAUTH_PROVIDERS_GOOGLE_CLIENT_ID}"
        client_secret: "${OAUTH_PROVIDERS_GOOGLE_CLIENT_SECRET}"
        authorization_url: "https://accounts.google.com/o/oauth2/auth"
        token_url: "https://oauth2.googleapis.com/token"
        userinfo:
          - url: "https://www.googleapis.com/oauth2/v3/userinfo"
            claims: {}  # Uses defaults: sub, email, name
        issuer: "https://accounts.google.com"
        jwks_url: "https://www.googleapis.com/oauth2/v3/certs"
        scopes: ["openid", "profile", "email"]

For TMI Web Application

Configure in src/environments/environment.prod.ts:

export const environment = {
  production: true,
  apiUrl: 'https://api.tmi.example.com',
  // Google OAuth handled by TMI server
};

Testing Google OAuth

# Check provider availability
curl https://api.tmi.example.com/oauth2/providers

# Should include:
{
  "providers": [
    {"id": "google", "name": "Google", "icon": "google"}
  ]
}

GitHub OAuth Setup

Create OAuth Application

  1. Go to GitHub Settings:

  2. Configure Application:

    • Application name: TMI Threat Modeling
    • Homepage URL: https://tmi.example.com
    • Application description: Collaborative threat modeling platform
    • Authorization callback URL: https://tmi.example.com/oauth2/callback
  3. Get Credentials:

    • Copy Client ID
    • Generate and copy Client Secret

GitHub OAuth Configuration

For TMI Server

Environment Variables:

OAUTH_PROVIDERS_GITHUB_ENABLED=true
OAUTH_PROVIDERS_GITHUB_CLIENT_ID=your-github-client-id
OAUTH_PROVIDERS_GITHUB_CLIENT_SECRET=your-github-client-secret

Configuration File (config-production.yml):

auth:
  oauth:
    providers:
      github:
        id: "github"
        name: "GitHub"
        enabled: true
        icon: "fa-brands fa-github"
        client_id: "${OAUTH_PROVIDERS_GITHUB_CLIENT_ID}"
        client_secret: "${OAUTH_PROVIDERS_GITHUB_CLIENT_SECRET}"
        authorization_url: "https://github.com/login/oauth/authorize"
        token_url: "https://github.com/login/oauth/access_token"
        auth_header_format: "token %s"  # GitHub uses "token" not "Bearer"
        accept_header: "application/json"
        userinfo:
          # Primary user info
          - url: "https://api.github.com/user"
            claims:
              subject_claim: "id"
              name_claim: "name"
              picture_claim: "avatar_url"
          # Email info (separate endpoint)
          - url: "https://api.github.com/user/emails"
            claims:
              email_claim: "[0].email"  # First email in array
              email_verified_claim: "[0].verified"
        scopes: ["user:email"]

Testing GitHub OAuth

# Verify provider is configured
curl https://api.tmi.example.com/oauth2/providers | jq '.providers[] | select(.id=="github")'

# Should return GitHub provider details

Microsoft OAuth Setup

Microsoft OAuth is more complex due to different account types. Choose the appropriate configuration based on your needs.

Create OAuth Application

  1. Go to Azure Portal:

    • Navigate to portal.azure.com
    • Go to "Azure Active Directory" → "App registrations"
    • Click "New registration"
  2. Configure Application:

    • Name: TMI Threat Modeling
    • Supported account types: Choose based on requirements:
      • Personal Microsoft accounts only: For consumer accounts
      • Accounts in any organizational directory: For work/school accounts
      • Both personal and work/school accounts: For all Microsoft accounts
  3. Set Redirect URI:

    • Platform: Web
    • Redirect URI: https://tmi.example.com/oauth2/callback
  4. Configure API Permissions:

    • Add permissions:
      • Microsoft Graph → Delegated permissions
      • User.Read
      • openid
      • profile
      • email
    • Grant admin consent (if required by your organization)
  5. Create Client Secret:

    • Go to "Certificates & secrets"
    • Click "New client secret"
    • Description: TMI Server
    • Expires: Choose expiration (recommended: 24 months)
    • Copy the secret value (shown only once!)
  6. Get Application ID:

    • Copy the Application (client) ID from Overview page

Microsoft OAuth Configuration

Microsoft has different endpoints based on account types. Choose the appropriate configuration:

Option 1: Personal Microsoft Accounts Only

Environment Variables:

OAUTH_PROVIDERS_MICROSOFT_ENABLED=true
OAUTH_PROVIDERS_MICROSOFT_CLIENT_ID=your-microsoft-client-id
OAUTH_PROVIDERS_MICROSOFT_CLIENT_SECRET=your-microsoft-client-secret

Configuration File (config-production.yml):

auth:
  oauth:
    providers:
      microsoft:
        id: "microsoft"
        name: "Microsoft"
        enabled: true
        icon: "fa-brands fa-microsoft"
        client_id: "${OAUTH_PROVIDERS_MICROSOFT_CLIENT_ID}"
        client_secret: "${OAUTH_PROVIDERS_MICROSOFT_CLIENT_SECRET}"
        authorization_url: "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize"
        token_url: "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"
        userinfo:
          - url: "https://graph.microsoft.com/v1.0/me"
            claims:
              subject_claim: "id"
              email_claim: "mail"  # Microsoft uses "mail" not "email"
              name_claim: "displayName"
              given_name_claim: "givenName"
              family_name_claim: "surname"
              email_verified_claim: "true"  # Literal - always verified
        issuer: "https://login.microsoftonline.com/consumers/v2.0"
        jwks_url: "https://login.microsoftonline.com/common/discovery/v2.0/keys"
        scopes: ["openid", "profile", "email", "User.Read"]

Option 2: All Microsoft Accounts (Personal + Work/School)

Use /common/ endpoint:

authorization_url: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
token_url: "https://login.microsoftonline.com/common/oauth2/v2.0/token"
issuer: "https://login.microsoftonline.com/common/v2.0"

Option 3: Work/School Accounts Only

Use /organizations/ endpoint:

authorization_url: "https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize"
token_url: "https://login.microsoftonline.com/organizations/oauth2/v2.0/token"
issuer: "https://login.microsoftonline.com/organizations/v2.0"

Option 4: Specific Azure AD Tenant

Use tenant-specific endpoint:

authorization_url: "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize"
token_url: "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token"
issuer: "https://login.microsoftonline.com/{tenant-id}/v2.0"

Important: The endpoint type MUST match your Azure AD app's "Supported account types" setting.

Testing Microsoft OAuth

# Verify provider is configured
curl https://api.tmi.example.com/oauth2/providers | jq '.providers[] | select(.id=="microsoft")'

# Test authentication (will return redirect URL)
curl -X POST https://api.tmi.example.com/oauth2/token?idp=microsoft \
  -H "Content-Type: application/json" \
  -d '{"code":"test"}'

Development vs Production

Development Configuration

For local development:

TMI Server - config-development.yml:

auth:
  oauth:
    callback_url: "http://localhost:8080/oauth2/callback"
    providers:
      google:
        enabled: true
        client_id: "dev-client-id.apps.googleusercontent.com"
        client_secret: "dev-client-secret"
        # ... rest of config

Authorized Redirect URIs (in OAuth provider dashboards):

  • Google: http://localhost:4200/oauth2/callback
  • GitHub: http://localhost:4200/oauth2/callback
  • Microsoft: http://localhost:4200/oauth2/callback

Production Configuration

TMI Server - config-production.yml:

auth:
  oauth:
    callback_url: "https://api.tmi.example.com/oauth2/callback"
    providers:
      # Use environment variables for secrets
      google:
        enabled: true
        client_id: "${OAUTH_PROVIDERS_GOOGLE_CLIENT_ID}"
        client_secret: "${OAUTH_PROVIDERS_GOOGLE_CLIENT_SECRET}"

Authorized Redirect URIs (in OAuth provider dashboards):

  • All providers: https://tmi.example.com/oauth2/callback

JWT Configuration

Generate Secure JWT Secret

# Generate 32-character random secret
openssl rand -base64 32

# Or use this command
python3 -c "import secrets; print(secrets.token_urlsafe(32))"

Configure JWT Settings

Environment Variables:

JWT_SECRET=your-very-secure-random-secret-minimum-32-characters
JWT_EXPIRATION_SECONDS=3600  # 1 hour
JWT_SIGNING_METHOD=HS256

Configuration File:

auth:
  jwt:
    secret: "${JWT_SECRET}"
    expiration_seconds: 3600  # 1 hour for access token
    signing_method: "HS256"

JWT Token Structure

TMI issues two types of tokens:

Access Token (short-lived, 1 hour):

{
  "sub": "user-unique-id",
  "email": "user@example.com",
  "name": "User Name",
  "iss": "tmi-server",
  "aud": "tmi-api",
  "exp": 1699999999,
  "iat": 1699996399
}

Refresh Token (long-lived, 30 days):

  • Stored in database
  • Used to obtain new access tokens
  • Can be revoked

Testing Authentication

Manual Testing Flow

  1. Check available providers:
curl https://api.tmi.example.com/oauth2/providers
  1. Start OAuth flow (in browser):
https://accounts.google.com/o/oauth2/auth?
  client_id=YOUR_CLIENT_ID&
  redirect_uri=https://tmi.example.com/oauth2/callback&
  response_type=code&
  scope=openid%20profile%20email&
  state=random-state-value
  1. Exchange authorization code (from web app):
curl -X POST https://api.tmi.example.com/oauth2/token?idp=google \
  -H "Content-Type: application/json" \
  -d '{
    "code": "authorization-code-from-callback",
    "state": "random-state-value",
    "redirect_uri": "https://tmi.example.com/oauth2/callback"
  }'
  1. Verify token:
curl https://api.tmi.example.com/oauth2/me \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Automated Testing

Create a test script (test-auth.sh):

#!/bin/bash

API_URL="https://api.tmi.example.com"

echo "Testing OAuth providers..."
PROVIDERS=$(curl -s ${API_URL}/oauth2/providers)
echo $PROVIDERS | jq .

# Check each provider
for PROVIDER in google github microsoft; do
  echo "Checking $PROVIDER..."
  echo $PROVIDERS | jq ".providers[] | select(.id==\"$PROVIDER\")"
done

echo "Testing JWT endpoint..."
curl -s ${API_URL}/version | jq .

Troubleshooting

Common Issues

1. "Provider not found" Error

Problem: TMI server can't find OAuth provider configuration.

Solutions:

  • Verify provider is enabled in configuration
  • Check environment variables are set
  • Restart TMI server after configuration changes
  • Check logs for configuration loading errors

2. "Invalid redirect URI" Error

Problem: OAuth provider rejects redirect URI.

Solutions:

  • Verify redirect URI in OAuth provider dashboard matches exactly
  • Check for trailing slash differences (/callback vs /callback/)
  • Ensure protocol matches (http vs https)
  • Confirm domain matches exactly (including www if present)

3. "Failed to exchange code" Error

Problem: TMI server cannot exchange authorization code for tokens.

Solutions:

  • Verify client secret is correct
  • Check callback URL configuration
  • Ensure authorization code hasn't expired (use immediately)
  • Verify OAuth provider credentials are active

4. Microsoft "userAudience configuration" Error

Problem: Endpoint type doesn't match Azure AD app configuration.

Solution: Match endpoint to app's "Supported account types":

  • Personal only → /consumers/
  • Work/school only → /organizations/
  • Both → /common/
  • Specific tenant → /{tenant-id}/

5. "CSRF validation failed" Error

Problem: State parameter doesn't match.

Solutions:

  • Verify web app properly stores and retrieves state
  • Check for session storage issues
  • Ensure state is generated randomly and securely
  • Verify no URL encoding issues with state parameter

Debug Mode

Enable detailed OAuth logging:

logging:
  level: "debug"
  log_api_requests: true
  log_api_responses: true

Check logs for:

# TMI server logs
journalctl -u tmi -f | grep -i oauth

# Look for:
# - Provider configuration loading
# - OAuth token exchange requests
# - User info API calls
# - JWT token generation

Testing Individual Providers

Google:

# Test Google userinfo endpoint manually
curl https://www.googleapis.com/oauth2/v3/userinfo \
  -H "Authorization: Bearer GOOGLE_ACCESS_TOKEN"

GitHub:

# Test GitHub user endpoint
curl https://api.github.com/user \
  -H "Authorization: token GITHUB_ACCESS_TOKEN"

# Test GitHub emails endpoint
curl https://api.github.com/user/emails \
  -H "Authorization: token GITHUB_ACCESS_TOKEN"

Microsoft:

# Test Microsoft Graph endpoint
curl https://graph.microsoft.com/v1.0/me \
  -H "Authorization: Bearer MICROSOFT_ACCESS_TOKEN"

SAML Enterprise SSO

TMI supports SAML 2.0 authentication for enterprise Single Sign-On (SSO) with identity providers like Okta, Azure AD, and others.

Overview

SAML 2.0 support enables:

  • Enterprise SSO: Integration with corporate identity providers
  • Group-Based Authorization: Access control based on IdP group memberships
  • Provider Isolation: Groups scoped to specific IdPs prevent cross-provider access
  • Session-Based Group Caching: Groups cached in Redis for session duration

SAML Dependencies

TMI uses the crewjam/saml library (v0.5.1) for SAML 2.0 protocol handling.

SAML Provider Configuration

Configure SAML providers in your configuration file:

oauth:
  providers:
    saml_okta:
      id: "saml_okta"
      name: "Okta SSO"
      type: "saml"
      icon: "fa-solid fa-key"
      enabled: true
      saml:
        entity_id: "https://tmi.example.com"
        acs_url: "https://tmi.example.com/saml/acs"
        slo_url: "https://tmi.example.com/saml/slo"
        idp_metadata_url: "https://okta.example.com/app/metadata"
        sp_private_key_path: "/path/to/sp.key"
        sp_certificate_path: "/path/to/sp.crt"
        attribute_mapping:
          email: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
          name: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
          groups: "http://schemas.microsoft.com/ws/2008/06/identity/claims/groups"

SAML Configuration Options

Option Description
entity_id Service Provider entity ID (unique identifier)
acs_url Assertion Consumer Service URL (receives SAML responses)
slo_url Single Logout URL (optional)
idp_metadata_url URL to fetch IdP metadata XML
idp_metadata_xml Alternative: static IdP metadata XML
sp_private_key_path Path to SP private key file
sp_certificate_path Path to SP certificate file
allow_idp_initiated Allow IdP-initiated SSO (default: false)
force_authn Force reauthentication (default: false)
attribute_mapping Map SAML attributes to user fields
group_attribute_name SAML attribute containing group memberships
group_prefix Optional prefix filter for groups

Default Attribute Mappings

TMI provides default attribute mappings for common IdPs:

Standard Claims (Microsoft/Azure AD):

  • Email: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
  • Name: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
  • Groups: http://schemas.microsoft.com/ws/2008/06/identity/claims/groups

Okta-Specific:

  • Email: email
  • Name: name
  • Groups: memberOf

SAML Endpoints

TMI exposes these SAML endpoints:

Endpoint Description
/saml/{provider}/metadata SP metadata for IdP configuration
/saml/{provider}/login Initiate SAML authentication
/saml/acs Assertion Consumer Service (receives SAML responses)
/saml/slo Single Logout endpoint

Group-Based Access Control

SAML groups enable fine-grained access control:

{
  "authorization": [
    {
      "subject": "security-team",
      "subject_type": "group",
      "idp": "saml_okta",
      "role": "writer"
    }
  ]
}

Provider Isolation: Groups from different IdPs are treated separately. A "security-team" group from Okta is distinct from "security-team" from Azure AD.

Group Discovery API

Use the groups endpoint for UI autocomplete:

GET /oauth2/providers/saml_okta/groups

Response:

{
  "idp": "saml_okta",
  "groups": [
    {
      "name": "security-team",
      "display_name": "Security Team",
      "used_in_authorizations": true
    }
  ]
}

Security Considerations

  1. Signature Validation: All SAML responses are cryptographically validated
  2. Certificate Management: Regularly rotate SP certificates
  3. Group Caching: Groups cached in Redis, cleared on logout
  4. Provider Isolation: Cross-provider access prevented by design

SAML Resources

Security Best Practices

  1. HTTPS Only: Always use HTTPS for OAuth callbacks in production
  2. Secure Secrets: Never commit client secrets to git
  3. State Parameter: Always validate state for CSRF protection
  4. Token Storage: Store tokens securely (httpOnly cookies preferred)
  5. Token Expiration: Use short expiration for access tokens (1 hour)
  6. Refresh Tokens: Implement refresh token rotation
  7. Scope Minimization: Request only necessary OAuth scopes
  8. Regular Rotation: Rotate OAuth client secrets periodically
  9. Monitor Access: Log authentication events
  10. Revocation: Implement token revocation for logout

Environment Variable-Only Configuration

TMI supports dynamic OAuth provider discovery through environment variables alone, without requiring YAML configuration files. This approach is ideal for containerized deployments, Heroku, and other platforms where configuration files are less practical.

Overview

OAuth providers are discovered by scanning for environment variables matching the pattern:

OAUTH_PROVIDERS_<PROVIDER_ID>_ENABLED=true

No hardcoded defaults - all configuration must be explicitly provided via environment variables.

Environment Variable Naming Convention

All OAuth provider configuration uses the pattern:

OAUTH_PROVIDERS_<PROVIDER_ID>_<FIELD>=<value>

Where:

  • <PROVIDER_ID> is an uppercase identifier (e.g., GOOGLE, GITHUB, MICROSOFT)
  • Provider keys are automatically converted to lowercase (e.g., GOOGLE to google)
  • Underscores in provider IDs are converted to hyphens (e.g., MY_PROVIDER to my-provider)

Required Fields

Each OAuth provider must have these fields configured:

Variable Pattern Description Example
_ENABLED Must be true to activate provider true
_CLIENT_ID OAuth client ID from provider abc123.apps.googleusercontent.com
_CLIENT_SECRET OAuth client secret from provider GOCSPX-xyz789...
_AUTHORIZATION_URL Provider's authorization endpoint https://accounts.google.com/o/oauth2/auth
_TOKEN_URL Provider's token endpoint https://oauth2.googleapis.com/token
_USERINFO_URL Primary userinfo endpoint https://www.googleapis.com/oauth2/v3/userinfo
_SCOPES Comma-separated list of scopes openid,profile,email

Optional Fields

Variable Pattern Description Example
_ID Provider ID (defaults to lowercase provider key) google
_NAME Display name Google
_ICON Icon URL or identifier https://example.com/google-icon.png
_ISSUER OIDC issuer URL for ID token validation https://accounts.google.com
_JWKS_URL JWKS endpoint for ID token verification https://www.googleapis.com/oauth2/v3/certs
_AUTH_HEADER_FORMAT Authorization header format (default: Bearer %s) token %s
_ACCEPT_HEADER Accept header for requests (default: application/json) application/json

Multiple Userinfo Endpoints

Some providers (like GitHub) require multiple userinfo endpoints:

Variable Pattern Description
_USERINFO_URL Primary endpoint
_USERINFO_SECONDARY_URL Second endpoint (optional)
_USERINFO_ADDITIONAL_URL Third endpoint (optional)

Claim Mappings

Customize claim extraction using these patterns:

OAUTH_PROVIDERS_<PROVIDER_ID>_USERINFO_CLAIMS_<CLAIM_NAME>=<json_path>
OAUTH_PROVIDERS_<PROVIDER_ID>_USERINFO_SECONDARY_CLAIMS_<CLAIM_NAME>=<json_path>
OAUTH_PROVIDERS_<PROVIDER_ID>_USERINFO_ADDITIONAL_CLAIMS_<CLAIM_NAME>=<json_path>

Standard Claim Names

  • SUBJECT_CLAIM - User's unique identifier
  • EMAIL_CLAIM - User's email address
  • EMAIL_VERIFIED_CLAIM - Email verification status
  • NAME_CLAIM - Full name
  • GIVEN_NAME_CLAIM - First name
  • FAMILY_NAME_CLAIM - Last name
  • PICTURE_CLAIM - Avatar/profile picture URL
  • GROUPS_CLAIM - Group memberships

Example: GitHub Custom Claims

OAUTH_PROVIDERS_GITHUB_USERINFO_CLAIMS_SUBJECT_CLAIM=id
OAUTH_PROVIDERS_GITHUB_USERINFO_CLAIMS_NAME_CLAIM=name
OAUTH_PROVIDERS_GITHUB_USERINFO_CLAIMS_PICTURE_CLAIM=avatar_url
OAUTH_PROVIDERS_GITHUB_USERINFO_SECONDARY_CLAIMS_EMAIL_CLAIM=[0].email
OAUTH_PROVIDERS_GITHUB_USERINFO_SECONDARY_CLAIMS_EMAIL_VERIFIED_CLAIM=[0].verified

Additional OAuth Parameters

Add custom parameters to authorization requests:

OAUTH_PROVIDERS_<PROVIDER_ID>_ADDITIONAL_PARAMS_<PARAM_NAME>=<value>

Example: Google Offline Access

OAUTH_PROVIDERS_GOOGLE_ADDITIONAL_PARAMS_ACCESS_TYPE=offline
OAUTH_PROVIDERS_GOOGLE_ADDITIONAL_PARAMS_PROMPT=consent

Complete Environment Variable Examples

Google OAuth (Environment Variables Only)

OAUTH_PROVIDERS_GOOGLE_ENABLED=true
OAUTH_PROVIDERS_GOOGLE_CLIENT_ID=123456789.apps.googleusercontent.com
OAUTH_PROVIDERS_GOOGLE_CLIENT_SECRET=GOCSPX-abc123def456
OAUTH_PROVIDERS_GOOGLE_NAME=Google
OAUTH_PROVIDERS_GOOGLE_ICON=https://example.com/google.png
OAUTH_PROVIDERS_GOOGLE_AUTHORIZATION_URL=https://accounts.google.com/o/oauth2/auth
OAUTH_PROVIDERS_GOOGLE_TOKEN_URL=https://oauth2.googleapis.com/token
OAUTH_PROVIDERS_GOOGLE_USERINFO_URL=https://www.googleapis.com/oauth2/v3/userinfo
OAUTH_PROVIDERS_GOOGLE_ISSUER=https://accounts.google.com
OAUTH_PROVIDERS_GOOGLE_JWKS_URL=https://www.googleapis.com/oauth2/v3/certs
OAUTH_PROVIDERS_GOOGLE_SCOPES=openid,profile,email

GitHub OAuth (Environment Variables Only)

OAUTH_PROVIDERS_GITHUB_ENABLED=true
OAUTH_PROVIDERS_GITHUB_CLIENT_ID=Iv1.abc123def456
OAUTH_PROVIDERS_GITHUB_CLIENT_SECRET=ghp_xyz789abc123
OAUTH_PROVIDERS_GITHUB_NAME=GitHub
OAUTH_PROVIDERS_GITHUB_ICON=https://example.com/github.png
OAUTH_PROVIDERS_GITHUB_AUTHORIZATION_URL=https://github.com/login/oauth/authorize
OAUTH_PROVIDERS_GITHUB_TOKEN_URL=https://github.com/login/oauth/access_token
OAUTH_PROVIDERS_GITHUB_USERINFO_URL=https://api.github.com/user
OAUTH_PROVIDERS_GITHUB_USERINFO_SECONDARY_URL=https://api.github.com/user/emails
OAUTH_PROVIDERS_GITHUB_AUTH_HEADER_FORMAT=token %s
OAUTH_PROVIDERS_GITHUB_ACCEPT_HEADER=application/json
OAUTH_PROVIDERS_GITHUB_SCOPES=user:email
OAUTH_PROVIDERS_GITHUB_USERINFO_CLAIMS_SUBJECT_CLAIM=id
OAUTH_PROVIDERS_GITHUB_USERINFO_CLAIMS_NAME_CLAIM=name
OAUTH_PROVIDERS_GITHUB_USERINFO_CLAIMS_PICTURE_CLAIM=avatar_url
OAUTH_PROVIDERS_GITHUB_USERINFO_SECONDARY_CLAIMS_EMAIL_CLAIM=[0].email
OAUTH_PROVIDERS_GITHUB_USERINFO_SECONDARY_CLAIMS_EMAIL_VERIFIED_CLAIM=[0].verified

Microsoft OAuth (Environment Variables Only)

OAUTH_PROVIDERS_MICROSOFT_ENABLED=true
OAUTH_PROVIDERS_MICROSOFT_CLIENT_ID=12345678-1234-1234-1234-123456789abc
OAUTH_PROVIDERS_MICROSOFT_CLIENT_SECRET=abc~123DEF456ghi789
OAUTH_PROVIDERS_MICROSOFT_NAME=Microsoft
OAUTH_PROVIDERS_MICROSOFT_ICON=https://example.com/microsoft.png
# Use UUID-based URLs for personal Microsoft accounts
OAUTH_PROVIDERS_MICROSOFT_AUTHORIZATION_URL=https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/oauth2/v2.0/authorize
OAUTH_PROVIDERS_MICROSOFT_TOKEN_URL=https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/oauth2/v2.0/token
OAUTH_PROVIDERS_MICROSOFT_USERINFO_URL=https://graph.microsoft.com/v1.0/me
OAUTH_PROVIDERS_MICROSOFT_ISSUER=https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0
OAUTH_PROVIDERS_MICROSOFT_JWKS_URL=https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/discovery/v2.0/keys
OAUTH_PROVIDERS_MICROSOFT_SCOPES=openid,profile,email,User.Read
OAUTH_PROVIDERS_MICROSOFT_USERINFO_CLAIMS_SUBJECT_CLAIM=id
OAUTH_PROVIDERS_MICROSOFT_USERINFO_CLAIMS_EMAIL_CLAIM=mail
OAUTH_PROVIDERS_MICROSOFT_USERINFO_CLAIMS_NAME_CLAIM=displayName
OAUTH_PROVIDERS_MICROSOFT_USERINFO_CLAIMS_GIVEN_NAME_CLAIM=givenName
OAUTH_PROVIDERS_MICROSOFT_USERINFO_CLAIMS_FAMILY_NAME_CLAIM=surname
OAUTH_PROVIDERS_MICROSOFT_USERINFO_CLAIMS_EMAIL_VERIFIED_CLAIM=true

Important Notes for Microsoft:

  1. UUID vs /consumers: Microsoft returns the UUID-based issuer (9188040d-6c67-4c5b-b112-36a304b66dad) for personal accounts instead of /consumers. You MUST use the UUID in all URLs for issuer validation to pass.

  2. Finding your tenant UUID: The UUID 9188040d-6c67-4c5b-b112-36a304b66dad is the standard Microsoft consumer tenant. For organizational tenants, use your organization's tenant ID.

  3. Issuer URL matching: The ISSUER URL must exactly match what Microsoft returns in ID tokens, or OIDC validation will fail.

Heroku Configuration

To configure providers in Heroku:

# Set variables one at a time
heroku config:set OAUTH_PROVIDERS_GOOGLE_ENABLED=true --app your-app-name
heroku config:set OAUTH_PROVIDERS_GOOGLE_CLIENT_ID=your-client-id --app your-app-name
# ... etc

# Or use Heroku dashboard UI to set all variables

Verifying Configuration

Check loaded providers in application logs:

OAuth providers loaded providers_count=3 enabled_providers=[google github microsoft]

Each provider should log:

Loaded OAuth provider configuration provider_key=google name=Google

Environment Variable Troubleshooting

Provider not appearing

  • Verify OAUTH_PROVIDERS_<PROVIDER_ID>_ENABLED=true is set
  • Check provider ID format (must be uppercase in environment variable)
  • Review application logs for discovery output

OIDC issuer mismatch error

failed to create OIDC provider: oidc: issuer URL provided to client (...) did not match the issuer URL returned by provider (...)

Solution: Set OAUTH_PROVIDERS_<PROVIDER_ID>_ISSUER to the exact URL the provider returns. For Microsoft personal accounts, use the UUID-based URL (9188040d-6c67-4c5b-b112-36a304b66dad), not /consumers.

Missing userinfo data

  • Verify USERINFO_URL is correct
  • Check claim mappings match provider's JSON response
  • Add secondary/additional endpoints if needed

Managing Multiple Environments

Environment-Specific OAuth Apps

Create separate OAuth applications for each environment:

Environment Google App GitHub App Microsoft App
Development TMI Dev tmi-dev TMI Development
Staging TMI Staging tmi-staging TMI Staging
Production TMI Production tmi-prod TMI Production

Benefits:

  • Separate credentials per environment
  • Different redirect URIs
  • Independent monitoring
  • Safer testing

Configuration Management

Use environment-specific configurations:

# Development
export CONFIG_ENV=development
./tmiserver --config=config-${CONFIG_ENV}.yml

# Production
export CONFIG_ENV=production
./tmiserver --config=config-${CONFIG_ENV}.yml

Next Steps

Related Pages

Clone this wiki locally