diff --git a/internal/api/handlers/management/auth_files.go b/internal/api/handlers/management/auth_files.go index 41a4fde4..d98c759c 100644 --- a/internal/api/handlers/management/auth_files.go +++ b/internal/api/handlers/management/auth_files.go @@ -427,9 +427,59 @@ func (h *Handler) buildAuthFileEntry(auth *coreauth.Auth) gin.H { log.WithError(err).Warnf("failed to stat auth file %s", path) } } + if claims := extractCodexIDTokenClaims(auth); claims != nil { + entry["id_token"] = claims + } return entry } +func extractCodexIDTokenClaims(auth *coreauth.Auth) gin.H { + if auth == nil || auth.Metadata == nil { + return nil + } + if !strings.EqualFold(strings.TrimSpace(auth.Provider), "codex") { + return nil + } + idTokenRaw, ok := auth.Metadata["id_token"].(string) + if !ok { + return nil + } + idToken := strings.TrimSpace(idTokenRaw) + if idToken == "" { + return nil + } + claims, err := codex.ParseJWTToken(idToken) + if err != nil || claims == nil { + return nil + } + + result := gin.H{} + if v := strings.TrimSpace(claims.CodexAuthInfo.ChatgptPlanType); v != "" { + result["plan_type"] = v + } + if v := strings.TrimSpace(claims.CodexAuthInfo.UserID); v != "" { + result["user_id"] = v + } + + if len(claims.CodexAuthInfo.Organizations) > 0 { + orgs := make([]gin.H, 0, len(claims.CodexAuthInfo.Organizations)) + for _, org := range claims.CodexAuthInfo.Organizations { + orgs = append(orgs, gin.H{ + "id": strings.TrimSpace(org.ID), + "title": strings.TrimSpace(org.Title), + "role": strings.TrimSpace(org.Role), + "is_default": org.IsDefault, + }) + } + result["organizations"] = orgs + } + + if len(result) == 0 { + return nil + } + return result +} + func authEmail(auth *coreauth.Auth) string { if auth == nil { return ""