8. Login
8.1 Endpoint
POST api/Tokens/Create
- Controller:
TokensController([Route("api/[controller]")]→api/Tokens,[AllowAnonymous],[TenantIdHeader]). - Required header:
TenantId: {tenantId}— note this is the header nameTenantId, notX-Tenantor anyX-prefixed variant. - Request body:
TokenRequest(string PhoneNumber, string Password)(arecord, positional parameters).
8.2 Tenant database provisioning — not triggered by registration
Registration (Chapter 5) does not provision the tenant's application
database. For a tenant that already existed at the last application startup, provisioning happens via
DatabaseInitializer.InitializeDatabasesAsync, a Hangfire-enqueued, per-tenant loop that runs only for
already-verified, already-active, non-root tenants at app startup.
For a brand-new tenant (registered after the app was already running), that startup loop never sees
it. Provisioning instead happens via a synchronous inline fallback inside TokenService.GetTokenAsync
— confirmed empirically:
- First login attempt after verification returns HTTP 423 with body
"tenant_provisioning_in_progress"while migrations and seeding run in the background/inline. - A later retry succeeds once seeding completes — confirmed via the full seed-pipeline log trace ending
in
"Seeded default notification delivery policies for tenant", the last step inApplicationDbSeeder.SeedDatabaseAsync's pipeline.
Frontend implication: the login screen must handle 423 as "still provisioning, retry shortly" —
not as an authentication failure — for any tenant that just completed activation.
8.3 Success response
{
"data": { "token": "...", "refreshToken": "...", "..." : "..." },
"statusCode": 200,
"succeeded": true,
"messages": ["..."]
}
The JWT carries TenantId, Identifier, role, and permission claims — read via ICurrentUser on
subsequent requests.
8.4 Verified dev credential pattern
Every tenant's admin user is seeded via SeedAdminUserAsync with the same hardcoded default password
(DefaultAdminConstants.Password). For the newly-registered dev tenant, the admin phone number is the
adminPhoneNumber submitted at registration. Login succeeded on the first retry after the 423
provisioning window closed.
