Na de fix van #22 faalt de Microsoft-login met een nieuwe fout:
{"data":{"email":{"code":"validation_required","message":"Cannot be blank."}},"message":"Failed to create record.","status":400}
Root cause (gereproduceerd met mock-Entra, PocketBase v0.30.4)
De provider haalt de gebruikersclaims op via Graph https://graph.microsoft.com/oidc/userinfo. Dat endpoint geeft de email-claim alleen terug als het account een mail-attribuut heeft (Exchange-mailbox). Voor accounts zonder mail-attribuut (gebruikelijk in lab-/testtenants) ontbreekt de claim → PocketBase maakt het record aan met lege e-mail → email is verplicht op team_members → 400 "Failed to create record".
De fingerprint-matrix uit de analyse (token-fail / userinfo-fail / e-mail-ontbreekt) matcht exact op de derde variant; de door de gebruiker aangeleverde response-body is identiek aan de lokaal gereproduceerde.
Fix (twee lagen, beide nodig voor robuustheid)
Claims uit het id_token i.p.v. Graph-userinfo (userInfoURL: "" in de provider-config). Het Entra v2 id_token bevat naast email (indien aanwezig) áltijd preferred_username — de UPN, bij Respellion gelijk aan het primaire e-mailadres. Empirisch geverifieerd: PocketBase mapt preferred_username → username en houdt de volledige claims beschikbaar in rawUser. De provider-config gaat via de #18-reconciler, dus Labs flipt automatisch bij de eerstvolgende deploy-boot — geen migratie nodig.
UPN-fallback in een onRecordAuthWithOAuth2Request-hook: als de email-claim leeg is en preferred_username een geldig e-mailadres is (regex-guard; guest-UPN's met #EXT# vallen er bewust buiten), wordt die als e-mail gebruikt (lowercased — matcht de ENTRA_ADMIN_EMAILS-allow-list). De hook logt bovendien elke gefaalde OAuth2-poging met de onderliggende fout naar de containerlog — de observability die we bij deze debugsessie misten.
Lokaal bewezen: id_token zonder e-mailclaim → record met email=rve@respellion.nl, role=admin via allow-list, onboarding-defaults gezet.
Verificatie
Repro: id_token/userinfo zonder e-mail → exact de productie-400
Fix: zelfde scenario → 200, e-mail uit UPN, role/enrollment correct
Mail-attribuut zetten op de Entra-accounts lost het voor dít account op, maar laat de app fragiel voor elk volgend account zonder mailbox; de UPN-fallback maakt de login deterministisch voor alle organisatie-accounts.
Gerelateerd: #16, #18, #20, #22 (PR #23 — nog niet gemerged; deze fix stapelt daarop).
## Symptoom
Na de fix van #22 faalt de Microsoft-login met een nieuwe fout:
```json
{"data":{"email":{"code":"validation_required","message":"Cannot be blank."}},"message":"Failed to create record.","status":400}
```
## Root cause (gereproduceerd met mock-Entra, PocketBase v0.30.4)
De provider haalt de gebruikersclaims op via **Graph `https://graph.microsoft.com/oidc/userinfo`**. Dat endpoint geeft de `email`-claim **alleen** terug als het account een mail-attribuut heeft (Exchange-mailbox). Voor accounts zonder mail-attribuut (gebruikelijk in lab-/testtenants) ontbreekt de claim → PocketBase maakt het record aan met lege e-mail → `email` is verplicht op `team_members` → 400 "Failed to create record".
De fingerprint-matrix uit de analyse (token-fail / userinfo-fail / e-mail-ontbreekt) matcht exact op de derde variant; de door de gebruiker aangeleverde response-body is identiek aan de lokaal gereproduceerde.
## Fix (twee lagen, beide nodig voor robuustheid)
1. **Claims uit het id_token i.p.v. Graph-userinfo** (`userInfoURL: ""` in de provider-config). Het Entra v2 id_token bevat naast `email` (indien aanwezig) áltijd `preferred_username` — de UPN, bij Respellion gelijk aan het primaire e-mailadres. Empirisch geverifieerd: PocketBase mapt `preferred_username` → `username` en houdt de volledige claims beschikbaar in `rawUser`. De provider-config gaat via de #18-reconciler, dus Labs flipt automatisch bij de eerstvolgende deploy-boot — geen migratie nodig.
2. **UPN-fallback in een `onRecordAuthWithOAuth2Request`-hook**: als de `email`-claim leeg is en `preferred_username` een geldig e-mailadres is (regex-guard; guest-UPN's met `#EXT#` vallen er bewust buiten), wordt die als e-mail gebruikt (lowercased — matcht de `ENTRA_ADMIN_EMAILS`-allow-list). De hook logt bovendien elke gefaalde OAuth2-poging met de onderliggende fout naar de containerlog — de observability die we bij deze debugsessie misten.
Lokaal bewezen: id_token zonder e-mailclaim → record met `email=rve@respellion.nl`, `role=admin` via allow-list, onboarding-defaults gezet.
## Verificatie
- [x] Repro: id_token/userinfo zonder e-mail → exact de productie-400
- [x] Fix: zelfde scenario → 200, e-mail uit UPN, role/enrollment correct
- [ ] Volledige matrix na implementatie (met/zonder e-mailclaim, ongeldig UPN → nette fout, regressie #22-matrix + #18-harness)
- [ ] E2E op Labs met echt Microsoft-account
## Alternatief overwogen en afgewezen
Mail-attribuut zetten op de Entra-accounts lost het voor dít account op, maar laat de app fragiel voor elk volgend account zonder mailbox; de UPN-fallback maakt de login deterministisch voor alle organisatie-accounts.
Gerelateerd: #16, #18, #20, #22 (PR #23 — nog niet gemerged; deze fix stapelt daarop).
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
Na de fix van #22 faalt de Microsoft-login met een nieuwe fout:
Root cause (gereproduceerd met mock-Entra, PocketBase v0.30.4)
De provider haalt de gebruikersclaims op via Graph
https://graph.microsoft.com/oidc/userinfo. Dat endpoint geeft deemail-claim alleen terug als het account een mail-attribuut heeft (Exchange-mailbox). Voor accounts zonder mail-attribuut (gebruikelijk in lab-/testtenants) ontbreekt de claim → PocketBase maakt het record aan met lege e-mail →emailis verplicht opteam_members→ 400 "Failed to create record".De fingerprint-matrix uit de analyse (token-fail / userinfo-fail / e-mail-ontbreekt) matcht exact op de derde variant; de door de gebruiker aangeleverde response-body is identiek aan de lokaal gereproduceerde.
Fix (twee lagen, beide nodig voor robuustheid)
userInfoURL: ""in de provider-config). Het Entra v2 id_token bevat naastemail(indien aanwezig) áltijdpreferred_username— de UPN, bij Respellion gelijk aan het primaire e-mailadres. Empirisch geverifieerd: PocketBase maptpreferred_username→usernameen houdt de volledige claims beschikbaar inrawUser. De provider-config gaat via de #18-reconciler, dus Labs flipt automatisch bij de eerstvolgende deploy-boot — geen migratie nodig.onRecordAuthWithOAuth2Request-hook: als deemail-claim leeg is enpreferred_usernameeen geldig e-mailadres is (regex-guard; guest-UPN's met#EXT#vallen er bewust buiten), wordt die als e-mail gebruikt (lowercased — matcht deENTRA_ADMIN_EMAILS-allow-list). De hook logt bovendien elke gefaalde OAuth2-poging met de onderliggende fout naar de containerlog — de observability die we bij deze debugsessie misten.Lokaal bewezen: id_token zonder e-mailclaim → record met
email=rve@respellion.nl,role=adminvia allow-list, onboarding-defaults gezet.Verificatie
Alternatief overwogen en afgewezen
Mail-attribuut zetten op de Entra-accounts lost het voor dít account op, maar laat de app fragiel voor elk volgend account zonder mailbox; de UPN-fallback maakt de login deterministisch voor alle organisatie-accounts.
Gerelateerd: #16, #18, #20, #22 (PR #23 — nog niet gemerged; deze fix stapelt daarop).