Skip to main content

Token Exchange

Team Tilgangsstyring tilbyr en tjeneste på SKIP for token exchange. Den baserer seg blant annet på komponenter utviklet av applikasjonsplattformen i Nav, Nais:

  • Tokendings, en token exchange-server
  • Jwker, en Kubernetes-operator for klientregistrering i Tokendings
  • Texas, en sidecar-tjeneste for veksling av tokens mot Tokendings og validering av tokens i applikasjonen din

Nødvendig oppsett

Enkleste måte å ta i bruk Token Exchange på er ved å spesifisere "texas" som tilgjengelig sidecar-tjeneste, samt bruke SecurityConfig-ressursen for relevante tokenx-oppsett:

Eksempel på SecurityConfig for Token Exchange
apiVersion: security.skip.kartverket.no/v1alpha
kind: SecurityConfig
spec:
applicationRef: my-app
tokenx:
enabled: true
accessPolicy:
...
...

Ved å sette enabled: true vil SecurityConfig opprette en klientregistrering i Tokendings for applikasjonen din, inkludert nødvendige nettverksåpninger.

Innhold i accessPolicy-feltet vil avhenge av hvilken rolle den aktuelle applikasjonen har:

  • Sluttbrukertjenester: utelat accessPolicy-feltet, ettersom ingen skal kunne veksle tokens med din applikasjon som audience
  • Underliggende tjenester: definer accessPolicy-feltet, enten ved å arve fra applikasjonens nettverksregler ved å sette inheritInboundRules: true, eller ved å spesifisere eksplisitte klienter med clients-feltet.

Det er mulig, men ikke anbefalt, å bruke Tokendings uten bruk av Texas-sidecar og/eller SecurityConfig-ressurtypen. Ta kontakt med Team Tilgangsstyring dersom du mener dette er aktuelt for deg.

Request

Token exchange utgøres ved å gjøre et HTTP-kall som følger:

POST <TEXAS_URL>/api/v1/token/exchange
Content-Type: application/json

{
"identity_provider": "tokenx",
"user_token": "<sluttbruker-token>",
"target": "<cluster>:<namespace>:<app>"
}

gitt at target-applikasjonen har en klientregistrering i Tokendings med din applikasjon som godkjent klient.

Respons

Responsen vil være på json-format. Token ligger i access_token-feltet.

{
"access_token": "<access-token>",
"expires_in": <antall sekunder (int)>,
"token_type": "Bearer"
}

Validere vekslede tokens

Innvekslede tokens, dvs. tokens utstedt av Tokendings, kan valideres med Texas på lik linje med andre tokens. Se dokumentasjon for validering av tokens. I tillegg må du validere at aud-claimet tilsvarer din applikasjon, og at client-claimet tilsvarer applikasjonen som utfører kallet mot deg (f.eks ved å sammenlikne med SPIFFE-identitet i X-Forwarded-Client-Cert-headeren).

Direkte testing av tokenx-beskyttede applikasjoner

Det kan være tungvint å teste underliggende applikasjoner dersom disse kun kan kalles fra fremforliggende sluttbrukertjenester. Derfor tilbyr vi en web-tjeneste for å utføre token exchange med et sluttbruker-token.

For at tjenesten skal kunne veksle tokens med din applikasjon som audience må du legge til tokenx-token-generator-appen som en gyldig klient i SecurityConfig-ressursen:

Eksempel på SecurityConfig med tokenx-token-generator som gyldig klient
apiVersion: security.skip.kartverket.no/v1alpha1
kind: SecurityConfig
spec:
applicationRef: my-app
tokenx:
enabled: true
accessPolicy:
clients:
- application: tokenx-token-generator-backend
namespaces: tokenx-token-generator
...

Husk at denne klienten kun bør godkjennes i dev-miljøet - aldri i produksjon!

Hvilke brukere som kan veksle tokens med hvilke audiences er begrenset basert på Entra ID-grupper. Kontakt Team Tilgangsstyring via #gen-tilgangsstyring på Slack for å få gi ditt team tillatelse til et gitt audience.

Lokal kjøring i Docker

For å kjøre de nødvendige komponentene ifbm. token exchange lokalt anbefaler vi bruk av Docker. Et eksempel på dette finner du her. Istedenfor Tokendings, som er token exchange-serveren vi kjører på SKIP, benyttes mock-oauth2-server. Med litt konfigurasjon kan vi få mock-oauth2-server til å returnere ganske like tokens som Tokendings gjør på SKIP. Dersom du skal validere tokenx-utstedte tokens lokalt bør du sette well_known_url / jwks_uri i appen som skal validere til å gå mot mock-oauth.

Eksempel på docker-compose.yml ved veksling av ID-porten-tokens (idp=https://test.idporten.no):

services:
mock-oauth2-server:
image: ghcr.io/navikt/mock-oauth2-server:latest
ports:
- "8091:8080"
environment:
SERVER_PORT: 8080
JSON_CONFIG: | # config som sikrer nokså like returnerte tokens som på SKIP
{ "interactiveLogin": false, "httpServer": "NettyWrapper", "tokenCallbacks": [ { "issuerId": "default", "tokenExpiry": 3600, "requestMappings": [ { "requestParam": "grant_type", "match": "urn:ietf:params:oauth:grant-type:token-exchange", "claims": { "aud": "$${audience}", "client_id": "$${client_id}", "iss": "http://tokendings.tokenx-api:8080", "idp": "https://test.idporten.no" } } ] } ] }

texas:
image: ghcr.io/nais/texas:latest
ports:
- "3000:3000"
environment:
BIND_ADDRESS: "0.0.0.0:3000"
TOKEN_X_ENABLED: true
TOKEN_X_CLIENT_ID: "ditt-cluster:ditt-namespace:ditt-applikasjonsnavn"
TOKEN_X_WELL_KNOWN_URL: "http://mock-oauth2-server:8080/default/.well-known/openid-configuration"
TOKEN_X_PRIVATE_JWK: '{"p":"_LNnIjBshCrFuxtjUC2KKzg_NTVv26UZh5j12_9r5mYTxb8yW047jOYFEGvIdMkTRLGOBig6fLWzgd62lnLainzV35J6K6zr4jQfTldLondlkldMR6nQrp1KfnNUuRbKvzpNKkhl12-f1l91l0tCx3s4blztvWgdzN2xBfvWV68","kty":"RSA","q":"9MIWsbIA3WjiR_Ful5FM8NCgb6JdS2D6ySHVepoNI-iAPilcltF_J2orjfLqAxeztTskPi45wtF_-eV4GIYSzvMo-gFiXLMrvEa7WaWizMi_7Bu9tEk3m_f3IDLN9lwULYoebkDbiXx6GOiuj0VkuKz8ckYFNKLCMP9QRLFff-0","d":"J6UX848X8tNz-09PFvcFDUVqak32GXzoPjnuDjBsxNUvG7LxenLmM_i8tvYl0EW9Ztn4AiCqJUoHw5cX3jz_mSqGl7ciaDedpKm_AetcZwHiEuT1EpSKRPMmOMQSqcJqXrdbbWB8gdUrnTKZIlJCfj7yqgT16ypC43TnwjA0UwxhG5pHaYjKI3pPdoHg2BzA-iubHjVn15Sz7-pnjBmeGDbEFa7ADY-1yPHCmqqvPKTNhoCNW6RpG34Id9hXslPa3X-7pAhJrDBd0_NPlktSA2rUkifYiZURhHR5ijhe0v3uw6kYP8f_foVm_C8O1ExkxXh9Dg8KDZ89dbsSOtBc0Q","e":"AQAB","use":"sig","kid":"l7C_WJgbZ_6e59vPrFETAehX7Dsp7fIyvSV4XhotsGs","qi":"cQFN5q5WhYkzgd1RS0rGqvpX1AkmZMrLv2MW04gSfu0dDwpbsSAu8EUCQW9oA4pr6V7R9CBSu9kdN2iY5SR-hZvEad5nDKPV1F3TMQYv5KpRiS_0XhfV5PcolUJVO_4p3h8d-mo2hh1Sw2fairAKOzvnwJCQ6DFkiY7H1cqwA54","dp":"YTql9AGtvyy158gh7jeXcgmySEbHQzvDFulDr-IXIg8kjHGEbp0rTIs0Z50RA95aC5RFkRjpaBKBfvaySjDm5WIi6GLzntpp6B8l7H6qG1jVO_la4Df2kzjx8LVvY8fhOrKz_hDdHodUeKdCF3RdvWMr00ruLnJhBPJHqoW7cwE","alg":"RS256","dq":"IZA4AngRbEtEtG7kJn6zWVaSmZxfRMXwvgIYvy4-3Qy2AVA0tS3XTPVfMaD8_B2U9CY_CxPVseR-sysHc_12uNBZbycfcOzU84WTjXCMSZ7BysPnGMDtkkLHra-p1L29upz1HVNhh5H9QEswHM98R2LZX2ZAsn4bORLZ1AGqweU","n":"8ZqUp5Cs90XpNn8tJBdUUxdGH4bjqKjFj8lyB3x50RpTuECuwzX1NpVqyFENDiEtMja5fdmJl6SErjnhj6kbhcmfmFibANuG-0WlV5yMysdSbocd75C1JQbiPdpHdXrijmVFMfDnoZTQ-ErNsqqngTNkn5SXBcPenli6Cf9MTSchZuh_qFj_B7Fp3CWKehTiyBcLlNOIjYsXX8WQjZkWKGpQ23AWjZulngWRektLcRWuEKTWaRBtbAr3XAfSmcqTICrebaD3IMWKHDtvzHAt_pt4wnZ06clgeO2Wbc980usnpsF7g8k9p81RcbS4JEZmuuA9NCmOmbyADXwgA9_-Aw"}'

Testing lokalt og i CI/CD

For integrasjonstesting kan du kjøre de nødvendige kompoentene med testcontainers. Oppsettet her er identisk med lokal kjøring i Docker beskrevet over, med unntak av at texas og mock-oauth kjøres opp i testcontainers istedenfor med docker compose. Du finner et eksempel på oppsett i en Java-applikasjon her. Her ville du brukt variabelen texasUrl på samme måte som miljøvariabelen TEXAS_URL blir tilgjengelig for din applikasjon på SKIP.

Eksempel på å starte texas med testcontainers i Java:

var texasContainer = new GenericContainer<>(DockerImageName.parse("ghcr.io/nais/texas:latest"))
.withExposedPorts(3000)
.withEnv("BIND_ADDRESS", "0.0.0.0:3000")
.withEnv("TOKEN_X_ENABLED", "true")
.withEnv("TOKEN_X_CLIENT_ID", "mitt-applikasjonsnavn")
.withEnv("TOKEN_X_WELL_KNOWN_URL", "http://mock-oauth2-server:8080/default/.well-known/openid-configuration")
.withEnv("TOKEN_X_PRIVATE_JWK", """
{"p":"_LNnIjBshCrFuxtjUC2KKzg_NTVv26UZh5j12_9r5mYTxb8yW047jOYFEGvIdMkTRLGOBig6fLWzgd62lnLainzV35J6K6zr4jQfTldLondlkldMR6nQrp1KfnNUuRbKvzpNKkhl12-f1l91l0tCx3s4blztvWgdzN2xBfvWV68","kty":"RSA","q":"9MIWsbIA3WjiR_Ful5FM8NCgb6JdS2D6ySHVepoNI-iAPilcltF_J2orjfLqAxeztTskPi45wtF_-eV4GIYSzvMo-gFiXLMrvEa7WaWizMi_7Bu9tEk3m_f3IDLN9lwULYoebkDbiXx6GOiuj0VkuKz8ckYFNKLCMP9QRLFff-0","d":"J6UX848X8tNz-09PFvcFDUVqak32GXzoPjnuDjBsxNUvG7LxenLmM_i8tvYl0EW9Ztn4AiCqJUoHw5cX3jz_mSqGl7ciaDedpKm_AetcZwHiEuT1EpSKRPMmOMQSqcJqXrdbbWB8gdUrnTKZIlJCfj7yqgT16ypC43TnwjA0UwxhG5pHaYjKI3pPdoHg2BzA-iubHjVn15Sz7-pnjBmeGDbEFa7ADY-1yPHCmqqvPKTNhoCNW6RpG34Id9hXslPa3X-7pAhJrDBd0_NPlktSA2rUkifYiZURhHR5ijhe0v3uw6kYP8f_foVm_C8O1ExkxXh9Dg8KDZ89dbsSOtBc0Q","e":"AQAB","use":"sig","kid":"l7C_WJgbZ_6e59vPrFETAehX7Dsp7fIyvSV4XhotsGs","qi":"cQFN5q5WhYkzgd1RS0rGqvpX1AkmZMrLv2MW04gSfu0dDwpbsSAu8EUCQW9oA4pr6V7R9CBSu9kdN2iY5SR-hZvEad5nDKPV1F3TMQYv5KpRiS_0XhfV5PcolUJVO_4p3h8d-mo2hh1Sw2fairAKOzvnwJCQ6DFkiY7H1cqwA54","dp":"YTql9AGtvyy158gh7jeXcgmySEbHQzvDFulDr-IXIg8kjHGEbp0rTIs0Z50RA95aC5RFkRjpaBKBfvaySjDm5WIi6GLzntpp6B8l7H6qG1jVO_la4Df2kzjx8LVvY8fhOrKz_hDdHodUeKdCF3RdvWMr00ruLnJhBPJHqoW7cwE","alg":"RS256","dq":"IZA4AngRbEtEtG7kJn6zWVaSmZxfRMXwvgIYvy4-3Qy2AVA0tS3XTPVfMaD8_B2U9CY_CxPVseR-sysHc_12uNBZbycfcOzU84WTjXCMSZ7BysPnGMDtkkLHra-p1L29upz1HVNhh5H9QEswHM98R2LZX2ZAsn4bORLZ1AGqweU","n":"8ZqUp5Cs90XpNn8tJBdUUxdGH4bjqKjFj8lyB3x50RpTuECuwzX1NpVqyFENDiEtMja5fdmJl6SErjnhj6kbhcmfmFibANuG-0WlV5yMysdSbocd75C1JQbiPdpHdXrijmVFMfDnoZTQ-ErNsqqngTNkn5SXBcPenli6Cf9MTSchZuh_qFj_B7Fp3CWKehTiyBcLlNOIjYsXX8WQjZkWKGpQ23AWjZulngWRektLcRWuEKTWaRBtbAr3XAfSmcqTICrebaD3IMWKHDtvzHAt_pt4wnZ06clgeO2Wbc980usnpsF7g8k9p81RcbS4JEZmuuA9NCmOmbyADXwgA9_-Aw"}
""");

texasContainer.start();

texasUrl = String.format("http://%s:%d",
texasContainer.getHost(),
texasContainer.getMappedPort(3000));

Mock-oauth kan også brukes til å hente ut tokens til bruk ifbm. utførelse av token exchange.