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 {
|
||||
claims["yao:is_owner"] = *userdata.YaoIsOwner
|
||||
}
|
||||
if userdata.YaoTypeID != "" {
|
||||
claims["yao:type_id"] = userdata.YaoTypeID
|
||||
}
|
||||
// Add Yao team info if present
|
||||
if userdata.YaoTeam != nil {
|
||||
teamMap := make(map[string]interface{})
|
||||
|
|
@ -601,6 +604,22 @@ func (s *Service) SignIDToken(clientID, scope string, expiresIn int, userdata *t
|
|||
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)
|
||||
if scope != "" {
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@ func (user OIDCUserInfo) Map() map[string]interface{} {
|
|||
if user.YaoIsOwner != nil {
|
||||
result["yao:is_owner"] = user.YaoIsOwner
|
||||
}
|
||||
if user.YaoTypeID != "" {
|
||||
result["yao:type_id"] = user.YaoTypeID
|
||||
}
|
||||
|
||||
// Add Yao team info if present and has content
|
||||
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
|
||||
// if user.Raw != nil {
|
||||
// // 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 {
|
||||
userInfo.YaoIsOwner = &isOwner
|
||||
}
|
||||
if typeID, ok := user["yao:type_id"].(string); ok {
|
||||
userInfo.YaoTypeID = typeID
|
||||
}
|
||||
|
||||
// Yao team info (nested object)
|
||||
if teamData, ok := user["yao:team"].(map[string]interface{}); ok {
|
||||
|
|
@ -283,6 +306,21 @@ func MakeOIDCUserInfo(user map[string]interface{}) *OIDCUserInfo {
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -694,6 +694,8 @@ type OIDCUserInfo struct {
|
|||
YaoTeamID string `json:"yao:team_id,omitempty"` // Yao team ID
|
||||
YaoTeam *OIDCTeamInfo `json:"yao:team,omitempty"` // Yao team info
|
||||
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 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
type OIDCAddress struct {
|
||||
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.Sub = subject
|
||||
|
||||
// Prepare extra claims for team context
|
||||
var extraClaims map[string]interface{}
|
||||
// Prepare extra claims for access token
|
||||
extraClaims := make(map[string]interface{})
|
||||
|
||||
// Add team context if available
|
||||
if teamID != "" && team != nil {
|
||||
extraClaims = map[string]interface{}{
|
||||
"team_id": teamID,
|
||||
}
|
||||
extraClaims["team_id"] = teamID
|
||||
|
||||
// Add tenant_id if available from the team
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
var oidcToken string
|
||||
var err error
|
||||
if extraClaims != nil {
|
||||
if len(extraClaims) > 0 {
|
||||
oidcToken, err = oauth.OAuth.SignIDToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), yaoClientConfig.ExpiresIn, oidcUserInfo, extraClaims)
|
||||
} else {
|
||||
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
|
||||
var accessToken string
|
||||
if extraClaims != nil {
|
||||
if len(extraClaims) > 0 {
|
||||
accessToken, err = oauth.OAuth.MakeAccessToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.ExpiresIn, extraClaims)
|
||||
} else {
|
||||
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
|
||||
var refreshToken string
|
||||
if extraClaims != nil {
|
||||
if len(extraClaims) > 0 {
|
||||
refreshToken, err = oauth.OAuth.MakeRefreshToken(yaoClientConfig.ClientID, strings.Join(scopes, " "), subject, yaoClientConfig.RefreshTokenExpiresIn, extraClaims)
|
||||
} else {
|
||||
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["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
|
||||
teamID, err := provider.CreateTeam(ctx, teamData)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -415,6 +415,7 @@ type CreateInvitationRequest struct {
|
|||
type TeamConfig struct {
|
||||
Roles []*TeamRole `json:"roles,omitempty"`
|
||||
Invite *InviteConfig `json:"invite,omitempty"`
|
||||
Type string `json:"type,omitempty"` // Default type for new teams
|
||||
}
|
||||
|
||||
// TeamRole represents a team role configuration
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue