Skip to content

Setting Up Authentication

Eric Fitzgerald edited this page Nov 12, 2025 · 1 revision

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):

    • User Type: External (or Internal for Google Workspace)
    • App name: TMI Threat Modeling
    • User support email: [email protected]
    • Developer contact: [email protected]
    • Scopes: openid, profile, email
  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": "[email protected]",
  "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"

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

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