Aller au contenu principal
Petanque Life

Global Player Passport (FIPJP)

F03.08 8 fonctionnalités

En bref

Global Player Passport is the FIPJP-tenant-scoped cross-tenant identity layer that links a player's shared auth-identity to every national license they hold across all federations. It enables privacy-safe public verification across federations, atomic FIPJP-YYYY-NNNNNN passport numbering, automatic fire-and-forget onboarding when a national license is issued, M2M-secured sync from each national tenant, and an idempotent backfill job to seed passports for the existing license population.

Comment ça fonctionne

The Global Passport lives in the FIPJP tenant and acts as the canonical cross-tenant view of a player. The GlobalPassport Beanie document carries a passport_id formatted FIPJP-YYYY-NNNNNN, a unique auth_identity_id linking back to the shared OAuth/OTP identity, personal details, and an embedded linked_licenses list with tenant_code, license_number, season, status, and last_verified_at per entry. Because everything is tenant-scoped to FIPJP, no national tenant ever stores the passport itself — they reference it indirectly through the auth identity.

Passport numbering is governed by FIPJPTenantConfig, a singleton that holds passport_number_format, next_passport_sequence (incremented atomically via $inc to avoid collisions under concurrency), photo_required, and photo_max_size_kb. Admin endpoints cover the lifecycle: POST /fipjp/passports creates a passport, GET /fipjp/passports/{id} retrieves it, POST /link-license attaches a national license, POST /verify-license verifies a linked license against the issuing national tenant (with a 24-hour cache to avoid hammering the source-of-truth API), and PATCH /status suspends or revokes. Two public verification endpoints — GET /public/passports/{id} and GET /public/passports/by-license?tenant_code&license_number — return privacy-safe data only (name, nationality, license status), explicitly omitting date of birth and photo, so visiting referees and event organisers can verify a player without leaking PII.

Automatic onboarding is implemented in passport_sync.py: link_local_license_to_passport() creates a passport on the player's first license issuance and appends to the linked_licenses list on subsequent issuances. The sync is fire-and-forget — if the FIPJP call fails, the national license issuance still succeeds, and the link is reconciled later. Each national TenantConfig carries fipjp_passport_sync_enabled (default true), fipjp_passport_sync_endpoint, and fipjp_m2m_credentials_secret_ref so M2M auth happens through Key Vault rather than configuration. A backfill job, backfill_global_passports(), iterates every active license across every national tenant and creates or updates passports per auth_identity; the job is idempotent so it can be re-run safely.

Capacités clés

  • FIPJP-tenant-scoped GlobalPassport with cross-tenant passport_id (FIPJP-YYYY-NNNNNN)
  • Embedded linked_licenses list per passport with last_verified_at and 24-hour verification cache
  • Atomic passport sequence numbering via FIPJPTenantConfig and $inc
  • Privacy-safe public verification endpoints by passport_id and by national license
  • Automatic onboarding (fire-and-forget) on first national license issuance
  • Per-national-tenant sync configuration with M2M credentials via Key Vault secret reference
  • Idempotent backfill job to seed passports for existing license populations

En pratique

A Senegalese player travels to France for an international tournament. At registration, the event organiser scans the QR on the player's national license and the system calls GET /public/passports/by-license?tenant_code=SN&license_number=SEN-2026-001234. FIPJP returns the passport with linked licenses across Senegal and a previous one in Morocco — name, nationality, current license status only, no DoB or photo.

The organiser confirms the player is in good standing and admits them. Behind the scenes, when the Senegalese federation issued that license earlier in the year, the national tenant's passport_sync.py fire-and-forget call to FIPJP appended the new license to the player's existing GlobalPassport in the linked_licenses list and bumped last_verified_at. The cache means subsequent verifications during the same tournament weekend hit the cache rather than the national tenant's API.

Fonctionnalités de ce sous-système

8
ID Status Fonctionnalités
F03.08.01 Livré GlobalPassport datamodell — Beanie-dokument med passport_id (FIPJP-YYYY-NNNNNN), auth_identity_id (unik), personuppgifter, linked_licenses (embedded lista med tenant_code, license_number, season, status, last_verified_at). Tenant-scoped till FIPJP. — PL-T009 ✅ PL-T009
F03.08.02 Livré FIPJPTenantConfig — Singleton per FIPJP-tenant: passport_number_format, next_passport_sequence (atomärt via $inc), photo_required, photo_max_size_kb. — PL-T009 ✅ PL-T009
F03.08.03 Livré FIPJP admin endpoints — POST /fipjp/passports (skapa), GET /fipjp/passports/{id} (hämta), POST /link-license (koppla licens), POST /verify-license (verifiera mot nationell tenant, 24h cache), PATCH /status (suspendera/återkalla). — PL-T009 ✅ PL-T009
F03.08.04 Livré Publika verifierings-endpoints — GET /public/passports/{id} (privacy-safe: namn, nationalitet, licensstatus — ingen DoB/foto), GET /public/passports/by-license?tenant_code&license_number (lookup). — PL-T009 ✅ PL-T009
F03.08.05 Livré Automatisk onboarding — passport_sync.py: link_local_license_to_passport() skapar passport vid första licens, lägger till licens vid ytterligare. Fire-and-forget (licensutfärdande misslyckas inte vid sync-fel). — PL-T009 ✅ PL-T009
F03.08.06 Livré TenantConfig passport sync — fipjp_passport_sync_enabled (default true), fipjp_passport_sync_endpoint, fipjp_m2m_credentials_secret_ref på alla national tenants. — PL-T009 ✅ PL-T009
F03.08.07 Livré Backfill-jobb — backfill_global_passports(): itererar alla aktiva licenser över alla national tenants, skapar passport per auth_identity, länka licens. Idempotent. — PL-T009 ✅ PL-T009
F03.08.08 Livré Cross-tenant suspension-koppling till passport — CrossTenantSuspensionEntry.subject_passport_id länkar varje global suspension till sitt FIPJP-pass. Subscriber-tenants matchar på subject_passport_id (O(1)) först, sen fallback på subject_full_name_normalized + birth_year när passport saknas. Aktiv ban blockerar licens-issuance och event-inscription via LocalSuspensionCache-uppslag. Se docs/domain/discipline/cross-tenant-suspension.md. — PL-T213 ✅ PL-T213