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 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 14:06:11 -07:00
parent 0d38bbae00
commit d6cc82755d
3 changed files with 14 additions and 12 deletions

View File

@@ -77,6 +77,7 @@ type PublicKey struct {
type TokenClaims struct { type TokenClaims struct {
Valid bool `json:"valid"` Valid bool `json:"valid"`
Sub string `json:"sub,omitempty"` Sub string `json:"sub,omitempty"`
Username string `json:"username,omitempty"`
Roles []string `json:"roles,omitempty"` Roles []string `json:"roles,omitempty"`
ExpiresAt string `json:"expires_at,omitempty"` ExpiresAt string `json:"expires_at,omitempty"`
} }

View File

@@ -451,6 +451,7 @@ type validateRequest struct {
type validateResponse struct { type validateResponse struct {
Subject string `json:"sub,omitempty"` Subject string `json:"sub,omitempty"`
Username string `json:"username,omitempty"`
ExpiresAt string `json:"expires_at,omitempty"` ExpiresAt string `json:"expires_at,omitempty"`
Roles []string `json:"roles,omitempty"` Roles []string `json:"roles,omitempty"`
Valid bool `json:"valid"` Valid bool `json:"valid"`
@@ -490,12 +491,16 @@ func (s *Server) handleTokenValidate(w http.ResponseWriter, r *http.Request) {
return return
} }
writeJSON(w, http.StatusOK, validateResponse{ resp := validateResponse{
Valid: true, Valid: true,
Subject: claims.Subject, Subject: claims.Subject,
Roles: claims.Roles, Roles: claims.Roles,
ExpiresAt: claims.ExpiresAt.Format("2006-01-02T15:04:05Z"), 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 { type issueTokenRequest struct {

View File

@@ -100,15 +100,13 @@ components:
format: date-time format: date-time
example: "2026-03-11T09:01:23Z" example: "2026-03-11T09:01:23Z"
actor_id: actor_id:
type: string type: [string, "null"]
format: uuid format: uuid
nullable: true
description: UUID of the account that performed the action. Null for bootstrap events. description: UUID of the account that performed the action. Null for bootstrap events.
example: 550e8400-e29b-41d4-a716-446655440000 example: 550e8400-e29b-41d4-a716-446655440000
target_id: target_id:
type: string type: [string, "null"]
format: uuid format: uuid
nullable: true
description: UUID of the affected account, if applicable. description: UUID of the affected account, if applicable.
ip_address: ip_address:
type: string type: string
@@ -207,22 +205,20 @@ components:
type: boolean type: boolean
example: true example: true
not_before: not_before:
type: string type: [string, "null"]
format: date-time format: date-time
nullable: true
description: | description: |
Earliest time the rule becomes active. NULL means no constraint Earliest time the rule becomes active. NULL means no constraint
(always active). Rules where `not_before > now()` are skipped (always active). Rules where `not_before > now()` are skipped
during evaluation. during evaluation.
example: "2026-04-01T00:00:00Z" example: "2026-04-01T00:00:00Z"
expires_at: expires_at:
type: string type: [string, "null"]
format: date-time format: date-time
nullable: true
description: | description: |
Time after which the rule is no longer active. NULL means no Time after which the rule is no longer active. NULL means no
constraint (never expires). Rules where `expires_at <= now()` are constraint (never expires). Rules where expires_at is in the past
skipped during evaluation. are skipped during evaluation.
example: "2026-06-01T00:00:00Z" example: "2026-06-01T00:00:00Z"
created_at: created_at:
type: string type: string