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
+58
View File
@@ -72,6 +72,64 @@ func (api *API) userDebugOIDC(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusOK, link.Claims)
}
// Returns the merged OIDC claims for the authenticated user.
//
// @Summary Get OIDC claims for the authenticated user
// @ID get-oidc-claims-for-the-authenticated-user
// @Security CoderSessionToken
// @Produce json
// @Tags Users
// @Success 200 {object} codersdk.OIDCClaimsResponse
// @Router /users/oidc-claims [get]
func (api *API) userOIDCClaims(rw http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
apiKey = httpmw.APIKey(r)
)
user, err := api.Database.GetUserByID(ctx, apiKey.UserID)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to get user.",
Detail: err.Error(),
})
return
}
if user.LoginType != database.LoginTypeOIDC {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "User is not an OIDC user.",
})
return
}
//nolint:gocritic // GetUserLinkByUserIDLoginType requires reading
// rbac.ResourceSystem. The endpoint is scoped to the authenticated
// user's own identity via apiKey, so this is safe.
link, err := api.Database.GetUserLinkByUserIDLoginType(
dbauthz.AsSystemRestricted(ctx),
database.GetUserLinkByUserIDLoginTypeParams{
UserID: user.ID,
LoginType: database.LoginTypeOIDC,
},
)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to get user link.",
Detail: err.Error(),
})
return
}
claims := link.Claims.MergedClaims
if claims == nil {
claims = map[string]interface{}{}
}
httpapi.Write(ctx, rw, http.StatusOK, codersdk.OIDCClaimsResponse{
Claims: claims,
})
}
// Returns whether the initial user has been created or not.
//
// @Summary Check initial user created