Site is online, maar een normale gebruiker die met Microsoft inlogt krijgt:
Only superusers can perform this action.
Console: POST /api/collections/team_members/auth-with-oauth2 → 403 Forbidden. (De COOP-meldingen over window.close in de console zijn onschuldige popup-warnings, geen oorzaak.)
Root cause (lokaal gereproduceerd)
De team_members-collection is aangemaakt met createRule: null (migratie 1781000000_team_members_to_auth.js, met als aanname "Creation/deletion happen via OAuth2 / superuser only"). Die aanname klopt niet: PocketBase (v0.23+) past de createRule toe op de OAuth2-sign-up — het automatisch aanmaken van het auth-record bij een eerste login. createRule: null betekent "alleen superusers", dus elke eerste login faalt met exact deze 403.
Omdat de migratie de oude PIN-records bewust heeft gedropt, is iedere gebruiker een eerste login → niemand kan erin (behalve superusers via het dashboard).
POST /api/collections/team_members/auth-with-oauth2 (nieuwe gebruiker)
→ {"data":{},"message":"Only superusers can perform this action.","status":403}
authRule: "" staat goed (bestaande records zouden kunnen inloggen) — het probleem zit uitsluitend op de create bij eerste login.
Fix
PocketBase heeft hiervoor een dedicated rule-context: @request.context = "oauth2". Daarmee mag record-creatie uitsluitend binnen de OAuth2-flow plaatsvinden — de anonieme REST-create (POST /api/collections/team_members/records) blijft superuser-only (belangrijk: anders kan iedereen records met bv. role: "admin" klaarzetten).
pb_migrations/1781000002_allow_oauth2_signup.js (nieuw) — zet createRule = '@request.context = "oauth2"' op de bestaande collection (Labs is al gemigreerd; de originele migratie draait daar niet opnieuw).
Repro zonder fix: eerste login → 403 "Only superusers can perform this action."
Met fix, upgrade-pad (DB waar 1781000000 al applied is → 1781000002 erbij): eerste login → 200, record aangemaakt met role uit allow-list, enrollment_status = "not_started", naam uit OIDC-claim
Tweede login zelfde gebruiker (bestaand record, authRule-pad): 200, geen duplicaat
Verse DB (volledige keten): zelfde resultaat
Anonieme REST-create blijft 403
E2E op Labs met echt Microsoft-account (menselijke check)
Follow-up (bewust buiten scope)
TeamManager-rules: updateRule: '@request.auth.id = id' en deleteRule: null betekenen dat admins via de app géén rollen van anderen kunnen wijzigen of leden verwijderen (alleen superusers via het dashboard). Als TeamManager dat moet kunnen, is een aparte rule-uitbreiding nodig (bv. @request.auth.role = "admin"); dat raakt de trust-boundary en verdient een eigen afweging.
Gerelateerd: #16 (feature), #18 (ledger/SSO-fix), #20 (Caddyfile-crash — PR #21 nog niet gemerged!).
## Symptoom
Site is online, maar een normale gebruiker die met Microsoft inlogt krijgt:
> **Only superusers can perform this action.**
Console: `POST /api/collections/team_members/auth-with-oauth2` → **403 Forbidden**. (De COOP-meldingen over `window.close` in de console zijn onschuldige popup-warnings, geen oorzaak.)
## Root cause (lokaal gereproduceerd)
De `team_members`-collection is aangemaakt met **`createRule: null`** (migratie `1781000000_team_members_to_auth.js`, met als aanname "Creation/deletion happen via OAuth2 / superuser only"). Die aanname klopt niet: **PocketBase (v0.23+) past de `createRule` toe op de OAuth2-sign-up** — het automatisch aanmaken van het auth-record bij een eerste login. `createRule: null` betekent "alleen superusers", dus elke *eerste* login faalt met exact deze 403.
Omdat de migratie de oude PIN-records bewust heeft gedropt, is **iedere** gebruiker een eerste login → niemand kan erin (behalve superusers via het dashboard).
Reproductie (PocketBase v0.30.4, mock-OIDC-provider lokaal, productie-migraties):
```
POST /api/collections/team_members/auth-with-oauth2 (nieuwe gebruiker)
→ {"data":{},"message":"Only superusers can perform this action.","status":403}
```
`authRule: ""` staat goed (bestaande records zouden kunnen inloggen) — het probleem zit uitsluitend op de create bij eerste login.
## Fix
PocketBase heeft hiervoor een dedicated rule-context: **`@request.context = "oauth2"`**. Daarmee mag record-creatie uitsluitend binnen de OAuth2-flow plaatsvinden — de anonieme REST-create (`POST /api/collections/team_members/records`) blijft superuser-only (belangrijk: anders kan iedereen records met bv. `role: "admin"` klaarzetten).
1. **`pb_migrations/1781000002_allow_oauth2_signup.js`** (nieuw) — zet `createRule = '@request.context = "oauth2"'` op de bestaande collection (Labs is al gemigreerd; de originele migratie draait daar niet opnieuw).
2. **`pb_migrations/1781000000_team_members_to_auth.js`** — zelfde rule voor verse omgevingen (bestandsnaam blijft gelijk; ledger-veilig).
3. **`scripts/setup-pb-collections.mjs`** — fallback-entry gespiegeld (per PROTECTED.md-conventie).
4. **`docs/auth-spec.md`** — ADR + rules-matrix bijgewerkt.
## Verificatie (lokaal, mock-OIDC)
- [x] Repro zonder fix: eerste login → 403 "Only superusers can perform this action."
- [ ] Met fix, upgrade-pad (DB waar 1781000000 al applied is → 1781000002 erbij): eerste login → 200, record aangemaakt met `role` uit allow-list, `enrollment_status = "not_started"`, naam uit OIDC-claim
- [ ] Tweede login zelfde gebruiker (bestaand record, authRule-pad): 200, geen duplicaat
- [ ] Verse DB (volledige keten): zelfde resultaat
- [ ] Anonieme REST-create blijft 403
- [ ] E2E op Labs met echt Microsoft-account (menselijke check)
## Follow-up (bewust buiten scope)
- **TeamManager-rules**: `updateRule: '@request.auth.id = id'` en `deleteRule: null` betekenen dat admins via de app géén rollen van anderen kunnen wijzigen of leden verwijderen (alleen superusers via het dashboard). Als TeamManager dat moet kunnen, is een aparte rule-uitbreiding nodig (bv. `@request.auth.role = "admin"`); dat raakt de trust-boundary en verdient een eigen afweging.
Gerelateerd: #16 (feature), #18 (ledger/SSO-fix), #20 (Caddyfile-crash — PR #21 nog niet gemerged!).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Symptoom
Site is online, maar een normale gebruiker die met Microsoft inlogt krijgt:
Console:
POST /api/collections/team_members/auth-with-oauth2→ 403 Forbidden. (De COOP-meldingen overwindow.closein de console zijn onschuldige popup-warnings, geen oorzaak.)Root cause (lokaal gereproduceerd)
De
team_members-collection is aangemaakt metcreateRule: null(migratie1781000000_team_members_to_auth.js, met als aanname "Creation/deletion happen via OAuth2 / superuser only"). Die aanname klopt niet: PocketBase (v0.23+) past decreateRuletoe op de OAuth2-sign-up — het automatisch aanmaken van het auth-record bij een eerste login.createRule: nullbetekent "alleen superusers", dus elke eerste login faalt met exact deze 403.Omdat de migratie de oude PIN-records bewust heeft gedropt, is iedere gebruiker een eerste login → niemand kan erin (behalve superusers via het dashboard).
Reproductie (PocketBase v0.30.4, mock-OIDC-provider lokaal, productie-migraties):
authRule: ""staat goed (bestaande records zouden kunnen inloggen) — het probleem zit uitsluitend op de create bij eerste login.Fix
PocketBase heeft hiervoor een dedicated rule-context:
@request.context = "oauth2". Daarmee mag record-creatie uitsluitend binnen de OAuth2-flow plaatsvinden — de anonieme REST-create (POST /api/collections/team_members/records) blijft superuser-only (belangrijk: anders kan iedereen records met bv.role: "admin"klaarzetten).pb_migrations/1781000002_allow_oauth2_signup.js(nieuw) — zetcreateRule = '@request.context = "oauth2"'op de bestaande collection (Labs is al gemigreerd; de originele migratie draait daar niet opnieuw).pb_migrations/1781000000_team_members_to_auth.js— zelfde rule voor verse omgevingen (bestandsnaam blijft gelijk; ledger-veilig).scripts/setup-pb-collections.mjs— fallback-entry gespiegeld (per PROTECTED.md-conventie).docs/auth-spec.md— ADR + rules-matrix bijgewerkt.Verificatie (lokaal, mock-OIDC)
roleuit allow-list,enrollment_status = "not_started", naam uit OIDC-claimFollow-up (bewust buiten scope)
updateRule: '@request.auth.id = id'endeleteRule: nullbetekenen dat admins via de app géén rollen van anderen kunnen wijzigen of leden verwijderen (alleen superusers via het dashboard). Als TeamManager dat moet kunnen, is een aparte rule-uitbreiding nodig (bv.@request.auth.role = "admin"); dat raakt de trust-boundary en verdient een eigen afweging.Gerelateerd: #16 (feature), #18 (ledger/SSO-fix), #20 (Caddyfile-crash — PR #21 nog niet gemerged!).