Merge pull request #1196 from trheyi/main
Add Yao type information to user and token claims
This commit is contained in:
commit
51f3ed70e6
6 changed files with 142 additions and 8 deletions
|
|
@ -576,6 +576,9 @@ func (s *Service) SignIDToken(clientID, scope string, expiresIn int, userdata *t
|
||||||
if userdata.YaoIsOwner != nil {
|
if userdata.YaoIsOwner != nil {
|
||||||
claims["yao:is_owner"] = *userdata.YaoIsOwner
|
claims["yao:is_owner"] = *userdata.YaoIsOwner
|
||||||
}
|
}
|
||||||
|
if userdata.YaoTypeID != "" {
|
||||||
|
claims["yao:type_id"] = userdata.YaoTypeID
|
||||||
|
}
|
||||||
// Add Yao team info if present
|
// Add Yao team info if present
|
||||||
if userdata.YaoTeam != nil {
|
if userdata.YaoTeam != nil {
|
||||||
teamMap := make(map[string]interface{})
|
teamMap := make(map[string]interface{})
|
||||||
|
|
@ -601,6 +604,22 @@ func (s *Service) SignIDToken(clientID, scope string, expiresIn int, userdata *t
|
||||||
claims["yao:team"] = teamMap
|
claims["yao:team"] = teamMap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Add Yao type info if present
|
||||||
|
if userdata.YaoType != nil {
|
||||||
|
typeMap := make(map[string]interface{})
|
||||||
|
if userdata.YaoType.TypeID != "" {
|
||||||
|
typeMap["type_id"] = userdata.YaoType.TypeID
|
||||||
|
}
|
||||||
|
if userdata.YaoType.Name != "" {
|
||||||
|
typeMap["name"] = userdata.YaoType.Name
|
||||||
|
}
|
||||||
|
if userdata.YaoType.Locale != "" {
|
||||||
|
typeMap["locale"] = userdata.YaoType.Locale
|
||||||
|
}
|
||||||
|
if len(typeMap) > 0 {
|
||||||
|
claims["yao:type"] = typeMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add scope if provided (useful for determining which claims to include)
|
// Add scope if provided (useful for determining which claims to include)
|
||||||
if scope != "" {
|
if scope != "" {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,9 @@ func (user OIDCUserInfo) Map() map[string]interface{} {
|
||||||
if user.YaoIsOwner != nil {
|
if user.YaoIsOwner != nil {
|
||||||
result["yao:is_owner"] = user.YaoIsOwner
|
result["yao:is_owner"] = user.YaoIsOwner
|
||||||
}
|
}
|
||||||
|
if user.YaoTypeID != "" {
|
||||||
|
result["yao:type_id"] = user.YaoTypeID
|
||||||
|
}
|
||||||
|
|
||||||
// Add Yao team info if present and has content
|
// Add Yao team info if present and has content
|
||||||
if user.YaoTeam != nil {
|
if user.YaoTeam != nil {
|
||||||
|
|
@ -134,6 +137,23 @@ func (user OIDCUserInfo) Map() map[string]interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add Yao type info if present and has content
|
||||||
|
if user.YaoType != nil {
|
||||||
|
typeMap := make(map[string]interface{})
|
||||||
|
if user.YaoType.TypeID != "" {
|
||||||
|
typeMap["type_id"] = user.YaoType.TypeID
|
||||||
|
}
|
||||||
|
if user.YaoType.Name != "" {
|
||||||
|
typeMap["name"] = user.YaoType.Name
|
||||||
|
}
|
||||||
|
if user.YaoType.Locale != "" {
|
||||||
|
typeMap["locale"] = user.YaoType.Locale
|
||||||
|
}
|
||||||
|
if len(typeMap) > 0 {
|
||||||
|
result["yao:type"] = typeMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Include raw data if available
|
// Include raw data if available
|
||||||
// if user.Raw != nil {
|
// if user.Raw != nil {
|
||||||
// // Merge raw data, but let structured fields take precedence
|
// // Merge raw data, but let structured fields take precedence
|
||||||
|
|
@ -254,6 +274,9 @@ func MakeOIDCUserInfo(user map[string]interface{}) *OIDCUserInfo {
|
||||||
if isOwner, ok := user["yao:is_owner"].(bool); ok {
|
if isOwner, ok := user["yao:is_owner"].(bool); ok {
|
||||||
userInfo.YaoIsOwner = &isOwner
|
userInfo.YaoIsOwner = &isOwner
|
||||||
}
|
}
|
||||||
|
if typeID, ok := user["yao:type_id"].(string); ok {
|
||||||
|
userInfo.YaoTypeID = typeID
|
||||||
|
}
|
||||||
|
|
||||||
// Yao team info (nested object)
|
// Yao team info (nested object)
|
||||||
if teamData, ok := user["yao:team"].(map[string]interface{}); ok {
|
if teamData, ok := user["yao:team"].(map[string]interface{}); ok {
|
||||||
|
|
@ -283,6 +306,21 @@ func MakeOIDCUserInfo(user map[string]interface{}) *OIDCUserInfo {
|
||||||
userInfo.YaoTeam = team
|
userInfo.YaoTeam = team
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Yao type info (nested object)
|
||||||
|
if typeData, ok := user["yao:type"].(map[string]interface{}); ok {
|
||||||
|
typeInfo := &OIDCTypeInfo{}
|
||||||
|
if typeID, ok := typeData["type_id"].(string); ok {
|
||||||
|
typeInfo.TypeID = typeID
|
||||||
|
}
|
||||||
|
if name, ok := typeData["name"].(string); ok {
|
||||||
|
typeInfo.Name = name
|
||||||
|
}
|
||||||
|
if locale, ok := typeData["locale"].(string); ok {
|
||||||
|
typeInfo.Locale = locale
|
||||||
|
}
|
||||||
|
userInfo.YaoType = typeInfo
|
||||||
|
}
|
||||||
|
|
||||||
return userInfo
|
return userInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -694,6 +694,8 @@ type OIDCUserInfo struct {
|
||||||
YaoTeamID string `json:"yao:team_id,omitempty"` // Yao team ID
|
YaoTeamID string `json:"yao:team_id,omitempty"` // Yao team ID
|
||||||
YaoTeam *OIDCTeamInfo `json:"yao:team,omitempty"` // Yao team info
|
YaoTeam *OIDCTeamInfo `json:"yao:team,omitempty"` // Yao team info
|
||||||
YaoIsOwner *bool `json:"yao:is_owner,omitempty"` // Yao is owner
|
YaoIsOwner *bool `json:"yao:is_owner,omitempty"` // Yao is owner
|
||||||
|
YaoTypeID string `json:"yao:type_id,omitempty"` // Yao user type ID
|
||||||
|
YaoType *OIDCTypeInfo `json:"yao:type,omitempty"` // Yao user type info
|
||||||
|
|
||||||
// Raw response for debugging and custom processing
|
// Raw response for debugging and custom processing
|
||||||
Raw map[string]interface{} `json:"raw,omitempty"` // Original provider response
|
Raw map[string]interface{} `json:"raw,omitempty"` // Original provider response
|
||||||
|
|
@ -709,6 +711,13 @@ type OIDCTeamInfo struct {
|
||||||
UpdatedAt *int64 `json:"updated_at,omitempty"` // Team updated at (seconds since epoch)
|
UpdatedAt *int64 `json:"updated_at,omitempty"` // Team updated at (seconds since epoch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OIDCTypeInfo represents user type information based on OIDC standard
|
||||||
|
type OIDCTypeInfo struct {
|
||||||
|
TypeID string `json:"type_id,omitempty"` // User type identifier
|
||||||
|
Name string `json:"name,omitempty"` // User type name
|
||||||
|
Locale string `json:"locale,omitempty"` // User type locale
|
||||||
|
}
|
||||||
|
|
||||||
// OIDCAddress represents the OIDC address claim structure
|
// OIDCAddress represents the OIDC address claim structure
|
||||||
type OIDCAddress struct {
|
type OIDCAddress struct {
|
||||||
Formatted string `json:"formatted,omitempty"` // Full mailing address
|
Formatted string `json:"formatted,omitempty"` // Full mailing address
|
||||||
|
|
|
||||||
|
|
@ -300,12 +300,12 @@ func issueTokens(ctx context.Context, userid string, teamID string, team map[str
|
||||||
oidcUserInfo := oauthtypes.MakeOIDCUserInfo(user)
|
oidcUserInfo := oauthtypes.MakeOIDCUserInfo(user)
|
||||||
oidcUserInfo.Sub = subject
|
oidcUserInfo.Sub = subject
|
||||||
|
|
||||||
// Prepare extra claims for team context
|
// Prepare extra claims for access token
|
||||||
var extraClaims map[string]interface{}
|
extraClaims := make(map[string]interface{})
|
||||||
|
|
||||||
|
// Add team context if available
|
||||||
if teamID != "" && team != nil {
|
if teamID != "" && team != nil {
|
||||||
extraClaims = map[string]interface{}{
|
extraClaims["team_id"] = teamID
|
||||||
"team_id": teamID,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add tenant_id if available from the team
|
// Add tenant_id if available from the team
|
||||||
if tenantID := toString(team["tenant_id"]); tenantID != "" {
|
if tenantID := toString(team["tenant_id"]); tenantID != "" {
|
||||||
|
|
@ -344,10 +344,46 @@ func issueTokens(ctx context.Context, userid string, teamID string, team map[str
|
||||||
oidcUserInfo.YaoTeam = teamInfo
|
oidcUserInfo.YaoTeam = teamInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add type information (use team type if in team context, otherwise use user type)
|
||||||
|
var typeID string
|
||||||
|
if teamID != "" && team != nil {
|
||||||
|
// Team context - use team's type
|
||||||
|
typeID = toString(team["type_id"])
|
||||||
|
} else {
|
||||||
|
// Personal context - use user's type
|
||||||
|
typeID = toString(user["type_id"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if typeID != "" {
|
||||||
|
// Add type_id to extra claims for access token
|
||||||
|
extraClaims["type_id"] = typeID
|
||||||
|
oidcUserInfo.YaoTypeID = typeID
|
||||||
|
|
||||||
|
// Get type details
|
||||||
|
userProvider, err := oauth.OAuth.GetUserProvider()
|
||||||
|
if err == nil {
|
||||||
|
typeInfo, err := userProvider.GetType(ctx, typeID)
|
||||||
|
if err == nil && typeInfo != nil {
|
||||||
|
// Add type info to OIDC user info
|
||||||
|
typeDetails := &oauthtypes.OIDCTypeInfo{}
|
||||||
|
if typeIDVal := toString(typeInfo["type_id"]); typeIDVal != "" {
|
||||||
|
typeDetails.TypeID = typeIDVal
|
||||||
|
}
|
||||||
|
if name := toString(typeInfo["name"]); name != "" {
|
||||||
|
typeDetails.Name = name
|
||||||
|
}
|
||||||
|
if locale := toString(typeInfo["locale"]); locale != "" {
|
||||||
|
typeDetails.Locale = locale
|
||||||
|
}
|
||||||
|
oidcUserInfo.YaoType = typeDetails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sign OIDC Token
|
// Sign OIDC Token
|
||||||
var oidcToken string
|
var oidcToken string
|
||||||
var err error
|
var err error
|
||||||
if extraClaims != nil {
|
if len(extraClaims) > 0 {
|
||||||
oidcToken, err = oauth.OAuth.SignIDToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), yaoClientConfig.ExpiresIn, oidcUserInfo, extraClaims)
|
oidcToken, err = oauth.OAuth.SignIDToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), yaoClientConfig.ExpiresIn, oidcUserInfo, extraClaims)
|
||||||
} else {
|
} else {
|
||||||
oidcToken, err = oauth.OAuth.SignIDToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), yaoClientConfig.ExpiresIn, oidcUserInfo)
|
oidcToken, err = oauth.OAuth.SignIDToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), yaoClientConfig.ExpiresIn, oidcUserInfo)
|
||||||
|
|
@ -358,7 +394,7 @@ func issueTokens(ctx context.Context, userid string, teamID string, team map[str
|
||||||
|
|
||||||
// Sign Access Token
|
// Sign Access Token
|
||||||
var accessToken string
|
var accessToken string
|
||||||
if extraClaims != nil {
|
if len(extraClaims) > 0 {
|
||||||
accessToken, err = oauth.OAuth.MakeAccessToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.ExpiresIn, extraClaims)
|
accessToken, err = oauth.OAuth.MakeAccessToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.ExpiresIn, extraClaims)
|
||||||
} else {
|
} else {
|
||||||
accessToken, err = oauth.OAuth.MakeAccessToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.ExpiresIn)
|
accessToken, err = oauth.OAuth.MakeAccessToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.ExpiresIn)
|
||||||
|
|
@ -369,7 +405,7 @@ func issueTokens(ctx context.Context, userid string, teamID string, team map[str
|
||||||
|
|
||||||
// Sign Refresh Token
|
// Sign Refresh Token
|
||||||
var refreshToken string
|
var refreshToken string
|
||||||
if extraClaims != nil {
|
if len(extraClaims) > 0 {
|
||||||
refreshToken, err = oauth.OAuth.MakeRefreshToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.RefreshTokenExpiresIn, extraClaims)
|
refreshToken, err = oauth.OAuth.MakeRefreshToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.RefreshTokenExpiresIn, extraClaims)
|
||||||
} else {
|
} else {
|
||||||
refreshToken, err = oauth.OAuth.MakeRefreshToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.RefreshTokenExpiresIn)
|
refreshToken, err = oauth.OAuth.MakeRefreshToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.RefreshTokenExpiresIn)
|
||||||
|
|
|
||||||
|
|
@ -689,6 +689,37 @@ func teamCreate(ctx context.Context, userID string, teamData maps.MapStrAny) (st
|
||||||
teamData["created_at"] = time.Now()
|
teamData["created_at"] = time.Now()
|
||||||
teamData["updated_at"] = time.Now()
|
teamData["updated_at"] = time.Now()
|
||||||
|
|
||||||
|
// Set default type_id from team config if not provided
|
||||||
|
if _, hasType := teamData["type_id"]; !hasType {
|
||||||
|
// Try to get locale from team data
|
||||||
|
locale := ""
|
||||||
|
if localeVal, ok := teamData["locale"].(string); ok && localeVal != "" {
|
||||||
|
locale = strings.TrimSpace(strings.ToLower(localeVal))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: try common locale variations or use "en" as final fallback
|
||||||
|
// This ensures we always get a valid config even if locale is invalid
|
||||||
|
teamConfig := GetTeamConfig(locale)
|
||||||
|
if teamConfig == nil {
|
||||||
|
// Try fallback locales in order
|
||||||
|
fallbackLocales := []string{"en", "zh-cn"}
|
||||||
|
for _, fallback := range fallbackLocales {
|
||||||
|
teamConfig = GetTeamConfig(fallback)
|
||||||
|
if teamConfig != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply default type from config if available
|
||||||
|
if teamConfig != nil && teamConfig.Type != "" {
|
||||||
|
teamData["type_id"] = teamConfig.Type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up: remove locale from team data as it's not stored in database
|
||||||
|
delete(teamData, "locale")
|
||||||
|
|
||||||
// Create team
|
// Create team
|
||||||
teamID, err := provider.CreateTeam(ctx, teamData)
|
teamID, err := provider.CreateTeam(ctx, teamData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -415,6 +415,7 @@ type CreateInvitationRequest struct {
|
||||||
type TeamConfig struct {
|
type TeamConfig struct {
|
||||||
Roles []*TeamRole `json:"roles,omitempty"`
|
Roles []*TeamRole `json:"roles,omitempty"`
|
||||||
Invite *InviteConfig `json:"invite,omitempty"`
|
Invite *InviteConfig `json:"invite,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"` // Default type for new teams
|
||||||
}
|
}
|
||||||
|
|
||||||
// TeamRole represents a team role configuration
|
// TeamRole represents a team role configuration
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue