ADR-0003. Temporarily skip authorization on License read routes
| Field | Value |
|---|---|
| Status | Accepted |
| Date | 2026-04-15 |
| Deciders | licensing-team, identity-team |
| Supersedes | — |
Context
LicenseControlleris built fromControllerFactory.defineCrudControllerwith per-routeauthorizepermissions (License.find,License.findById,License.count,License.findOne).- Casbin authorization is per-merchant (domain =
x-merchant-id). But a license is queried byentity(aUserorMerchant) before a merchant is selected — e.g. during onboarding, the user has no active merchant domain yet. - With per-merchant authz enabled, these reads would 403 for legitimate pre-merchant flows (free-trial onboarding, license discovery by entity).
- Write routes (
create,updateById,deleteById,deleteBy) and the lifecycle actions are not affected — they keep their permission gates.
Decision
We will set authorize: { skip: true } on the four License read routes (find, findById, count, findOne), leaving them authenticate-only (JWT or Basic). The original authorize lines are kept commented in place so they can be restored when the per-merchant scoping design ("option B") lands.
This is explicitly temporary and marked TEMP(authz-skip) in the source.
Consequences
| Pros | Cons |
|---|---|
| Pre-merchant onboarding flows work without 403 | License reads are authenticated but not authorized — any logged-in caller can read license rows |
No IGNIS / ControllerFactory changes needed | Carries a security-debt marker that must be paid down |
| Reversible — commented permissions document the target state | Easy to forget; relies on the TEMP comment + this ADR for tracking |
Alternatives Considered
| Option | Pros | Cons | Why rejected |
|---|---|---|---|
| Keep authz on reads | Fully gated | Breaks pre-merchant onboarding (no domain to scope against) | Blocks a core flow |
| Add a dedicated unauthenticated "discover by entity" route | Narrow surface | New route + schema; duplicates CRUD | More code for a temporary state |
| Implement per-entity (non-merchant) authz now | Correct long-term | Larger design ("option B"); not ready | Deferred, not abandoned |
References
licensing/src/controllers/license/license.controller.ts—routes.{find,findById,count,findOne}.authorize.skip+TEMP(authz-skip)comment- See user memory: per-merchant authorization re-enablement (Casbin domains,
x-merchant-id)