Ideapreneur Nepal logoSuperPreneur for Developers v1.0

Server-side contract

Your server exposes four small routes and calls one route on ours. There is no library to install: this page is the full contract, followed by an outline of the logic. Complete, tested servers in Node.js and Python are in Complete server examples, and every request and response is shown in Request and response examples.

Routes your server must provide#

Route Does
GET /api/config Returns {"shell_origin": "<web shell origin>"} so the page knows who to talk to in the browser
POST /api/nonce Creates a random nonce (at least 24 bytes), stores it for 2 minutes, returns {"nonce": "..."}
POST /api/session Body {token, nonce}. Removes the nonce first (so it works once), calls the verify route below, then returns {"session": "...", "sub": "...", "name": "..."}
every other /api/* Requires Authorization: Bearer <session>; answer 401 otherwise. Sessions can live in memory for about an hour

The route you call on SuperPreneur#

POST {IDP}/api/launch/verify with HTTP Basic auth (client_id as user, client_secret as password) and a JSON body {"token": "...", "nonce": "..."}. The API reference has the full description.

Status Body Meaning
200 {"sub": "...", "scopes": ["profile"], "name": "..."} Valid. name is present only if the profile scope was approved
400 {"error": "expired"} Token older than 60 seconds
400 {"error": "replayed"} Token already used
400 {"error": "nonce_mismatch"} The nonce does not match the one the token was issued for
400 {"error": "wrong_audience"} The token was issued for a different app
400 {"error": "invalid_token"} Bad signature or malformed token
400 {"error": "revoked"} The user revoked access, was deactivated, or the app is no longer allowed
401 {"error": "invalid_client"} Wrong client id or secret
403 {"error": "app_suspended"} The app has been suspended
429 More than 600 calls a minute

Responses are marked Cache-Control: no-store. A rejected call does not use up a valid token, so a wrong nonce does not lock the user out.

What sub is. A stable id for this user in your app only. Use it as the key for the user's data (carts, bookings, scores). Never use the name as a key.

The login step in outline (any language)#

POST /api/nonce:
    nonce = 24 random bytes, URL-safe Base64 without padding        (32 characters)
    nonces[nonce] = now + 120 seconds
    return {nonce}

POST /api/session  with {token, nonce}:
    if token is empty or nonce does not match ^[A-Za-z0-9_-]{16,128}$ :  return 400 bad_request
    expiry = nonces.remove(nonce)             # remove FIRST, in one atomic step
    if there was none, or it has expired:      return 400 unknown_or_expired_nonce
    reply = POST {IDP}/api/launch/verify       # Basic auth client_id:client_secret, 5 second timeout
                  body {token, nonce}
    if reply.status != 200:                    return 401 {error: reply.error}
    session = 32 random bytes, URL-safe Base64
    sessions[session] = {sub, name, expires = now + 1 hour}
    return 200 {session, sub, name}

every other /api/* route:
    user = sessions[Bearer token from the Authorization header], if not expired
    if no user:                                return 401 {error: no_session}
    ... key everything you store by user.sub

Nonces and sessions can live in memory on a single server. With several servers, keep them in a shared store such as Redis (see Generating the nonce).