SeloraXDEVELOPERS

Authorization Flow

Authorization Flow

The authorization flow redirects a user to SeloraX where they log in and grant your application permission to access their profile data. On approval, SeloraX redirects back to your website with a short-lived authorization code.

Step 1: Redirect to Authorize

Redirect the user's browser to the SeloraX authorization endpoint:

GET /api/oauth/authorize?response_type=code&client_id=sx_oc_...&redirect_uri=https://example.com/callback&scope=openid+profile+email&state=random_string

Query Parameters

ParameterRequiredDescription
response_typeYesMust be code
client_idYesYour client ID (format: sx_oc_...)
redirect_uriYesMust exactly match one of your registered redirect_uris
scopeYesSpace-separated list of scopes (e.g. openid profile email)
stateRecommendedRandom string to prevent CSRF. Verify this on callback.
code_challengeConditionalRequired for public clients. PKCE challenge.
code_challenge_methodConditionalS256 (recommended) or plain
nonceOptionalRandom string bound to the session for replay protection
store_idOptionalFor merchants with multiple stores, specify which store context to use

:::tip Always include state to protect against CSRF attacks. Generate a cryptographically random string, store it in the user's session, and verify it matches when the callback is received. :::

Example: Building the Authorization URL

const params = new URLSearchParams({
  response_type: 'code',
  client_id: 'sx_oc_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6',
  redirect_uri: 'https://example.com/auth/callback',
  scope: 'openid profile email',
  state: crypto.randomUUID(),
});
 
const authorizeUrl = `https://api.selorax.io/api/oauth/authorize?${params}`;
// Redirect user to authorizeUrl

The user must be logged into SeloraX. The platform determines their user type automatically:

  • Customers (req.user.isAdmin === false) → identified as customer
  • Merchants (req.user.isAdmin === true) → identified as merchant

If the user already approved your app with the same (or broader) scopes, SeloraX skips the consent screen and returns the authorization code immediately:

{
  "redirect_url": "https://example.com/callback?code=sx_ic_...&state=random_string",
  "status": 200
}

SeloraX returns consent screen data for the dashboard to render:

{
  "consent_required": true,
  "client": {
    "name": "My Website",
    "logo_url": "https://example.com/logo.png",
    "homepage_url": "https://example.com",
    "description": "E-commerce analytics dashboard"
  },
  "requested_scopes": [
    { "code": "openid", "name": "OpenID", "description": "Verify your identity" },
    { "code": "profile", "name": "Profile", "description": "Access your name and avatar" },
    { "code": "email", "name": "Email", "description": "Access your email address" }
  ],
  "user": {
    "name": "John Doe",
    "user_type": "customer"
  },
  "status": 200
}

After the user makes their choice, submit the decision:

POST /api/oauth/authorize/consent

Request Body

{
  "client_id": "sx_oc_...",
  "redirect_uri": "https://example.com/auth/callback",
  "scope": "openid profile email",
  "state": "random_string",
  "approved": true,
  "code_challenge": null,
  "code_challenge_method": null,
  "nonce": null,
  "store_id": null
}
FieldTypeRequiredDescription
client_idstringYesYour client ID
redirect_uristringYesMust match the original authorize request
scopestringYesSpace-separated scopes
statestringNoPassed through to the redirect URL
approvedbooleanYestrue to grant, false to deny
code_challengestringNoPKCE challenge (if using PKCE)
code_challenge_methodstringNoS256 or plain
noncestringNoOIDC nonce
store_idintegerNoStore context for merchants

If approved

Consent is saved (so the user won't be asked again for the same scopes), and an authorization code is generated:

{
  "redirect_url": "https://example.com/callback?code=sx_ic_...&state=random_string",
  "status": 200
}

If declined

{
  "redirect_url": "https://example.com/callback?error=access_denied&state=random_string",
  "status": 200
}

Step 4: Receive the Callback

Your callback endpoint receives the authorization code as a query parameter:

GET https://example.com/auth/callback?code=sx_ic_...&state=random_string
  1. Verify state matches what you stored in the user's session
  2. If error is present, handle the denial (e.g. error=access_denied)
  3. Exchange the code for tokens

:::warning Authorization codes expire after 60 seconds. Exchange them immediately. :::

Once a user approves your application for a set of scopes, their consent is stored. On subsequent visits:

  • If the requested scopes are the same or a subset of previously granted scopes → consent is skipped
  • If you request new scopes not previously granted → the consent screen is shown again

Consent is stored per unique combination of client_id, user_id, and user_type.

Rate Limiting

Both the authorization and consent endpoints are rate-limited to 30 requests per 15 minutes per IP address. If exceeded, the server responds with:

{
  "message": "Too many authorization requests.",
  "status": 429
}

This protects against brute-force consent attacks and authorization code enumeration.

Error Handling

ErrorDescription
invalid_clientClient ID not found or client is inactive
invalid_redirect_uriRedirect URI doesn't match any registered URI
invalid_scopeOne or more requested scopes are invalid or not allowed for this client
invalid_requestMissing required parameters, or PKCE required but not provided
access_deniedUser declined the consent screen
429Too many requests — rate limit exceeded