Auth and access¶
Login¶
/login offers email or username with password, phone OTP and Google. The default tab is the user's last method.
| Endpoint | Purpose |
|---|---|
POST /auth/login |
Username or email and password |
POST /auth/register-individual |
Learner email signup. No session until email OTP is verified. |
POST /auth/email-otp/{send,verify} |
Email OTP |
POST /auth/phone-otp/{send,verify} |
Phone OTP signup and login |
GET /auth/google/{authorize,callback} |
Google OAuth (web) |
POST /auth/google/mobile |
Google ID token from the native app |
POST /auth/refresh |
Rotate tokens |
POST /auth/logout |
Clear cookies |
GET /auth/me |
Current user, plan, capabilities, available chat modes |
Tokens¶
- Web uses HttpOnly cookies. Learner auth cookies are scoped to
.kwilo.aiso kwilo.ai and app.kwilo.ai share a session. - Mobile stores tokens in secure storage and sends a Bearer header.
- On a 401 the client queues requests, refreshes once and retries.
- Staging API sits behind Cloudflare Access. Native staging builds send a service token. Production isn't gated.
Identity model¶
| Field | Meaning |
|---|---|
role |
What the account can do. See Roles and permissions. |
signup_intent |
What a Learner said they are at signup: learner, parent, trainer or creator. Never used for authorisation. |
b2c_plan |
free or pro. Decides limits, not features. |
org_unit_id |
The active institution. Every Learner account has one too (the shared Individual workspace), so never test it for null to detect B2C. Use role == b2c_user. |
Uniqueness: username and google_id are unique. email isn't, because a parent can share an email with a child, except that one Learner account is allowed per email. Any lookup by email has to scope the query.
Capabilities¶
Between identity and plan sits a capability layer:
- Identity: role and signup intent.
- Capabilities: a pure function of identity, answering "can this account do X at all?" A miss is 403.
- Plan and quota: "is X unlocked right now?" A miss is 402 or 429.
Endpoints declare one capability each with require_capability. The frontend reads capabilities from /auth/me and never recomputes them.
| Capability | Institutional trainer | Institutional learner | Learner (B2C) |
|---|---|---|---|
| Chat explain | ✓ | ✓ | ✓ |
| Research | ✓ | · | · |
| Exam prep | · | ✓ | ✓ |
| Create presentation | ✓ | · | ✓ |
| Create lesson plan, question bank | ✓ | · | · |
| Build personal knowledge base | ✓ | · | ✓ |
| Query knowledge base | ✓ | ✓ | ✓ |
| Create image | · | · | ✓ |
Where it lives¶
apps/backend/src/api/v1/auth.py; Learner signup inkwiloai-b2capps/backend/src/core/authorization/(capabilities, modes, dependencies, ERP permission catalog)apps/backend/src/api/v1/capabilities.py