Identity Architecture
How authentication works across Yebo.
Core Principle
One identity, everywhere.
When you create a YeboID:
- You get one @handle
- You authenticate once
- All products recognize you
Architecture
┌─────────────────────────────────────────────────────────────┐
│ USER │
│ Phone: +254712345678 • @laslie │
└─────────────────────────────────────────────────────────────┘
│
Authenticates
▼
┌─────────────────────────────────────────────────────────────┐
│ YEBOID │
│ │
│ • Validates phone + PIN │
│ • Issues JWT tokens (access + refresh) │
│ • Manages profile, handle, KYC │
│ │
└─────────────────────────────────────────────────────────────┘
│
Issues JWT
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│YeboShops │ │YeboJobs │ │YeboLearn │
│ │ │ │ │ │
│ Validates │ │ Validates │ │ Validates │
│ JWT local │ │ JWT local │ │ JWT local │
└───────────┘ └───────────┘ └───────────┘Token System
Access Token
- Type: JWT
- Lifetime: 15 minutes
- Content:
json
{
"userId": "uuid",
"handle": "laslie",
"phone": "+254712345678",
"verified": true,
"iat": 1710770000,
"exp": 1710770900
}- Validation: Local (no API call)
Refresh Token
- Type: Opaque string
- Lifetime: 30 days
- Storage: Hashed in database
- Rotation: New one issued on each use
Shared Secret
All Yebo products share YEBOID_JWT_SECRET:
GCP Secret Manager (org level)
│
└── yeboid-jwt-secret
│
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
YeboShops YeboJobs YeboLearn
(mounted) (mounted) (mounted)This allows local validation:
javascript
// In any Yebo product
const jwt = require('jsonwebtoken');
function validateToken(token) {
return jwt.verify(token, process.env.YEBOID_JWT_SECRET);
}
// Middleware
function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
try {
const payload = validateToken(token);
req.userId = payload.userId;
req.handle = payload.handle;
req.verified = payload.verified;
next();
} catch {
res.status(401).json({ error: 'Invalid token' });
}
}User Linking
Each product has its own user table, linked to YeboID:
sql
-- In YeboShops
CREATE TABLE users (
id UUID PRIMARY KEY,
yeboid_user_id UUID UNIQUE NOT NULL,
-- Product-specific fields
seller_rating DECIMAL,
total_sales INTEGER DEFAULT 0
);
-- In YeboJobs
CREATE TABLE users (
id UUID PRIMARY KEY,
yeboid_user_id UUID UNIQUE NOT NULL,
-- Product-specific fields
cv_url TEXT,
skills JSONB
);First-Time Access
javascript
async function getOrCreateUser(yeboidUserId) {
let user = await db.users.findByYeboId(yeboidUserId);
if (!user) {
// First time on this product
user = await db.users.create({ yeboidUserId });
}
return user;
}Authentication Flows
Login
User YeboID Product
│ │ │
│──── Phone + PIN ──────►│ │
│ │ │
│◄─── Access Token ──────│ │
│◄─── Refresh Token ─────│ │
│ │ │
│──── Request + Token ──────────────────────────►│
│ │ │
│ │ (validates locally)│
│ │ │
│◄─────────────────── Response ──────────────────│Token Refresh
User YeboID
│ │
│── Refresh Token ──────►│
│ │
│◄── New Access Token ───│
│◄── New Refresh Token ──│ (old one invalidated)Cross-Product
User authenticated with YeboShops
│
│ Same token
▼
YeboJobs validates same JWT
│
│ User recognized
▼
Continue without re-loginSDK Usage
Node.js SDK
javascript
const { YeboIDAuth } = require('@yeboid/node');
const auth = new YeboIDAuth({
secret: process.env.YEBOID_JWT_SECRET
});
// Middleware
app.use(auth.middleware());
// In route
app.get('/profile', auth.required, (req, res) => {
// req.yeboUser available
res.json({
userId: req.yeboUser.userId,
handle: req.yeboUser.handle
});
});React SDK
jsx
import { YeboIDProvider, useYeboID } from '@yeboid/react';
function App() {
return (
<YeboIDProvider clientId="yeboshops">
<MyApp />
</YeboIDProvider>
);
}
function MyApp() {
const { user, isAuthenticated, login, logout } = useYeboID();
if (!isAuthenticated) {
return <button onClick={login}>Login with YeboID</button>;
}
return <div>Welcome, @{user.handle}!</div>;
}Security Considerations
Token Security
- Short-lived access tokens (15 min)
- Refresh token rotation
- HTTPS only
PIN Security
- bcrypt hashing (cost 12)
- Rate limiting (5 attempts / 15 min)
- Account lockout
Cross-Origin
- CORS configured per product
- Same-site cookies where applicable
Migration
For existing products with their own auth:
- Add
yeboid_user_idcolumn - Match existing users by phone
- Update auth middleware
- Deprecate old login
See Integration Guide for details.