Skip to content

Herde IdProvider (MockPorten) til sikker Test-IDP for #1409 #1983

Description

@TheTechArch

Bakgrunn

Som upstream Test-IDP for #1409 skal vi gjenbruke det eksisterende mock-prosjektet IdProvider ("MockPorten") (TheTechArch/IdProvider, gren master). En gjennomgang viser at den i dag er en mock som utsteder et gyldig token for en hvilken som helst pid uten autentisering. Stateless token-kjerne og Key Vault-/JWKS-infrastruktur er gjenbrukbar; hele autentiserings- og sikkerhetslaget mangler.

Denne issuen sporer herdingen av IdProvider slik at den oppfyller sikkerhetskravene i #1409. Den lastbærende invarianten (kun syntetisk Tenor-pid autentiseres, fail-closed) håndheves uansett også i altinn-authentication via RequireSyntheticPid (se #1409) — IdP-sjekken er forsvar i dybden.

Kodepekere under refererer TheTechArch/IdProvider på commit c848fbd.

Mål / akseptansekriterier

  • AK1: Ingen token utstedes uten korrekt delt tilgangspassord (ett secret som gir rett til å bruke Test-IDP — ingen brukernavn/per-bruker-passord, ingen Bridge siuser)
  • AK2: Delt passord verifiseres med konstant-tid-sammenligning; pid mottas kun via POST-body (aldri query/redirect/URL)
  • AK3: Kun velformet syntetisk Tenor-fnr (måned ∈ 81–92, gyldig mod11) slipper gjennom — fail-closed; alt annet access_denied
  • AK4: Test-IDP kan kun nås når klient er allowlistet og kall kom via PAR og acr=urn:altinn:acr:test-idp og scope=altinn:testauth og prompt=login
  • AK5: PKCE (S256) håndheves; private_key_jwt valideres på /token; autorisasjonskode er engangsbruk
  • AK6: Korrekt OIDC-discovery (issuer/endepunkter/JWKS) slik at altinn-authentication kan validere tokens
  • AK7: Feature flag som slår hele Test-IDP av umiddelbart; metrikker/strukturert PII-fri logging/alarmer på plass
  • AK8: Et ordinært fnr kan aldri ende opp i et utstedt token (verifisert med test)

Gap-analyse og oppgaver

Alvorsgrad: 🔴 Kritisk · 🟠 Høy · 🟡 Medium · ⚪ Lav

1. Autentisering (delt tilgangspassord — ikke per-bruker)

Modell: ett delt tilgangspassord gir rett til å bruke Test-IDP. Identiteten er en inn-parameter — oppgi gyldig Tenor-fnr. Ingen brukernavn, ingen per-bruker-passord, ingen Bridge siuser. Sprengvidde er begrenset av Tenor-gaten (kun syntetiske identiteter) + kjedet opt-in (allowlistet private_key_jwt-klient via PAR). Se #1409.

### Autentisering
- [x] 🔴 Fjern `?pid=`-snarveien som utsteder kode uten interaksjon (AuthorizeController GET) — pid→token-orakel  *(gjort, fase 1)*
- [ ] 🔴 Innfør delt-passord-validering: hent secret fra Key Vault; **konstant-tid-sammenligning**; aldri logg/ekko passordet
- [ ] 🔴 `pid` mottas kun via POST-body (aldri query/redirect/URL); fail-closed Tenor-sjekk før kode utstedes  *(Tenor-gate gjort i fase 1; POST-only gjenstår)*
- [ ] 🟠 Rate-limit + lockout på delt-passord-sjekken (ASP.NET rate limiting; eneste brute-force-mål siden ett delt secret)
- [ ] 🟠 Per-miljø secret; harness henter fra CI-secret-store, helst OIDC workload-identity-federasjon (ingen langlivet secret lagret)

2. Testbruker-identifikasjon (Tenor) — lastbærende invariant

### Tenor-gate
- [x] 🔴 Implementer `IsSyntheticTenorPid` (11 siffer, måned ∈ 81–92, normaliser D-nummer dag−40, mod11) og avvis ikke-syntetisk med `access_denied` FØR kode utstedes  *(gjort, fase 1 — `NorwegianIdentityNumber` + 15 enhetstester)*
- [ ] 🟠 Bekreft at `altinn-authentication` har tilsvarende `RequireSyntheticPid` (#1409) — IdP-sjekken er defence-in-depth, ikke eneste kontroll

3. Eksplisitt opt-in (kjedet routing)

### Opt-in
- [ ] 🟠 Klient-allowlist på `client_id` (ellers `unauthorized_client`)
- [ ] 🟠 Innfør `POST /par` (stateless: signert+kryptert `request_uri`-JWT); `/authorize` må kreve `request_uri` og avvise rå parametre
- [ ] 🟠 Krev `acr` inneholder `urn:altinn:acr:test-idp` (i dag bare ekko til token)
- [ ] 🟠 Krev `scope` inneholder `altinn:testauth`
- [ ] 🟡 Håndhev `prompt=login`

4. PKCE og klientautentisering på /token

### PKCE / client auth
- [ ] 🟠 Bind `code_challenge` inn i kode-JWT; verifiser `S256(code_verifier)==code_challenge` på `/token` (i dag ignoreres `code_verifier` helt), ellers `invalid_grant`
- [ ] 🟠 Valider `private_key_jwt` (iss/sub/aud/exp/jti + signatur mot Altinns registrerte JWKS)
- [ ] 🟠 Engangsbruk av autorisasjonskode (efemert konsumert-`jti`-sett; in-memory ved én instans, distribuert kun ved flere replicas)

5. Token-semantikk og output

### Token
- [ ] 🟡 Skill id_token og access_token (i dag returneres identisk token for begge; `refresh_token` er hardkodet "ADFSFDSFSDFDSFDSF")
- [ ] 🟡 Sett `amr=["pwd"]` (i dag hardkodet `["bankid"]`)
- [ ] 🟡 `acr=urn:altinn:acr:test-idp`; ingen `test=true`-claim (syntetisk fnr er markøren)
- [ ] 🟡 Bind og ekko `nonce` korrekt

6. Discovery / JWKS-korrekthet (OIDC-interop)

### Discovery
- [ ] 🟠 Én kanonisk `issuer` som matcher discovery `Issuer` (i dag avviker `IssCode`/`IssToken` fra discovery-issuer → Altinn token-validering feiler)
- [ ] 🟡 `AuthorizationEndpoint`/`TokenEndpoint` peker på faktiske ruter (`/Authorize`, `/token`), ikke base-URL
- [ ] 🟡 Legg til `pushed_authorization_request_endpoint`, `code_challenge_methods_supported=["S256"]`, `response_types_supported=["code"]` (i dag `["token"]`)
- [ ] 🟡 Fiks JWKS-path casing-mismatch (annonsert `/api/v1/OpenId/...` vs rute `/api/v1/openid/...`)

7. Observability og konfig

### Ops
- [x] 🟠 Feature flag (`TestIdpEnabled`, default av) som kortslutter alle endepunkter når av  *(gjort, fase 1)*
- [ ] 🟠 Metrikker (authorize-forsøk, denied-non-synthetic, shared-password-failures) + strukturert logging UTEN `pid`/passord + alarmer
- [ ] 🟠 Konfigseksjon for allowlist/PAR/acr/scope + delt-passord-secret i Key Vault
- [ ] 🟠 Flytt hardkodet App Insights connection string ut av `appsettings.json` og rotér lekket ingestion-nøkkel

8. Øvrige defekter / opprydning

### Cleanup
- [ ] ⚪ Fiks `Response_mode = acr_values` copy-paste-bug i AuthorizeController
- [ ] 🟡 Refaktorer `IToken`-laget (auth-beslutning ligger i `GetAuthorizationCode`) — splitt i auth/validerings-tjeneste
- [ ] ⚪ Fjern ADAL/`Microsoft.Azure.KeyVault` (deprecated) — behold kun `Azure.Security.KeyVault`
- [ ] ⚪ Erstatt `Views/Authorize/Index.cshtml` (eksponerer alle OIDC-parametre som redigerbare felt) med kun `{ delt_passord, tenor_fnr }`

Beholdes (allerede tilstrekkelig)

Anbefalt rekkefølge

  1. Stopp blødningen 🔴: fjern ?pid= (1), Tenor fail-closed gate (2), feature flag default av (7) — ✅ gjort (fase 1)
  2. Delt-passord-auth 🔴: Key Vault-secret, konstant-tid-sammenligning, POST-only, rate-limit/lockout ← fase 2 (neste)
  3. Opt-in-kjede 🟠: allowlist + acr + scope + prompt, deretter PAR
  4. Token-integritet 🟠: PKCE bind+verifiser, engangskode, private_key_jwt
  5. Interop 🟡: discovery/issuer/JWKS; separate id/access tokens
  6. Ops 🟠: metrikker/logging/alarmer, konfig + secret-hygiene

Åpne spørsmål (blokkerer design)

  • Er syntetiske Tenor-personer tilgjengelige via Bridge siuser? Bortfalt — delt-passord-modell uten Bridge/per-bruker-kredential (besluttet; se Implementer Test-ID-provider (upstream) for automatiserte testkjøringer #1409)
  • Skal nedstrøms acr være urn:altinn:acr:test-idp eller speile et ID-porten-nivå? (AuthenticationHelper.GetAuthenticationLevelForIdPorten i altinn-authentication må akseptere verdien)
  • Én IdP-instans (in-memory engangskode ok) eller HA-replicas (trenger distribuert dedupe)?

Trusselmodellering

### Trusselmodellering
- [ ] Åpner denne endringen for mulige nye trusler eller misbruk? (Test-IDP i prod — vurder pid-orakel, replay, kompromittert IdP som hevder reelt fnr)

Relatert: #1409

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions