feat: add endpoint and CLI for users to view their own OIDC claims (#23053)

- Adds a new API endpoint `GET /api/v2/users/oidc-claims` that returns
only the **merged claims** (not the separate id_token/userinfo
breakdown). Scoped exclusively to the authenticated user's own identity
— no user parameter, so users cannot view each other's claims.
- Adds a new CLI command:** `coder users oidc-claims` that hits the
above endpoint.
- The existing owner-only debug endpoint is preserved unchanged for
admins who need the full claim breakdown.


> 🤖 This PR was created with the help of Coder Agents, and will be
reviewed by my human. 🧑‍💻
This commit is contained in:
Cian Johnston
2026-03-18 22:10:04 +00:00
committed by GitHub
parent a6856320f9
commit be1c06dec9
15 changed files with 524 additions and 19 deletions
+22
View File
@@ -339,6 +339,14 @@ type OIDCAuthMethod struct {
IconURL string `json:"iconUrl"`
}
// OIDCClaimsResponse represents the merged OIDC claims for a user.
type OIDCClaimsResponse struct {
// Claims are the merged claims from the OIDC provider. These
// are the union of the ID token claims and the userinfo claims,
// where userinfo claims take precedence on conflict.
Claims map[string]interface{} `json:"claims"`
}
type UserParameter struct {
Name string `json:"name"`
Value string `json:"value"`
@@ -723,6 +731,20 @@ func (c *Client) UserRoles(ctx context.Context, user string) (UserRoles, error)
return roles, json.NewDecoder(res.Body).Decode(&roles)
}
// UserOIDCClaims returns the merged OIDC claims for the authenticated user.
func (c *Client) UserOIDCClaims(ctx context.Context) (OIDCClaimsResponse, error) {
res, err := c.Request(ctx, http.MethodGet, "/api/v2/users/oidc-claims", nil)
if err != nil {
return OIDCClaimsResponse{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return OIDCClaimsResponse{}, ReadBodyAsError(res)
}
var resp OIDCClaimsResponse
return resp, json.NewDecoder(res.Body).Decode(&resp)
}
// LoginWithPassword creates a session token authenticating with an email and password.
// Call `SetSessionToken()` to apply the newly acquired token to the client.
func (c *Client) LoginWithPassword(ctx context.Context, req LoginWithPasswordRequest) (LoginWithPasswordResponse, error) {