-
Notifications
You must be signed in to change notification settings - Fork 0
Setting Up Authentication
This guide covers configuring OAuth authentication for TMI with Google, GitHub, and Microsoft identity providers.
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:
- User clicks login button in web app
- Web app redirects to OAuth provider
- User authenticates with provider
- Provider redirects back to web app with authorization code
- Web app sends code to TMI server
- TMI server exchanges code for user info
- TMI server issues JWT tokens
- Web app uses JWT for API requests
[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]
- Web Application: Initiates OAuth flow, handles callback
- TMI Server: Exchanges authorization codes, issues JWT tokens
- OAuth Provider: Authenticates users, provides user information
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)
-
Go to Google Cloud Console:
- Navigate to console.cloud.google.com
- Create a new project or select existing project
-
Enable APIs:
- Go to "APIs & Services" → "Library"
- Search for "Google+ API" and enable it
-
Create OAuth Credentials:
- Go to "APIs & Services" → "Credentials"
- Click "Create Credentials" → "OAuth client ID"
- Choose "Web application"
-
Configure OAuth Consent Screen (if not done):
- User Type: External (or Internal for Google Workspace)
- App name: TMI Threat Modeling
- User support email: your-email@example.com
- Developer contact: your-email@example.com
- Scopes:
openid,profile,email
-
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)
-
-
-
Get Credentials:
- Copy Client ID and Client Secret
- Save for configuration
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-secretConfiguration 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"]Configure in src/environments/environment.prod.ts:
export const environment = {
production: true,
apiUrl: 'https://api.tmi.example.com',
// Google OAuth handled by TMI server
};# Check provider availability
curl https://api.tmi.example.com/oauth2/providers
# Should include:
{
"providers": [
{"id": "google", "name": "Google", "icon": "google"}
]
}-
Go to GitHub Settings:
- Navigate to github.com/settings/developers
- Click "OAuth Apps" → "New OAuth App"
-
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
-
Get Credentials:
- Copy Client ID
- Generate and copy Client Secret
Environment Variables:
OAUTH_PROVIDERS_GITHUB_ENABLED=true
OAUTH_PROVIDERS_GITHUB_CLIENT_ID=your-github-client-id
OAUTH_PROVIDERS_GITHUB_CLIENT_SECRET=your-github-client-secretConfiguration 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"]# Verify provider is configured
curl https://api.tmi.example.com/oauth2/providers | jq '.providers[] | select(.id=="github")'
# Should return GitHub provider detailsMicrosoft OAuth is more complex due to different account types. Choose the appropriate configuration based on your needs.
-
Go to Azure Portal:
- Navigate to portal.azure.com
- Go to "Azure Active Directory" → "App registrations"
- Click "New registration"
-
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
-
Set Redirect URI:
- Platform: Web
- Redirect URI:
https://tmi.example.com/oauth2/callback
-
Configure API Permissions:
- Add permissions:
- Microsoft Graph → Delegated permissions
User.Readopenidprofileemail
- Grant admin consent (if required by your organization)
- Add permissions:
-
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!)
-
Get Application ID:
- Copy the Application (client) ID from Overview page
Microsoft has different endpoints based on account types. Choose the appropriate configuration:
Environment Variables:
OAUTH_PROVIDERS_MICROSOFT_ENABLED=true
OAUTH_PROVIDERS_MICROSOFT_CLIENT_ID=your-microsoft-client-id
OAUTH_PROVIDERS_MICROSOFT_CLIENT_SECRET=your-microsoft-client-secretConfiguration 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"]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"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"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.
# 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"}'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 configAuthorized Redirect URIs (in OAuth provider dashboards):
- Google:
http://localhost:4200/oauth2/callback - GitHub:
http://localhost:4200/oauth2/callback - Microsoft:
http://localhost:4200/oauth2/callback
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
# Generate 32-character random secret
openssl rand -base64 32
# Or use this command
python3 -c "import secrets; print(secrets.token_urlsafe(32))"Environment Variables:
JWT_SECRET=your-very-secure-random-secret-minimum-32-characters
JWT_EXPIRATION_SECONDS=3600 # 1 hour
JWT_SIGNING_METHOD=HS256Configuration File:
auth:
jwt:
secret: "${JWT_SECRET}"
expiration_seconds: 3600 # 1 hour for access token
signing_method: "HS256"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
- Check available providers:
curl https://api.tmi.example.com/oauth2/providers- 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
- 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"
}'- Verify token:
curl https://api.tmi.example.com/oauth2/me \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"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 .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
Problem: OAuth provider rejects redirect URI.
Solutions:
- Verify redirect URI in OAuth provider dashboard matches exactly
- Check for trailing slash differences (
/callbackvs/callback/) - Ensure protocol matches (http vs https)
- Confirm domain matches exactly (including www if present)
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
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}/
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
Enable detailed OAuth logging:
logging:
level: "debug"
log_api_requests: true
log_api_responses: trueCheck 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 generationGoogle:
# 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"TMI supports SAML 2.0 authentication for enterprise Single Sign-On (SSO) with identity providers like Okta, Azure AD, and others.
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
TMI uses the crewjam/saml library (v0.5.1) for SAML 2.0 protocol handling.
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"| 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 |
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
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 |
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.
Use the groups endpoint for UI autocomplete:
GET /oauth2/providers/saml_okta/groupsResponse:
{
"idp": "saml_okta",
"groups": [
{
"name": "security-team",
"display_name": "Security Team",
"used_in_authorizations": true
}
]
}- Signature Validation: All SAML responses are cryptographically validated
- Certificate Management: Regularly rotate SP certificates
- Group Caching: Groups cached in Redis, cleared on logout
- Provider Isolation: Cross-provider access prevented by design
- HTTPS Only: Always use HTTPS for OAuth callbacks in production
- Secure Secrets: Never commit client secrets to git
- State Parameter: Always validate state for CSRF protection
- Token Storage: Store tokens securely (httpOnly cookies preferred)
- Token Expiration: Use short expiration for access tokens (1 hour)
- Refresh Tokens: Implement refresh token rotation
- Scope Minimization: Request only necessary OAuth scopes
- Regular Rotation: Rotate OAuth client secrets periodically
- Monitor Access: Log authentication events
- Revocation: Implement token revocation for logout
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.
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.
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.,
GOOGLEtogoogle) - Underscores in provider IDs are converted to hyphens (e.g.,
MY_PROVIDERtomy-provider)
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 |
| 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 |
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) |
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>
-
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
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].verifiedAdd custom parameters to authorization requests:
OAUTH_PROVIDERS_<PROVIDER_ID>_ADDITIONAL_PARAMS_<PARAM_NAME>=<value>
OAUTH_PROVIDERS_GOOGLE_ADDITIONAL_PARAMS_ACCESS_TYPE=offline
OAUTH_PROVIDERS_GOOGLE_ADDITIONAL_PARAMS_PROMPT=consentOAUTH_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,emailOAUTH_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].verifiedOAUTH_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=trueImportant Notes for Microsoft:
-
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. -
Finding your tenant UUID: The UUID
9188040d-6c67-4c5b-b112-36a304b66dadis the standard Microsoft consumer tenant. For organizational tenants, use your organization's tenant ID. -
Issuer URL matching: The
ISSUERURL must exactly match what Microsoft returns in ID tokens, or OIDC validation will fail.
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 variablesCheck 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
- Verify
OAUTH_PROVIDERS_<PROVIDER_ID>_ENABLED=trueis set - Check provider ID format (must be uppercase in environment variable)
- Review application logs for discovery output
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.
- Verify
USERINFO_URLis correct - Check claim mappings match provider's JSON response
- Add secondary/additional endpoints if needed
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
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- Component Integration - Connect web app to authentication
- Database Setup - Configure user storage
- Post-Deployment - Test authentication flow
- Security Best Practices - Additional security hardening
- Using TMI for Threat Modeling
- Accessing TMI
- Creating Your First Threat Model
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Metadata and Extensions
- Planning Your Deployment
- Deploying TMI Server
- OCI Container Deployment
- Terraform Deployment
- Certificate Automation
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Component Integration
- Post-Deployment
- Monitoring and Health
- Cloud Logging
- Database Operations
- Security Operations
- Performance and Scaling
- Maintenance Tasks