mirror of
https://github.com/btouchard/ackify.git
synced 2026-02-09 23:38:30 -06:00
fix(oauth): add Microsoft Graph API field mapping support
Support Microsoft-specific user info fields: - email: check 'mail' and 'userPrincipalName' as fallbacks - name: check 'displayName' (camelCase) for Microsoft Graph API
This commit is contained in:
@@ -344,12 +344,21 @@ func (p *OAuthProvider) parseUserInfo(resp *http.Response) (*models.User, error)
|
||||
return nil, fmt.Errorf("missing user ID in response")
|
||||
}
|
||||
|
||||
if email, ok := rawUser["email"].(string); ok {
|
||||
// Check for email in various provider-specific fields
|
||||
// - "email": Standard OIDC claim (Google, GitHub, GitLab, etc.)
|
||||
// - "mail": Microsoft Graph API
|
||||
// - "userPrincipalName": Microsoft fallback (UPN format)
|
||||
if email, ok := rawUser["email"].(string); ok && email != "" {
|
||||
user.Email = email
|
||||
} else if mail, ok := rawUser["mail"].(string); ok && mail != "" {
|
||||
user.Email = mail
|
||||
} else if upn, ok := rawUser["userPrincipalName"].(string); ok && upn != "" {
|
||||
user.Email = upn
|
||||
} else {
|
||||
return nil, fmt.Errorf("missing email in user info response")
|
||||
return nil, fmt.Errorf("missing email in user info response (checked: email, mail, userPrincipalName)")
|
||||
}
|
||||
|
||||
// Extract display name from various provider-specific fields
|
||||
var name string
|
||||
if fullName, ok := rawUser["name"].(string); ok && fullName != "" {
|
||||
name = fullName
|
||||
@@ -359,10 +368,12 @@ func (p *OAuthProvider) parseUserInfo(resp *http.Response) (*models.User, error)
|
||||
} else {
|
||||
name = firstName
|
||||
}
|
||||
} else if displayName, ok := rawUser["displayName"].(string); ok && displayName != "" {
|
||||
name = displayName
|
||||
} else if cn, ok := rawUser["cn"].(string); ok && cn != "" {
|
||||
name = cn
|
||||
} else if displayName, ok := rawUser["display_name"].(string); ok && displayName != "" {
|
||||
name = displayName
|
||||
} else if displayNameSnake, ok := rawUser["display_name"].(string); ok && displayNameSnake != "" {
|
||||
name = displayNameSnake
|
||||
} else if preferredName, ok := rawUser["preferred_username"].(string); ok && preferredName != "" {
|
||||
name = preferredName
|
||||
}
|
||||
|
||||
@@ -272,6 +272,55 @@ func TestOAuthProvider_parseUserInfo(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Microsoft Graph API - mail field",
|
||||
responseObj: map[string]interface{}{
|
||||
"id": "microsoft-id-12345",
|
||||
"mail": "user@company.com",
|
||||
"displayName": "Microsoft User",
|
||||
"userPrincipalName": "user@company.onmicrosoft.com",
|
||||
},
|
||||
wantErr: false,
|
||||
checkUser: func(t *testing.T, user *models.User) {
|
||||
if user.Sub != "microsoft-id-12345" {
|
||||
t.Errorf("Sub = %v, expected microsoft-id-12345", user.Sub)
|
||||
}
|
||||
if user.Email != "user@company.com" {
|
||||
t.Errorf("Email = %v, expected user@company.com (from mail field)", user.Email)
|
||||
}
|
||||
if user.Name != "Microsoft User" {
|
||||
t.Errorf("Name = %v, expected Microsoft User (from displayName)", user.Name)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Microsoft Graph API - userPrincipalName fallback",
|
||||
responseObj: map[string]interface{}{
|
||||
"id": "microsoft-id-67890",
|
||||
"displayName": "UPN User",
|
||||
"userPrincipalName": "user@company.onmicrosoft.com",
|
||||
},
|
||||
wantErr: false,
|
||||
checkUser: func(t *testing.T, user *models.User) {
|
||||
if user.Email != "user@company.onmicrosoft.com" {
|
||||
t.Errorf("Email = %v, expected user@company.onmicrosoft.com (from userPrincipalName)", user.Email)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "email field takes priority over mail",
|
||||
responseObj: map[string]interface{}{
|
||||
"sub": "12345",
|
||||
"email": "primary@example.com",
|
||||
"mail": "secondary@example.com",
|
||||
},
|
||||
wantErr: false,
|
||||
checkUser: func(t *testing.T, user *models.User) {
|
||||
if user.Email != "primary@example.com" {
|
||||
t.Errorf("Email = %v, expected primary@example.com (email should take priority)", user.Email)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing email",
|
||||
responseObj: map[string]interface{}{
|
||||
|
||||
Reference in New Issue
Block a user