Skip to content

Identity (Vabu ID) ​

Vabu ID is Vabu's login service. Use it when you do not want to build sign-in yourself, or when your audience signs in with a phone number. Your app calls Vabu ID's routes, and a person comes back with a signed token that every Vabu OS product understands.

If you already have your own login, you do not need Vabu ID. See sign-in options to choose.

How the pieces fit ​

  • A person signs in once and has one Vabu ID account.
  • When they sign in through your tenant, they also become a customer of your tenant. Customers are yours: another tenant cannot see them. Customer ids start cus_.
  • Sign-in gives your app an access token (15 minutes) and a refresh token (30 days at most per session).
  • Your server checks the access token against Vabu ID's public keys, so it never has to call Vabu to know who is calling.

Sign-in methods ​

MethodCalls
WhatsApp or SMS codePOST /auth/v1/otp/start, then POST /auth/v1/otp/verify
Email linkPOST /auth/v1/magic_link/start, then POST /auth/v1/magic_link/verify
Google, Apple or FacebookGET /auth/v1/oauth/{provider}/redirect, then the provider calls back to /auth/v1/oauth/{provider}/callback
Login with PaydPOST /auth/v1/payd/login with the person's Payd token

These routes are at the API host root, not under /v1: https://api.vabu.live/auth/v1/.... They return the usual { "data": ... } and error shapes. Send your publishable key, or a tenant field with your tenant code, so the person becomes your customer.

Phone code ​

http
POST /auth/v1/otp/start
Vabu-Publishable-Key: vpk_live_...

{ "phone": "+254712345678", "channel": "whatsapp" }

channel is whatsapp (the default) or sms. WhatsApp falls back to SMS. The code is valid for 5 minutes. Five wrong tries lock it, and a new code can be asked for after 30 seconds. Codes go to numbers in Kenya, Tanzania, Uganda, Rwanda, Burundi, South Sudan and Ethiopia. POST /auth/v1/otp/verify takes the phone and the six-digit code and returns a token pair:

json
{
  "data": {
    "object": "token_pair",
    "access_token": "eyJ...",
    "token_type": "Bearer",
    "expires_in": 900,
    "refresh_token": "Zk3...",
    "session_id": "ses_01JB3K5V8X2T4Q7W9Y1Z3A5C7E",
    "scope": "openid profile"
  }
}

Linking accounts ​

A new sign-in joins an existing account on its own only when its email or phone is verified and matches. Otherwise the response is a link_request, and the person confirms with POST /auth/v1/link/confirm after signing in to the account they already have.

Use the tokens ​

Send the access token as Authorization: Bearer .... Refresh it with the refresh token:

http
POST /auth/v1/token/refresh

{ "refresh_token": "Zk3..." }

You get a new pair, and the old refresh token is spent. Keep only the newest one, and run one refresh at a time per session. If a spent refresh token is used again, the whole session ends. A session also ends 30 days after it started.

Verify a token on your server ​

Vabu ID signs access tokens with ES256 and publishes the public keys at /.well-known/jwks.json. Check the signature, the expiry, the issuer and the audience. The issuer is the API host, and the audience is your tenant's ten_ id for a tenant sign-in.

ts
import { createRemoteJWKSet, jwtVerify } from 'jose'

const jwks = createRemoteJWKSet(new URL('https://api.vabu.live/.well-known/jwks.json'))

export async function verifyVabuToken(token: string, tenantId: string) {
  const { payload } = await jwtVerify(token, jwks, {
    issuer: 'https://api.vabu.live',
    audience: tenantId, // your tenant id, "ten_..."
  })
  return { person: payload.sub, customer: payload.cus, session: payload.sid }
}

The claims you will use: sub (the person, usr_...), cus (the customer in your tenant, cus_...), tid (your tenant), sid (the session) and amr (how they signed in).

A token for one tenant does not work for another, and a token from a test publishable key cannot call live-only routes. A suspended account loses all its sessions at once.

Customers ​

A customer is one of your end users. You can also create them yourself:

MethodPathWhat it does
GET/v1/customersFind customers by email, phone, external_id or metadata[key]
POST/v1/customersRegister your own user, with an Idempotency-Key
GET/v1/customers/{id}Show one
GET/v1/customers/external/{external_id}Show one by your own id
PATCH/v1/customers/{id}Change email, phone, name, external_id or metadata
json
{
  "data": {
    "id": "cus_01JB3K5V8X2T4Q7W9Y1Z3A5C7E",
    "object": "customer",
    "email": "amina@example.com",
    "phone": "+254712345678",
    "name": "Amina Wanjiru",
    "external_id": "user_42",
    "has_vabu_id": false,
    "livemode": true
  }
}

Customer calls take a secret key, or a restricted key with customers:read or customers:write. Publishable keys are refused. external_id is unique per tenant and environment, and a second customer with the same one returns 409 external_id_taken.

Sessions and devices ​

A signed-in person can see and end their own sessions:

MethodPathWhat it does
GET/auth/v1/meThe signed-in person, with their sign-in methods
GET/auth/v1/sessionsTheir open sessions, with device and last seen
DELETE/auth/v1/sessions/{id}End one
POST/auth/v1/logoutEnd the current session

A tenant token only sees and ends sessions for its own tenant.

All the Vabu ID routes ​

MethodPath
GET/.well-known/jwks.json
POST/auth/v1/payd/login
POST/auth/v1/otp/start and /auth/v1/otp/verify
POST/auth/v1/magic_link/start and /auth/v1/magic_link/verify
GET/auth/v1/oauth/{provider}/redirect and /auth/v1/oauth/{provider}/callback
POST/auth/v1/link/confirm
POST/auth/v1/token/refresh
POST/auth/v1/logout
GET/auth/v1/sessions, /auth/v1/me
DELETE/auth/v1/sessions/{id}
GET/v1/me

The customer routes above are in the API reference. The /auth/v1 routes are described here.

Vabu OS documentation