KeiSeiKit-1.0/skills/auth-setup/phase-4-authorization.md
Parfii-bot 505e727dcf feat(skills): /auth-setup 5-phase pipeline
Hub-and-spoke skill that converts "I need auth for app X" into a
reviewable plan across 5 phases: intake (flows/stack/storage/MFA),
identity-provider pick + env scaffold, session strategy + cookies,
authorization model + permission matrix, and threats + mitigations.

- 8 AskUserQuestion calls total (≥6 hub-and-spoke contract; 4 in Phase 1
  + 1 each in Phases 2–5).
- Reads all four _blocks/auth-*.md; never writes production code or
  secret values.
- RULE 0.8 (Secrets SSoT): emits env VARIABLE NAMES only; storage path
  is secrets/auth.env per domain-has-secrets.md.
- Constructor Pattern: 6 files, largest 115 LOC (<200 limit).
- Fail-closed default + NO DOWNGRADE on unsafe combinations
  (passkey-only without recovery → return recovery-path options, not
  "not supported").

Evidence grade [E2] — pipeline mirrors OWASP ASVS v4.0.3 chapters 2–4.
2026-04-21 20:47:21 +08:00

3.4 KiB

Phase 4 — Authorization model + permission matrix

Decides who-can-do-what after authentication. Reads _blocks/auth-authorization.md. Fail-closed by default.

4a — Model click (AskUserQuestion, single-select)

{
  "questions": [
    {
      "question": "Authorization model?",
      "header": "Authz",
      "multiSelect": false,
      "options": [
        {"label": "RBAC (roles → permissions)",
         "description": "Static roles (admin / editor / viewer). Simplest, enough for most apps with <5 roles."},
        {"label": "RBAC + ownership",
         "description": "Roles + per-row owner_id check. The sweet spot for multi-tenant SaaS."},
        {"label": "ABAC (policy engine)",
         "description": "Attributes + context (time, IP, resource tier). Use Cerbos or OPA. Adopt when rule count >~20."},
        {"label": "ReBAC (Google Zanzibar)",
         "description": "Graph-shaped sharing (folders/teams/orgs). SpiceDB or OpenFGA. Pick only if your domain is inherently graph-shaped."},
        {"label": "None — single-user app",
         "description": "No authz layer beyond authentication. Record explicitly."}
      ]
    }
  ]
}

Store as AUTHZ.

4b — Emit permission matrix skeleton (inline)

For RBAC / RBAC + ownership, emit a table stub the user must fill in before coding. Example for a notes app:

| Role   | notes:read | notes:write | notes:delete | users:manage |
|--------|:---------:|:-----------:|:------------:|:------------:|
| admin  |    all    |     all     |     all      |     yes      |
| editor |   owned   |   owned     |   owned      |      no      |
| viewer |   owned   |     no      |     no       |      no      |
  • Columns = resource:action tokens — these become the Permission enum variants in code.
  • Cells = all / owned / no / shared:<relation> — NEVER free-text.
  • Save as docs/permissions.md in the target repo; treat it as code (tested, reviewed, versioned).

4c — Enforcement-point rule (inline)

  • Middleware, not handlers. Every authenticated request runs an authz decision BEFORE the handler sees it. Handler receives a typed AuthorizedRequest<Action, Resource> or the request 403s earlier in the stack.
  • Ownership checks enforced in BOTH the middleware AND the data layer (WHERE tenant_id=$1 AND owner_id=$2). Double layer defeats IDOR.
  • Fail-closed: unknown action, missing role, policy-engine error → 403. Log every denial with subject + action + resource + reason.
  • Audit log append-only row on every privilege change, role assignment, and denied action. Required for SOC2 / HIPAA / ISO 27001.

4d — Policy-engine pick (inline, driven by AUTHZ)

  • RBAC / RBAC + ownership → in-code match on Permission enum; no engine.
  • ABAC → Cerbos (YAML rules, stateless decision service) OR OPA/Rego (general-purpose, steeper curve). Keep .cerbos.yaml / .rego files in the repo, unit-tested like code.
  • ReBAC → SpiceDB (Zanzibar reference) OR OpenFGA (Auth0-backed). Define the schema, seed relationships, use the client SDK.
  • None → emit a single line "authz skipped — no multi-user model".

Verify-criterion

  • AUTHZ is exactly one choice.
  • If RBAC chosen, permission-matrix skeleton with ≥1 row + ≥1 column is printed.
  • Enforcement-point rule (4c) is emitted verbatim — non-negotiable.
  • If a policy engine is implied by AUTHZ, the pick is named (Cerbos / OPA / SpiceDB / OpenFGA).