Re_Backend/docs/TANFLOW_USER_DATA_MAPPING.md

9.4 KiB

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 No
designation string Job designation/position Yes (as designation)
employeeType string Employee type (Dealer, Full-time, Contract, etc.) Yes (as jobTitle)
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
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.tsexchangeTanflowCodeForTokens()

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.designation || undefined, // Map designation to designation
  phone: tanflowUserInfo.phone_number || tanflowUserInfo.phone || undefined,
  // Additional fields
  manager: tanflowUserInfo.manager || undefined,
  jobTitle: tanflowUserInfo.employeeType || undefined, // Map employeeType to jobTitle
  postalAddress: tanflowUserInfo.address ? (typeof tanflowUserInfo.address === 'string' ? tanflowUserInfo.address : JSON.stringify(tanflowUserInfo.address)) : undefined,
  mobilePhone: tanflowUserInfo.mobile_phone || tanflowUserInfo.mobilePhone || undefined,
  adGroups: Array.isArray(tanflowUserInfo.groups) ? tanflowUserInfo.groups : 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 designation Optional
phone phone_number or phone Optional
manager manager Optional (extracted if available)
secondEmail N/A Not available from Tanflow
jobTitle employeeType Optional (maps employeeType to jobTitle)
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

1. Extract Additional Fields

Consider extracting these fields if available from Tanflow:

// Enhanced extraction (to be implemented)
const userData: SSOUserData = {
  // ... existing fields ...
  
  // Additional fields (already implemented)
  manager: tanflowUserInfo.manager || undefined,
  jobTitle: tanflowUserInfo.employeeType || undefined, // Map employeeType to jobTitle
  postalAddress: tanflowUserInfo.address ? (typeof tanflowUserInfo.address === 'string' ? tanflowUserInfo.address : JSON.stringify(tanflowUserInfo.address)) : undefined,
  mobilePhone: tanflowUserInfo.mobile_phone || tanflowUserInfo.mobilePhone || 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:

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