Skip to main content

UserInfo Endpoint

The UserInfo endpoint returns profile information about the authenticated user or agent. It is the standard OpenID Connect endpoint for reading the current profile claims (the name/value facts about the user, such as email) associated with an access token. Call it with the access token you received from the token endpoint; terms are explained in the glossary.

GET / POST /orgs/{orgId}/api/v1/oauth/userinfo

When to Use UserInfo​

The ID token contains user information captured at the moment of authentication. The UserInfo endpoint returns current profile data. Call it when you need to:

  • Display a user profile page.
  • Sync user data with your database.
  • Check current roles (only for clients that enable the roles claim).
  • Retrieve claims that aren't in the ID token.

Request​

Include the access token in the Authorization header. A user's token must carry the openid scope, even if it has no other scope; without it the endpoint returns 403 insufficient_scope.

curl -X GET https://app.lumoauth.dev/orgs/acme-corp/api/v1/oauth/userinfo \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response: User Identity​

For a user token granted openid profile email, from a client that enables the roles claim:

{
"sub": "12345",
"name": "John Smith",
"email": "john@example.com",
"email_verified": true,
"picture": "https://example.com/avatars/john.jpg",
"roles": ["ROLE_USER", "ROLE_EDITOR"],
"tenant": "acme-corp",
"updated_at": 1704063600
}

Response: Agent Identity​

For AI agents authenticated via client credentials or workload federation:

{
"sub": "agent_analyst_bot",
"name": "Financial Analyst Bot",
"agent_id": "agt_abc123",
"identity_type": "agent",
"capabilities": [
"read:reports",
"tool:search",
"write:analysis"
],
"workload_identity": "aws:sts:analyst-server",
"tenant": "acme-corp"
}

Response: Client Identity​

For a token issued to an OAuth client with the client credentials grant (no user, not an agent):

{
"sub": "client_abc123def456",
"client_id": "abc123def456",
"name": "Reporting service",
"tenant": "acme-corp",
"identity_type": "client"
}

Response Fields​

Standard OIDC Claims​

ClaimDescriptionScope Required
subUnique identifier for the user or agentopenid
nameFull nameprofile
emailEmail addressemail
email_verifiedWhether the email has been verifiedemail
pictureProfile picture URLprofile
given_name, family_name, preferred_username, locale, updated_at, …Other profile claimsprofile
phone_number, phone_number_verifiedPhonephone
addressPostal addressaddress

A token with only openid gets sub and tenant, nothing else.

LumoAuth-Specific Claims​

ClaimDescription
rolesArray of roles assigned to the user. Returned only when the token's client explicitly enables User roles under its JWT claims; a scope cannot request it
tenantOrganization slug the identity belongs to
identity_typeagent or client. Absent for user tokens
capabilitiesAgent-specific: allowed actions
workload_identityAgent-specific: external identity source

DPoP-bound tokens​

DPoP ties a token to a key your client holds, so a stolen copy cannot be replayed from another machine. If the access token was issued with a DPoP proof (token_type: "DPoP"), present it with the DPoP scheme and a fresh proof for this exact request. The proof must carry htm: "GET", htu set to the userinfo URL, and ath (the base64url SHA-256 of the access token).

curl https://app.lumoauth.dev/orgs/acme-corp/api/v1/oauth/userinfo \
-H "Authorization: DPoP YOUR_ACCESS_TOKEN" \
-H "DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Li4ufX0..."

Each proof is validated once per request (its jti is consumed), so a single proof is never reported as a replay against itself; sending the same proof twice is.

If the organization requires server nonces (settings.security.dpop_require_nonce), a proof without a fresh nonce is answered with:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: DPoP error="use_dpop_nonce", error_description="Resource server requires nonce in DPoP proof"
DPoP-Nonce: 5d2f0a9c8b7e6d4c3b2a1f0e9d8c7b6a
Content-Type: application/json

{"error": "use_dpop_nonce", "error_description": "Resource server requires a fresh nonce in the DPoP proof"}

Build a new proof with that value in nonce (new jti, fresh iat, same ath) and repeat the request. See Token formats.

Errors​

HTTPerrorCause
400invalid_requestNo Authorization header, or a scheme other than Bearer or DPoP
401invalid_tokenMissing, expired, revoked, or wrong-issuer token; a DPoP-bound token presented as Bearer; or the user has since been blocked, deactivated or deleted, signed out everywhere, or had their password reset (tokens issued before that are refused at once)
401invalid_dpop_proofProof malformed, expired, replayed, or its key does not match the token's cnf.jkt
401use_dpop_nonceThe organization requires a server nonce; retry with the DPoP-Nonce value
403insufficient_scopeThe token lacks openid (a user token with no scopes at all included)

Common mistakes​

  • Only sub comes back. The token was granted openid but not profile or email. Request those scopes; the client's JWT-claims settings cannot add claims the scopes don't cover.
  • roles is missing. Roles are opt-in per client. Turn on User roles under the application's JWT claims.
  • Token endpoint — obtain the access token needed to call UserInfo
  • Scopes — which claims are returned depends on the scopes granted
  • Token formats — DPoP overview

Standards used on this page​

Plain nameStandard
OpenID Connect (OIDC)OpenID Connect Core 1.0
Bearer tokensRFC 6750
Sender-constrained tokens (DPoP)RFC 9449

See the standards reference for what each one covers and how LumoAuth uses it.