Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b4VQVEJWetTT1dYzyJ
43 lines
2.3 KiB
Plaintext
43 lines
2.3 KiB
Plaintext
# JWT -> filter -> TenantContext -> schema-per-tenant (shared pool + search_path), real HTTP
|
||
provisioned schemas: acme, globex (ghostco deliberately not provisioned)
|
||
|
||
== the filter: who gets in
|
||
-- no Authorization header
|
||
GET /invoices -> HTTP 401 {"error":"missing bearer token"}
|
||
-- garbage token
|
||
GET /invoices -> HTTP 401 {"error":"invalid token"}
|
||
-- token signed with a different secret
|
||
GET /invoices -> HTTP 401 {"error":"invalid token"}
|
||
-- expired token (exp = 10 minutes ago)
|
||
GET /invoices -> HTTP 401 {"error":"invalid token"}
|
||
-- valid signature, no tenant_id claim
|
||
GET /invoices -> HTTP 403 {"error":"token carries no valid tenant"}
|
||
-- valid signature, tenant_id = "x; drop schema public cascade"
|
||
GET /invoices -> HTTP 403 {"error":"token carries no valid tenant"}
|
||
-- valid signature, tenant_id = "public" (reserved name)
|
||
GET /invoices -> HTTP 403 {"error":"token carries no valid tenant"}
|
||
|
||
== two tenants, same URL, different data
|
||
-- acme creates INV-1
|
||
POST /invoices -> HTTP 201 {"id":1,"number":"INV-1","amount":100.00,"tenant":"acme"}
|
||
-- acme creates INV-2
|
||
POST /invoices -> HTTP 201 {"id":2,"number":"INV-2","amount":250.00,"tenant":"acme"}
|
||
-- globex creates INV-9
|
||
POST /invoices -> HTTP 201 {"id":1,"number":"INV-9","amount":999.00,"tenant":"globex"}
|
||
-- acme lists
|
||
GET /invoices -> HTTP 200 [{"id":1,"number":"INV-1","amount":100.00,"tenant":"acme"},{"id":2,"number":"INV-2","amount":250.00,"tenant":"acme"}]
|
||
-- globex lists
|
||
GET /invoices -> HTTP 200 [{"id":1,"number":"INV-9","amount":999.00,"tenant":"globex"}]
|
||
|
||
== cross-tenant read by id
|
||
-- globex asks for acme's INV-2 by id (2)
|
||
GET /invoices/2 -> HTTP 404
|
||
-- acme asks for the same id
|
||
GET /invoices/2 -> HTTP 200 {"id":2,"number":"INV-2","amount":250.00,"tenant":"acme"}
|
||
|
||
== a well-formed tenant nobody has onboarded
|
||
-- token for tenant "ghostco": GET /invoices
|
||
GET /invoices -> HTTP 500 <!doctype html><html lang="en"><head><title>HTTP Status 500 – Internal Server Error</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1...
|
||
-- token for tenant "ghostco": POST /invoices
|
||
POST /invoices -> HTTP 500 <!doctype html><html lang="en"><head><title>HTTP Status 500 – Internal Server Error</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1...
|