194 lines
8.7 KiB
Markdown
194 lines
8.7 KiB
Markdown
# Tanflow SSO User Data Mapping
|
|
|
|
This document outlines all user information available from Tanflow IAM Suite and how it maps to our User model for user creation.
|
|
|
|
## Tanflow Userinfo Endpoint Response
|
|
|
|
Tanflow uses **OpenID Connect (OIDC) standard claims** via the `/protocol/openid-connect/userinfo` endpoint. The following fields are available:
|
|
|
|
### Standard OIDC Claims (Available from Tanflow)
|
|
|
|
| Tanflow Field | OIDC Standard Claim | Type | Description | Currently Extracted |
|
|
|--------------|---------------------|------|--------------|-------------------|
|
|
| `sub` | `sub` | string | **REQUIRED** - Subject identifier (unique user ID) | ✅ Yes (as `oktaSub`) |
|
|
| `email` | `email` | string | Email address | ✅ Yes |
|
|
| `email_verified` | `email_verified` | boolean | Email verification status | ❌ No |
|
|
| `preferred_username` | `preferred_username` | string | Preferred username (fallback for email) | ✅ Yes (fallback) |
|
|
| `name` | `name` | string | Full display name | ✅ Yes (as `displayName`) |
|
|
| `given_name` | `given_name` | string | First name | ✅ Yes (as `firstName`) |
|
|
| `family_name` | `family_name` | string | Last name | ✅ Yes (as `lastName`) |
|
|
| `phone_number` | `phone_number` | string | Phone number | ✅ Yes (as `phone`) |
|
|
| `phone_number_verified` | `phone_number_verified` | boolean | Phone verification status | ❌ No |
|
|
| `address` | `address` | object | Address object (structured) | ❌ No |
|
|
| `locale` | `locale` | string | User locale (e.g., "en-US") | ❌ No |
|
|
| `picture` | `picture` | string | Profile picture URL | ❌ No |
|
|
| `website` | `website` | string | Website URL | ❌ No |
|
|
| `profile` | `profile` | string | Profile page URL | ❌ No |
|
|
| `birthdate` | `birthdate` | string | Date of birth | ❌ No |
|
|
| `gender` | `gender` | string | Gender | ❌ No |
|
|
| `zoneinfo` | `zoneinfo` | string | Timezone (e.g., "America/New_York") | ❌ No |
|
|
| `updated_at` | `updated_at` | number | Last update timestamp | ❌ No |
|
|
|
|
### Custom Tanflow Claims (May be available)
|
|
|
|
These are **custom claims** that Tanflow may include based on their configuration:
|
|
|
|
| Tanflow Field | Type | Description | Currently Extracted |
|
|
|--------------|------|-------------|-------------------|
|
|
| `employeeId` | string | Employee ID from HR system | ✅ Yes |
|
|
| `employee_id` | string | Alternative employee ID field | ✅ Yes (fallback) |
|
|
| `department` | string | Department/Division | ✅ Yes |
|
|
| `designation` | string | Job designation/position | ✅ Yes |
|
|
| `title` | string | Job title | ✅ Yes (as `designation` fallback) |
|
|
| `organization` | string | Organization name | ❌ No |
|
|
| `division` | string | Division name | ❌ No |
|
|
| `location` | string | Office location | ❌ No |
|
|
| `manager` | string | Manager name/email | ❌ No |
|
|
| `manager_id` | string | Manager employee ID | ❌ No |
|
|
| `cost_center` | string | Cost center code | ❌ No |
|
|
| `employee_type` | string | Employee type (Full-time, Contract, etc.) | ❌ No |
|
|
| `hire_date` | string | Date of hire | ❌ No |
|
|
| `office_location` | string | Office location | ❌ No |
|
|
| `country` | string | Country code | ❌ No |
|
|
| `city` | string | City name | ❌ No |
|
|
| `state` | string | State/Province | ❌ No |
|
|
| `postal_code` | string | Postal/ZIP code | ❌ No |
|
|
| `groups` | array | Group memberships | ❌ No |
|
|
| `roles` | array | User roles | ❌ No |
|
|
|
|
## Current Extraction Logic
|
|
|
|
**Location:** `Re_Backend/src/services/auth.service.ts` → `exchangeTanflowCodeForTokens()`
|
|
|
|
```typescript
|
|
const userData: SSOUserData = {
|
|
oktaSub: tanflowSub, // Reuse oktaSub field for Tanflow sub
|
|
email: tanflowUserInfo.email || tanflowUserInfo.preferred_username || '',
|
|
employeeId: tanflowUserInfo.employeeId || tanflowUserInfo.employee_id || undefined,
|
|
firstName: tanflowUserInfo.given_name || tanflowUserInfo.firstName || undefined,
|
|
lastName: tanflowUserInfo.family_name || tanflowUserInfo.lastName || undefined,
|
|
displayName: tanflowUserInfo.name || tanflowUserInfo.displayName || undefined,
|
|
department: tanflowUserInfo.department || undefined,
|
|
designation: tanflowUserInfo.title || tanflowUserInfo.designation || undefined,
|
|
phone: tanflowUserInfo.phone_number || tanflowUserInfo.phone || undefined,
|
|
};
|
|
```
|
|
|
|
## User Model Fields Mapping
|
|
|
|
**Location:** `Re_Backend/src/models/User.ts`
|
|
|
|
| User Model Field | Tanflow Source | Required | Notes |
|
|
|-----------------|----------------|----------|-------|
|
|
| `userId` | Auto-generated UUID | ✅ | Primary key |
|
|
| `oktaSub` | `sub` | ✅ | Unique identifier from Tanflow |
|
|
| `email` | `email` or `preferred_username` | ✅ | Primary identifier |
|
|
| `employeeId` | `employeeId` or `employee_id` | ❌ | Optional HR system ID |
|
|
| `firstName` | `given_name` or `firstName` | ❌ | Optional |
|
|
| `lastName` | `family_name` or `lastName` | ❌ | Optional |
|
|
| `displayName` | `name` or `displayName` | ❌ | Auto-generated if missing |
|
|
| `department` | `department` | ❌ | Optional |
|
|
| `designation` | `title` or `designation` | ❌ | Optional |
|
|
| `phone` | `phone_number` or `phone` | ❌ | Optional |
|
|
| `manager` | `manager` | ❌ | **NOT currently extracted** |
|
|
| `secondEmail` | N/A | ❌ | Not available from Tanflow |
|
|
| `jobTitle` | `title` | ❌ | **NOT currently extracted** |
|
|
| `employeeNumber` | N/A | ❌ | Not available from Tanflow |
|
|
| `postalAddress` | `address` (structured) | ❌ | **NOT currently extracted** |
|
|
| `mobilePhone` | N/A | ❌ | Not available from Tanflow |
|
|
| `adGroups` | `groups` | ❌ | **NOT currently extracted** |
|
|
| `location` | `address`, `city`, `state`, `country` | ❌ | **NOT currently extracted** |
|
|
| `role` | Default: 'USER' | ✅ | Default role assigned |
|
|
| `isActive` | Default: true | ✅ | Auto-set to true |
|
|
| `lastLogin` | Current timestamp | ✅ | Auto-set on login |
|
|
|
|
## Recommended Enhancements
|
|
|
|
### 1. Extract Additional Fields
|
|
|
|
Consider extracting these fields if available from Tanflow:
|
|
|
|
```typescript
|
|
// Enhanced extraction (to be implemented)
|
|
const userData: SSOUserData = {
|
|
// ... existing fields ...
|
|
|
|
// Additional fields
|
|
manager: tanflowUserInfo.manager || undefined,
|
|
jobTitle: tanflowUserInfo.title || tanflowUserInfo.designation || undefined,
|
|
postalAddress: tanflowUserInfo.address ? JSON.stringify(tanflowUserInfo.address) : undefined,
|
|
adGroups: Array.isArray(tanflowUserInfo.groups) ? tanflowUserInfo.groups : undefined,
|
|
|
|
// Location object
|
|
location: {
|
|
city: tanflowUserInfo.city || undefined,
|
|
state: tanflowUserInfo.state || undefined,
|
|
country: tanflowUserInfo.country || undefined,
|
|
office: tanflowUserInfo.office_location || undefined,
|
|
timezone: tanflowUserInfo.zoneinfo || undefined,
|
|
},
|
|
};
|
|
```
|
|
|
|
### 2. Log Available Fields
|
|
|
|
Add logging to see what Tanflow actually returns:
|
|
|
|
```typescript
|
|
logger.info('Tanflow userinfo response', {
|
|
availableFields: Object.keys(tanflowUserInfo),
|
|
hasEmail: !!tanflowUserInfo.email,
|
|
hasEmployeeId: !!(tanflowUserInfo.employeeId || tanflowUserInfo.employee_id),
|
|
hasDepartment: !!tanflowUserInfo.department,
|
|
hasManager: !!tanflowUserInfo.manager,
|
|
hasGroups: Array.isArray(tanflowUserInfo.groups),
|
|
groupsCount: Array.isArray(tanflowUserInfo.groups) ? tanflowUserInfo.groups.length : 0,
|
|
sampleData: {
|
|
sub: tanflowUserInfo.sub?.substring(0, 10) + '...',
|
|
email: tanflowUserInfo.email?.substring(0, 10) + '...',
|
|
name: tanflowUserInfo.name,
|
|
}
|
|
});
|
|
```
|
|
|
|
## User Creation Flow
|
|
|
|
1. **Token Exchange** → Get `access_token` from Tanflow
|
|
2. **Userinfo Call** → Call `/protocol/openid-connect/userinfo` with `access_token`
|
|
3. **Extract Data** → Map Tanflow fields to `SSOUserData` interface
|
|
4. **User Lookup** → Check if user exists by `email`
|
|
5. **Create/Update** → Create new user or update existing user
|
|
6. **Generate Tokens** → Generate JWT access/refresh tokens
|
|
|
|
## Testing Recommendations
|
|
|
|
1. **Test with Real Tanflow Account**
|
|
- Log actual userinfo response
|
|
- Document all available fields
|
|
- Verify field mappings
|
|
|
|
2. **Handle Missing Fields**
|
|
- Ensure graceful fallbacks
|
|
- Don't fail if optional fields are missing
|
|
- Log warnings for missing expected fields
|
|
|
|
3. **Validate Required Fields**
|
|
- `sub` (oktaSub) - REQUIRED
|
|
- `email` or `preferred_username` - REQUIRED
|
|
|
|
## Next Steps
|
|
|
|
1. ✅ **Current Implementation** - Basic OIDC claims extraction
|
|
2. 🔄 **Enhancement** - Extract additional custom claims (manager, groups, location)
|
|
3. 🔄 **Logging** - Add detailed logging of Tanflow response
|
|
4. 🔄 **Testing** - Test with real Tanflow account to see actual fields
|
|
5. 🔄 **Documentation** - Update this doc with actual Tanflow response structure
|
|
|
|
## Notes
|
|
|
|
- Tanflow uses **Keycloak** under the hood (based on URL structure)
|
|
- Keycloak supports custom user attributes that may be available
|
|
- Some fields may require specific realm/client configuration in Tanflow
|
|
- Contact Tanflow support to confirm available custom claims
|
|
|