SeloraXDEVELOPERS

UserInfo

UserInfo Endpoint

After obtaining an access token via token exchange, use it to retrieve the authenticated user's profile data. The claims returned are filtered based on the scopes granted to the token.

Two authentication methods are supported:

MethodHTTPUse case
Bearer tokenGET /api/oauth/userinfoBrowser-side, standard OIDC
Client credentialsPOST /api/oauth/userinfoServer-to-server

Method 1: Bearer Token (GET)

Include the access token in the Authorization header:

GET /api/oauth/userinfo
Authorization: Bearer sx_it_...

Example Request

curl -X GET https://api.selorax.io/api/oauth/userinfo \
  -H "Authorization: Bearer sx_it_..."

Response: Customer

A customer with scopes openid profile email phone store:

{
  "sub": "customer:42",
  "name": "John Doe",
  "picture": "https://r2.selorax.io/avatars/user_42.jpg",
  "email": "[email protected]",
  "email_verified": true,
  "phone_number": "+8801712345678",
  "phone_number_verified": true,
  "store_id": 22,
  "store_name": "Florist BD",
  "role": "customer"
}

Response: Merchant

A merchant with scopes openid profile email store:

{
  "sub": "merchant:7",
  "name": "Jane Admin",
  "picture": null,
  "email": "[email protected]",
  "email_verified": true,
  "store_id": 22,
  "store_name": "Florist BD",
  "role": "admin"
}

Method 2: Client Credentials (POST)

For server-to-server token validation, your backend can send the client_id, client_secret, and access_token directly — no Authorization header needed. This is similar to Google's tokeninfo pattern.

POST /api/oauth/userinfo
Content-Type: application/json

Request Body

{
  "client_id": "sx_oc_...",
  "client_secret": "sx_os_...",
  "access_token": "sx_it_..."
}
FieldRequiredDescription
client_idYesYour OAuth client ID
client_secretYesYour OAuth client secret
access_tokenYesThe user's access token to look up

Example Request

curl -X POST https://api.selorax.io/api/oauth/userinfo \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "sx_oc_a1b2c3...",
    "client_secret": "sx_os_d4e5f6...",
    "access_token": "sx_it_g7h8i9..."
  }'

Security

  • The access token must belong to your client. If the token was issued to a different client, the request is rejected with 403 token_mismatch.
  • Both client_id and client_secret are validated before the token is resolved.

Claims by Scope

The response only includes claims for scopes that were granted to the access token:

ScopeClaimsDescription
openidsubUser identifier. Format: {user_type}:{user_id} (e.g. customer:42, merchant:7)
profilename, pictureDisplay name and avatar URL. picture may be null.
emailemail, email_verifiedEmail address and whether it's been verified.
phonephone_number, phone_number_verifiedPhone number (E.164 format) and verification status. For merchants, phone_number_verified is always true. For customers, it reflects the actual verification status from the database.
storestore_id, store_name, roleStore context. For merchants, role comes from store_admins_link. For customers, role is always "customer".

If a scope was not granted, its claims are omitted from the response entirely.

Merchant Store Context

Merchants may manage multiple stores. The store scope returns the store context that was specified during authorization:

  • If store_id was provided in the authorize request → that store's data is returned
  • The platform validates the merchant has access to the specified store via store_admins_link

Example: Full Integration (Node.js)

Browser-side (Bearer Token)

// After token exchange, fetch user profile from the browser
async function getSeloraXUser(accessToken) {
  const res = await fetch('https://api.selorax.io/api/oauth/userinfo', {
    headers: { 'Authorization': `Bearer ${accessToken}` },
  });
 
  if (!res.ok) {
    throw new Error(`UserInfo failed: ${res.status}`);
  }
 
  return await res.json();
}

Server-side (Client Credentials)

// Validate a user's access token from your backend
async function validateSeloraXToken(accessToken) {
  const res = await fetch('https://api.selorax.io/api/oauth/userinfo', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id: process.env.SELORAX_CLIENT_ID,
      client_secret: process.env.SELORAX_CLIENT_SECRET,
      access_token: accessToken,
    }),
  });
 
  if (!res.ok) {
    throw new Error(`Token validation failed: ${res.status}`);
  }
 
  const user = await res.json();
 
  return {
    id: user.sub,           // "customer:42"
    name: user.name,         // "John Doe"
    email: user.email,       // "[email protected]"
    avatar: user.picture,    // URL or null
    store: user.store_name,  // "Florist BD"
  };
}

:::tip Use the POST method from your backend to validate tokens server-to-server. This avoids exposing your access token in browser-side requests and lets your server verify the token belongs to your client. :::

Error Responses

GET (Bearer Token)

StatusCodeDescription
401missing_tokenNo Authorization: Bearer header provided
401invalid_tokenToken is expired, revoked, or malformed
404User not found in the database
500auth_errorInternal error during token resolution

POST (Client Credentials)

StatusCodeDescription
400invalid_requestMissing client_id, client_secret, or access_token
401invalid_clientClient not found or wrong client_secret
401invalid_tokenToken is expired, revoked, or has invalid format
403token_mismatchAccess token was issued to a different client
404User not found in the database

Example

{
  "message": "Invalid or expired access token.",
  "code": "invalid_token",
  "status": 401
}

Caching

Access tokens are cached in Redis for 5 minutes (keyed by 16-character prefix). Repeated UserInfo calls within the cache window are fast — no database query is needed for token resolution.