Token exchange

Token Exchange i HelseID baserer seg på IETF-spesifikasjonen RFC 8693: OAuth 2.0 Token Exchange, som beskriver en protokoll for å hente ut tokens fra en STS for «impersonation» og «delegation».

I HelseID brukes denne spesifikasjonen når bakenforliggende tjenester, i hovedsak REST API-er, skal kalle andre tjenester på vegne av en autentisert person og/eller virksomhet.

Sentrale begreper

Token Exchange innfører noen nye begreper:

subject_token: Et Access-token utstedt av HelseID som kan inneholde claims om en autentisert person og klientens virksomhet.

subject client: En klient som har mottatt et subject_token fra HelseID.

actor client: En klient som ønsker å veksle inn et subject_token ved bruk av token exchange. Hensikten med dette er å få tilbake et nytt Access-token som kan brukes til å kalle en annen tjeneste. Actor client vil typisk være et API som vil hente data fra et annet API som også er sikret med HelseID.

Token exchange-flyten

Bruken av Token exchange i HelseID skjer med en flyt der en subject client først ber om et Access-token fra HelseID, og deretter bruker dette tokenet mot et API som har en actor client innebygd. Det er actor client som gjør selve Token-exchange-kallet, men flyten gjennom hele kjeden kan være litt mer kompleks.

Overordnet flyt

  1. Subject client forespør autentisering av bruker og ber om tilgang til API 1
  2. HelseID utsteder Access-token (AT#1, subject_token) med tilgang til API 1
  3. Subject client gjør et kall til API 1, med AT#1 i Authorization-headeren
  4. API 1 trenger data fra API 2 og trenger dermed et Access-token for dette
  5. Actor client (API 1) gjør en Token Exchange-forespørsel mot Token-endepunktet på HelseID og ber om tilgang til API 2
    • AT#1 er subject_token i denne forespørselen
  6. HelseId utsteder et nytt Access-token (AT#2) og returnerer det til Actor client (API 1)
  7. Actor client (API 1) gjør et kall til API 2 med AT#2 i Authorization-headeren

Token exchange

Forespørsel til HelseID med kall til Token-endepunktet

For å få utstedt et Access-token med Token exchange-mekanismen, må Actor client bruke Token-endepunktet på samme vis som en vanlig HelseID-klient. Dog må enkelte spesialparametre brukes i kallet:

  • grant_type: skal alltid være urn:ietf:params:oauth:grant-type:token-exchange
  • subject_token: Access-tokenet som ble utstedt av HelseID og som skal utveksles
  • subject_token_type: skal alltid være urn:ietf:params:oauth:token-type:access_token

Et eksempel på en slik POST-forespørsel:

POST /connection/token

grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
scope=api1 api2&
subject_token=eyJhbGciOiJSUzI...lZ22kWJV4pHr8t&
subject_token_type=urn:ietf:params:oauth:token-type:access_token&
client_assertion=eyJhbGciOiJSUzI...lZ22kWJV4pHr8t&
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
client_id=3c349ff9-20ea-4026-bf8f-f2fb9ed1bb59&

Respons ved suksess

Hvis HelseID sender ut et Access-token, så er responsen lik den som er spesifsert i RFC8693 (Token Exchange).

Feilmeldinger fra Token-endepunktet

Når det oppstår en feil i kallet til Token-endepunktet, vil HelseID sende ut en feilmelding, med en mer detaljert beskrivelse. Tabellen under beskriver de vanligste feilmeldingene du kan få:

Feilmelding Feilbeskrivelse Tolkning av feil
invalid_request invalid subject_token - [detaljert informasjon] Denne feilen kan for eksempel komme av at subject-tokenet ikke er utstedt av HelseID, eller at levetiden på tokenet har har gått ut.
invalid_request not permitted Denne feilen oppstår hvis subject client ikke er konfigurert til å kunne bruke token exchange for actor client.
invalid_target invalid scopes requested Dette oppstår dersom actor client spør om scopes som går på tvers av API-ressurser.
invalid_request subject_token exchanged too many times (5) Dette oppstår dersom actor client forsøker å veksle inn et token som allerede er vekslet inn for mange ganger.
invalid_request The audience in the subject token and the client with client_id '[client_id]' have different configuration owners. Actor client en annen konfigurasjonseier enn aud (API-ressursen) i subject token.

Se også generelle feilmeldinger.

Utstedt Access-token

Ved vellykket Token Exchange vil actor client få utstedt et Access-token.

Følgende claims vil være endret i det nye tokenet:

  • aud: audience for API 2

Følgende claim identifiserer actor client:

  • act: Et sett av claims som identifiserer virksomhet og klient som opptrer på vegne av en autentisert person. act-claimet er bygd opp som følger:

    • iss: HelseID STS som har identifisert actor client
    • client_id: identifikator for actor client
    • helseid://claims/client/claims/orgnr_parent: organisasjonsnummer for hovedenhet
    • helseid://claims/client/claims/orgnr_child: organisasjonsnummer for underenhet

For kallkjeder der Token Exchange benyttes flere ganger, vil du få en nøstet struktur av act-claims. Den innerste actor er den eldste, og den ytterste actor er den nyeste - og altså den aktive actor for det aktuelle Access-tokenet.

Følgende claim identifiserer subject client:

  • helseid://claims/client/original_client_id: klient-ID fra det første subject_token i kjeden

Claims som blir kopiert fra subject_token:

  • Alle claims med prefiks helseid://
  • sub: HelseID-spesifikk identifikator for autentisert bruker
  • name: Navn på brukeren som ble autentisert
  • given_name: Fornavn på brukeren som ble autentisert
  • middle_name: Mellomnavn på brukeren som ble autentisert
  • family_name: Etternavn på brukeren som ble autentisert
  • sid: Sesjons-ID for den påloggede brukeren
  • idp: IDP som ble brukt i forbindelse med autentisering av bruker
  • amr: påloggingsmetode i IDP som ble brukt i forbindelse med autentisering av bruker
  • auth_time: tidspunktet da brukeren ble autentisert

⚠️  Konsekvenser av dette (for API-eiere):

* Claim som ikke finnes i det innkommende tokenet (`subject_token`), vil ikke være med i det nye tokenet, selv om API 2 har behov for denne informasjonen, for eksempel en pålogget bruker.

Eksempel på et Access-token fra Token exchange

{
  "alg": "RS256",
  "kid": "78667F90DC11BF04BD9467D1F9120C4A4340B4CF",
  "x5t": "eGZ_kNwRvwS9lGfR-RIMSkNAtM8",
  "typ": "JWT"
}.{
  "iss": "https://helseid-sts.test.nhn.no",
  "nbf": 1713888289,
  "iat": 1713888289,
  "exp": 1713888889,
  "aud": "nhn:helseid-public-samplecode",
  "cnf": {
    "jkt": "aoezvIe32RdFpTP4FIxwBYb6VGX0dp3ecvoVjFkdXQk"
  },
  "scope": [
    "nhn:helseid-public-samplecode/client-credentials"
  ],
  "amr": [
    "pwd"
  ],
  "client_id": "helseid-sample-token-exchange-actor-client",
  "helseid://claims/client/original_client_id": "helseid-sample-token-exchange-subject-client",
  "client_amr": "private_key_jwt",
  "helseid://claims/identity/pid": "11857998857",
  "helseid://claims/identity/security_level": "4",
  "name": "VIRKELIG KJELTRING",
  "jti": "25488FC67BAB3419DC6E70C273FBB871",
  "sid": "0FAB2BC0164BF60B39ECED460E2A56BA",
  "act": {
    "iss": "https://helseid-sts.test.nhn.no",
    "client_id": "helseid-sample-token-exchange-actor-client",
    "helseid://claims/client/claims/orgnr_parent": "999977774"
  },
  "helseid://claims/client/claims/orgnr_parent": "999977774",
  "oldsub": "LHcSgmD3Q/SO/0dLxXvhKBnh1ebeq61C3ndgEIkptbo=",
  "sub": "vJs8Xr2F58spTNEPHM/a07KdZtSBGLQN9EmHuBGLy/c=",
  "auth_time": 1713888282,
  "idp": "testidp-oidc",
  "helseid://claims/client/amr": "rsa_private_key",
  "helseid://claims/client/client_name": "helseid-sample-token-exchange-actor-client",
  "helseid://claims/client/client_tenancy": "single-tenant"
}.[Signature]