-
Notifications
You must be signed in to change notification settings - Fork 2
Setting Up Authentication
This guide walks you through configuring OAuth authentication for TMI with Google, GitHub, and Microsoft identity providers. For an overview of how authentication works in TMI (including OAuth flows, client credentials, and JWT tokens), see Authentication. For general architecture context, see Architecture-and-Design.
TMI uses OAuth 2.0 for authentication. You can enable one or more of the following providers:
- Google OAuth -- Google accounts and Google Workspace
- GitHub OAuth -- GitHub personal and organization accounts
- Microsoft OAuth -- Microsoft accounts (personal, work, and school)
TMI implements the OAuth 2.0 Authorization Code flow with PKCE (Proof Key for Code Exchange). Here is the step-by-step sequence:
- The user clicks the login button in the web app.
- The web app generates a PKCE code verifier and code challenge.
- The web app redirects to the TMI server at
/oauth2/authorize, passingcode_challenge,code_challenge_method=S256,client_callback, andidpparameters. - The TMI server stores the PKCE challenge and redirects to the OAuth provider.
- The user authenticates with the provider.
- The provider redirects back to the TMI server at
/oauth2/callbackwith an authorization code. - The TMI server binds the PKCE challenge to the authorization code and redirects back to the web app's
client_callbackwith the code and state. - The web app exchanges the authorization code and
code_verifierat the TMI server's/oauth2/tokenendpoint. - The TMI server validates the PKCE challenge, exchanges the code with the OAuth provider for user info, and issues JWT tokens.
- The web app uses the JWT for subsequent API requests.
[User Browser]
↓ (1) Click login
[TMI Web App]
↓ (2) Generate PKCE code_verifier + code_challenge
↓ (3) Redirect to /oauth2/authorize?idp=...&code_challenge=...&client_callback=...
[TMI Server]
↓ (4) Store PKCE challenge, redirect to OAuth provider
[OAuth Provider (Google/GitHub/Microsoft)]
↓ (5) User authenticates
[OAuth Provider]
↓ (6) Redirect with auth code to /oauth2/callback
[TMI Server]
↓ (7) Bind PKCE challenge to auth code, redirect to client_callback
[TMI Web App]
↓ (8) POST /oauth2/token with code + code_verifier + grant_type=authorization_code
[TMI Server]
↓ (9) Validate PKCE, exchange code with provider, return JWT tokens
[TMI Web App]
↓ (10) Use JWT for API calls
[TMI Server]
| Component | Role |
|---|---|
| Web Application | Initiates the OAuth flow with PKCE and exchanges the authorization code for tokens |
| TMI Server | Manages PKCE challenges, handles OAuth callbacks, exchanges codes with providers, and issues JWT tokens |
| OAuth Provider | Authenticates users and provides user information |
| Redis | Stores PKCE challenges, OAuth state, refresh tokens, and the token blacklist |
For more detail on Redis usage, see Database-Setup.
Before you set up authentication, confirm the following are in place:
- TMI server deployed and accessible (see Deploying-TMI-Server)
- TMI web application deployed, for production redirect URIs (see Deploying-TMI-Web-Application)
- Domain names configured for production
- SSL/TLS certificates installed (HTTPS is required for OAuth in production; see Certificate-Automation)
-
Go to Google Cloud Console
- Navigate to console.cloud.google.com.
- Create a new project or select an existing project.
-
Create OAuth Credentials
- Go to "APIs & Services" > "Credentials".
- Click "Create Credentials" > "OAuth client ID".
- Choose "Web application".
-
Configure the OAuth Consent Screen (if not already 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 the 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 the Client ID and Client Secret.
- Save them 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 the API URL 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 the 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 the Client ID.
- Generate and copy the 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 than Google or GitHub because Microsoft supports different account types, each with its own endpoint. Choose the configuration that matches your needs.
-
Go to the Azure Portal
- Navigate to portal.azure.com.
- Go to "Azure Active Directory" > "App registrations".
- Click "New registration".
-
Configure the Application
- Name: TMI Threat Modeling
- Supported account types -- choose based on your 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 the Redirect URI
- Platform: Web
- Redirect URI:
https://tmi.example.com/oauth2/callback
-
Configure API Permissions
- Add the following permissions under Microsoft Graph > Delegated permissions:
User.Readopenidprofileemail
- Grant admin consent if required by your organization.
- Add the following permissions under Microsoft Graph > Delegated permissions:
-
Create a Client Secret
- Go to "Certificates & secrets".
- Click "New client secret".
- Description: TMI Server.
- Expires: choose an expiration period (recommended: 24 months).
- Copy the secret value immediately -- it is shown only once.
-
Get the Application ID
- Copy the Application (client) ID from the Overview page.
Microsoft uses different endpoints depending on the account types you support. Choose the configuration that matches your Azure AD app registration.
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 you choose must match the "Supported account types" setting in your Azure AD app registration. A mismatch causes authentication failures.
# 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, use the following settings. See Getting-Started-with-Development for full development environment setup.
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
For production deployments, always use environment variables for secrets. See Configuration-Reference for the complete list of settings.
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
Use either of these commands to generate a cryptographically random secret:
# Option 1: OpenSSL
openssl rand -base64 32
# Option 2: Python
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, default 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, default 30 days):
- Stored in the database.
- Used to obtain new access tokens without re-authenticating.
- Can be revoked individually.
For more on token security practices, see Security-Best-Practices.
-
Check available providers
curl https://api.tmi.example.com/oauth2/providers
-
Start the OAuth flow (open in a 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 the authorization code (from the 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 the token
curl https://api.tmi.example.com/oauth2/me \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
For more details on the API endpoints used above, see REST-API-Reference.
You can create a test script (test-auth.sh) to validate all providers at once:
#!/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 .For additional troubleshooting guidance, see Common-Issues and Debugging-Guide.
Problem: The TMI server cannot find the OAuth provider configuration.
Solutions:
- Verify that the provider is enabled in your configuration file.
- Check that the required environment variables are set.
- Restart the TMI server after making configuration changes.
- Check the server logs for configuration loading errors.
Problem: The OAuth provider rejects the redirect URI.
Solutions:
- Verify the redirect URI in the OAuth provider dashboard matches the configured URI exactly.
- Check for trailing slash differences (
/callbackvs./callback/). - Ensure the protocol matches (
httpvs.https). - Confirm the domain matches exactly, including
wwwif present.
Problem: The TMI server cannot exchange the authorization code for tokens.
Solutions:
- Verify the client secret is correct.
- Check the callback URL configuration.
- Ensure the authorization code has not expired (use it immediately after receiving it).
- Verify the OAuth provider credentials are still active.
Problem: Endpoint type doesn't match Azure AD app configuration.
Solution: Match the endpoint to your app's "Supported account types" setting:
- Personal only →
/consumers/ - Work/school only →
/organizations/ - Both →
/common/ - Specific tenant →
/{tenant-id}/
Problem: State parameter doesn't match.
Solutions:
- Verify the web app properly stores and retrieves the state parameter.
- Check for session storage issues.
- Ensure the state value is generated randomly and securely.
- Verify there are no URL encoding issues with the 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
- Authentication
- Creating Your First Threat Model
- Understanding the User Interface
- Working with Data Flow Diagrams
- Managing Threats
- Collaborative Threat Modeling
- Using Notes and Documentation
- Timmy AI Assistant
- Metadata and Extensions
- Planning Your Deployment
- Terraform Deployment (AWS, OCI, GCP, Azure)
- Deploying TMI Server
- OCI Container Deployment
- Certificate Automation
- Deploying TMI Web Application
- Setting Up Authentication
- Database Setup
- Component Integration
- Post-Deployment
- Branding and Customization
- Monitoring and Health
- Cloud Logging
- Database Operations
- Security Operations
- Performance and Scaling
- Maintenance Tasks
- Getting Started with Development
- Architecture and Design
- API Integration
- Testing
- Contributing
- Extending TMI
- Dependency Upgrade Plans
- DFD Graphing Library Reference
- Migration Instructions