From d6cc82755dae4df7038c682b01f41256ed9402ca Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Sun, 15 Mar 2026 14:06:11 -0700 Subject: [PATCH] Add username to token validate response - Include username field in validateResponse struct - Look up account by UUID and populate username on success - Add username field to Go client TokenClaims struct - Fix OpenAPI nullable type syntax (use array form) Co-Authored-By: Claude Sonnet 4.6 --- clients/go/client.go | 1 + internal/server/server.go | 9 +++++++-- openapi.yaml | 16 ++++++---------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/clients/go/client.go b/clients/go/client.go index 36d71f0..432ea87 100644 --- a/clients/go/client.go +++ b/clients/go/client.go @@ -77,6 +77,7 @@ type PublicKey struct { type TokenClaims struct { Valid bool `json:"valid"` Sub string `json:"sub,omitempty"` + Username string `json:"username,omitempty"` Roles []string `json:"roles,omitempty"` ExpiresAt string `json:"expires_at,omitempty"` } diff --git a/internal/server/server.go b/internal/server/server.go index cf77cba..2404c84 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -451,6 +451,7 @@ type validateRequest struct { type validateResponse struct { Subject string `json:"sub,omitempty"` + Username string `json:"username,omitempty"` ExpiresAt string `json:"expires_at,omitempty"` Roles []string `json:"roles,omitempty"` Valid bool `json:"valid"` @@ -490,12 +491,16 @@ func (s *Server) handleTokenValidate(w http.ResponseWriter, r *http.Request) { return } - writeJSON(w, http.StatusOK, validateResponse{ + resp := validateResponse{ Valid: true, Subject: claims.Subject, Roles: claims.Roles, ExpiresAt: claims.ExpiresAt.Format("2006-01-02T15:04:05Z"), - }) + } + if acct, err := s.db.GetAccountByUUID(claims.Subject); err == nil { + resp.Username = acct.Username + } + writeJSON(w, http.StatusOK, resp) } type issueTokenRequest struct { diff --git a/openapi.yaml b/openapi.yaml index 2d6dbce..b9611ba 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -100,15 +100,13 @@ components: format: date-time example: "2026-03-11T09:01:23Z" actor_id: - type: string + type: [string, "null"] format: uuid - nullable: true description: UUID of the account that performed the action. Null for bootstrap events. example: 550e8400-e29b-41d4-a716-446655440000 target_id: - type: string + type: [string, "null"] format: uuid - nullable: true description: UUID of the affected account, if applicable. ip_address: type: string @@ -207,22 +205,20 @@ components: type: boolean example: true not_before: - type: string + type: [string, "null"] format: date-time - nullable: true description: | Earliest time the rule becomes active. NULL means no constraint (always active). Rules where `not_before > now()` are skipped during evaluation. example: "2026-04-01T00:00:00Z" expires_at: - type: string + type: [string, "null"] format: date-time - nullable: true description: | Time after which the rule is no longer active. NULL means no - constraint (never expires). Rules where `expires_at <= now()` are - skipped during evaluation. + constraint (never expires). Rules where expires_at is in the past + are skipped during evaluation. example: "2026-06-01T00:00:00Z" created_at: type: string