External Identities
External Identities
Section titled “External Identities”Manage links between Mantis users and external identity provider accounts.
Overview
Section titled “Overview”External identities link Mantis user accounts to identity provider accounts, enabling SSO authentication and user lifecycle management.
External Identity Model
Section titled “External Identity Model”| Field | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
user_id | UUID | Linked Mantis user |
provider_id | UUID | Identity provider reference |
external_user_id | String | User ID at IdP (sub claim) |
external_username | String? | Username at IdP |
external_email | String? | Email at IdP |
access_token_encrypted | String? | Encrypted access token |
refresh_token_encrypted | String? | Encrypted refresh token |
token_expires_at | DateTime? | Token expiration |
last_login_at | DateTime? | Last SSO login via this link |
created_at | DateTime | Link creation time |
How External Identities Work
Section titled “How External Identities Work”Identity Linking Flow
Section titled “Identity Linking Flow”Multiple Identities
Section titled “Multiple Identities”A single Mantis user can have multiple external identities:
This allows users to:
- Login via different IdPs
- Migrate between IdPs
- Use personal and work accounts
Viewing External Identities
Section titled “Viewing External Identities”User Details
Section titled “User Details”User details include linked external identities:
curl -H "Authorization: Bearer $TOKEN" \ "https://api.mantis.local/api/v1/admin/users/$USER_ID"Response:
{ "id": "user_abc123", "email": "alice@company.com", "username": "alice", "external_identities": [ { "provider_id": "idp_xyz789", "provider_name": "Okta", "external_username": "alice.smith", "external_email": "alice.smith@company.com", "last_login_at": "2024-01-15T10:30:00Z" }, { "provider_id": "idp_abc456", "provider_name": "Azure AD", "external_username": "asmith", "external_email": "asmith@company.onmicrosoft.com", "last_login_at": "2024-01-10T08:15:00Z" } ]}Direct Query
Section titled “Direct Query”SELECT ei.id, u.email AS mantis_email, ip.name AS provider_name, ei.external_username, ei.external_email, ei.last_login_atFROM external_identities eiJOIN users u ON u.id = ei.user_idJOIN identity_providers ip ON ip.id = ei.provider_idWHERE u.email = 'alice@company.com';Identity Link Scenarios
Section titled “Identity Link Scenarios”Automatic Linking by Email
Section titled “Automatic Linking by Email”When a user logs in via SSO, Mantis checks for existing users by email:
Manual Linking
Section titled “Manual Linking”Currently, external identity links are created automatically during SSO. To manually create a link:
INSERT INTO external_identities ( user_id, provider_id, external_user_id, external_username, external_email, created_at) VALUES ( (SELECT id FROM users WHERE email = 'alice@company.com'), (SELECT id FROM identity_providers WHERE name = 'Okta'), 'okta_external_sub_id', 'alice.smith', 'alice.smith@company.com', NOW());Unlinking Identities
Section titled “Unlinking Identities”When to Unlink
Section titled “When to Unlink”- User leaving organization
- Migrating to different IdP
- Security incident response
- Account cleanup
Via SQL
Section titled “Via SQL”-- Delete specific external identity linkDELETE FROM external_identitiesWHERE user_id = (SELECT id FROM users WHERE email = 'alice@company.com')AND provider_id = (SELECT id FROM identity_providers WHERE name = 'Okta');Token Storage
Section titled “Token Storage”Encrypted Tokens
Section titled “Encrypted Tokens”IdP tokens are stored encrypted for potential future use:
| Token | Purpose |
|---|---|
access_token | API calls to IdP (user info, etc.) |
refresh_token | Obtaining new access tokens |
Tokens are encrypted using AES-256-GCM with provider-specific AAD:
let aad = format!("external_identity:{}:{}", provider_id, external_user_id);let encrypted = encryptor.encrypt_string(token, &aad)?;Token Refresh
Section titled “Token Refresh”On each SSO login, tokens are updated:
UPDATE external_identitiesSET access_token_encrypted = $new_access_token, refresh_token_encrypted = $new_refresh_token, token_expires_at = $expiration, last_login_at = NOW(), updated_at = NOW()WHERE id = $identity_id;Identity Conflicts
Section titled “Identity Conflicts”Same External ID, Different Users
Section titled “Same External ID, Different Users”External ID "ext123" → User "alice"New login with "ext123" → Always maps to "alice"External identity links are unique per provider:
UNIQUE (provider_id, external_user_id)Same Email, Different External IDs
Section titled “Same Email, Different External IDs”User "alice@company.com" ← External ID "ext123" (Okta)User "alice@company.com" ← External ID "ext456" (Azure AD)Both link to the same Mantis user via email matching.
Conflict Resolution
Section titled “Conflict Resolution”| Scenario | Behavior |
|---|---|
| Existing external_id + same user | Update tokens |
| Existing external_id + different user | Use original link |
| New external_id + existing email | Link to existing user |
| New external_id + new email | Create new user (if auto_create) |
Last Login Tracking
Section titled “Last Login Tracking”Purpose
Section titled “Purpose”last_login_at tracks the most recent SSO login via each provider:
SELECT ip.name, ei.last_login_at, NOW() - ei.last_login_at AS time_since_loginFROM external_identities eiJOIN identity_providers ip ON ip.id = ei.provider_idWHERE ei.user_id = $user_idORDER BY ei.last_login_at DESC;Inactive Identity Detection
Section titled “Inactive Identity Detection”Find identities not used recently:
SELECT u.email, ip.name AS provider, ei.last_login_atFROM external_identities eiJOIN users u ON u.id = ei.user_idJOIN identity_providers ip ON ip.id = ei.provider_idWHERE ei.last_login_at < NOW() - INTERVAL '90 days'ORDER BY ei.last_login_at;User Lifecycle
Section titled “User Lifecycle”Onboarding
Section titled “Onboarding”- User assigned to IdP groups
- First SSO login creates:
- Mantis user (if auto_create enabled)
- External identity link
- Role assignments (from mappings)
Offboarding
Section titled “Offboarding”- Remove user from IdP groups
- Disable IdP account
- (Optional) Disable Mantis account
- (Optional) Remove external identity links
IdP Migration
Section titled “IdP Migration”- Configure new IdP
- Users login via new IdP
- New external identity links created automatically
- Old links remain (can use both IdPs)
- (Optional) Remove old links after migration complete
Audit Trail
Section titled “Audit Trail”External identity events are logged:
Link Created (first SSO login)
Section titled “Link Created (first SSO login)”When a new external identity link is created during SSO, Mantis emits a single event covering both the new user/link creation and the resulting login:
{ "event": "auth.sso_user_created", "category": "authentication", "severity": "security", "actor": { "user_id": "...", "username": "alice" }, "resource": { "type": "user", "id": "..." }, "metadata": { "provider_id": "...", "provider_name": "Okta", "external_user_id": "00u1234567890", "created": true }, "outcome": "success"}Login via External Identity
Section titled “Login via External Identity”Subsequent logins via an existing external identity link emit:
{ "event": "auth.sso_login", "category": "authentication", "severity": "security", "actor": { "user_id": "...", "username": "alice" }, "resource": { "type": "user", "id": "..." }, "metadata": { "provider_id": "019b937d-4862-8001-a001-000000000001", "provider_name": "Okta", "external_user_id": "00u1234567890", "created": false }, "outcome": "success"}Database Schema
Section titled “Database Schema”CREATE TABLE external_identities ( id UUID PRIMARY KEY, user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, provider_id UUID NOT NULL REFERENCES identity_providers(id) ON DELETE CASCADE, external_user_id TEXT NOT NULL, external_username TEXT, external_email TEXT, access_token_encrypted TEXT, refresh_token_encrypted TEXT, token_expires_at TIMESTAMP, last_login_at TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP, UNIQUE (provider_id, external_user_id));
CREATE INDEX idx_external_identities_user_id ON external_identities(user_id);CREATE INDEX idx_external_identities_provider_id ON external_identities(provider_id);CREATE INDEX idx_external_identities_external_user_id ON external_identities(external_user_id);Troubleshooting
Section titled “Troubleshooting”User Cannot Login via SSO
Section titled “User Cannot Login via SSO”-
Check external identity exists:
SELECT * FROM external_identitiesWHERE external_user_id = 'sub_from_idp'AND provider_id = $provider_id; -
Verify linked user is active:
SELECT u.status FROM users uJOIN external_identities ei ON ei.user_id = u.idWHERE ei.external_user_id = 'sub_from_idp'; -
Check provider is enabled:
SELECT is_enabled FROM identity_providers WHERE id = $provider_id;
Wrong User Linked
Section titled “Wrong User Linked”This occurs when:
- External ID was already linked to different user
- Email changed at IdP
Resolution:
-- Remove incorrect linkDELETE FROM external_identitiesWHERE id = $incorrect_link_id;
-- User will be re-linked on next loginDuplicate Users Created
Section titled “Duplicate Users Created”Occurs when:
- Email not returned in ID token
- Different email at IdP than Mantis
Prevention:
- Ensure email scope is requested
- Configure correct email claim
Resolution:
- Merge users manually
- Update external identity links
Token Decryption Fails
Section titled “Token Decryption Fails”Error: Failed to decrypt client secret- Verify encryption key hasn’t changed
- Check AAD format matches
- Re-link identity if key rotated
Best Practices
Section titled “Best Practices”Security
Section titled “Security”- Audit links regularly - Review inactive links
- Monitor login patterns - Detect anomalies
- Prompt token rotation - On security events
- Limit link age - Consider expiring old links
Operations
Section titled “Operations”- Document linking behavior for support team
- Test migrations before switching IdPs
- Monitor link creation for unexpected patterns
- Clean up unused external identities
User Experience
Section titled “User Experience”- Clear error messages for link issues
- Support multiple IdPs per user when needed
- Communicate changes when migrating IdPs
Next Steps
Section titled “Next Steps”- Overview - Identity provider overview
- OIDC Configuration - Provider setup
- Role Mappings - Group-to-role mapping
- Users - User management
