Gå til indholdet

Integrationsguide

Foretrækker du alt på én side? Se single-page versionen — komprimeret med de vigtigste kode-eksempler.

Kom i gang

Før integration skal følgende være på plads:

Forudsætninger

  1. Aftale med MedCom om Keycloak-klient og adgang til VDX Auth API. Se adgangsvejledning og kontakt vdx@medcom.dk.
  2. Brugeren skal være kendt i VDX (lokal bruger i databasen, eller external bruger via organisationens IdP).
  3. JWT audience i access token skal matche det forventede audience for miljøet (stage default: organisation-audience).
  4. CORS og host: Din webapps origin skal whitelistes hos MedCom, hvis browseren kalder API'et direkte (mønster B i Integrationsmønstre).

Miljøer

Miljø Auth API base URL Keycloak discovery (stage)
Stage https://mgmtauthapi.vconf-stage.dk https://login.vconf-stage.dk/auth/realms/broker/.well-known/openid-configuration
Produktion https://mgmtauthapi.vconf.dk https://login.vconf.dk/auth/realms/broker/.well-known/openid-configuration

Bekræft den konkrete produktions-URL og Keycloak-konfiguration hos MedCom.

Autentificering

Flow

  1. Log brugeren ind i din webapp via Authorization Code Flow med PKCE mod VDX Keycloak.
  2. Modtag et access token fra Keycloak efter succesfuldt login.
  3. Send access token i alle kald til VDX Auth API:
Authorization: Bearer DIT_ACCESS_TOKEN
  1. Forny token via dit OIDC-biblioteks refresh-logik, når access token udløber.

Keycloak OpenID Discovery

Stage discovery URL:

https://login.vconf-stage.dk/auth/realms/broker/.well-known/openid-configuration

Discovery-endpointet returnerer alle relevante OAuth2-URLs (authorization_endpoint, token_endpoint, end_session_endpoint, jwks_uri). Brug det som single source of truth — undgå at hard-code endpoints i din klient.

Applikationsidentitet

API'et identificerer kaldende klient via JWT-claim azp (authorized party). Mangler claim, logges "unknown". Feltet application i /v1/user-responsen afspejler typisk samme værdi.

Valgfri header ved autorisationsskift

Header Beskrivelse
X-VDX-AuthorizationId Heltal > 0 — skifter effektiv gruppe til en autorisation brugeren ejer. Se Autorisationsskift.

Se også JWT-claims, Login-flow og Fejlkoder.

Integrationsmønstre

API'et understøtter kun bruger-bundne JWT'er (Authorization Code + PKCE). "Service-to-service" handler her om hvor i din arkitektur kaldet udføres, ikke om Client Credentials. Vælg ét af to mønstre — mønster A er anbefalet for de fleste server-side webapps:

A) Backend-til-API (anbefalet)

Din webapps backend kalder VDX Auth API på vegne af brugeren med dennes access token:

  1. Brugeren logger ind via Keycloak (PKCE) mod din webapp.
  2. Access og refresh tokens gemmes server-side (fx i krypteret session/cookie).
  3. Backend kalder API'et med Authorization: Bearer <brugerens access_token> via en gen-anvendelig HTTP-klient (timeout 10–30 sek.).
  4. Brugeren ser aldrig access token i browseren.

Fordele: Ingen tokens i browser, ingen CORS-konfiguration, ingen XSS-risiko for tokens, centraliseret fejlhåndtering og caching server-side.

Se Login-flow for den anbefalede sekvens og Best practices for caching.

B) Browser-til-API (direkte fra frontend)

SPA-style frontends kan kalde VDX Auth API direkte, hvis:

  1. Din webapps origin er whitelisted hos MedCom.
  2. Frontend håndterer Keycloak login (fx via keycloak-js) og opbevarer tokens (typisk i memory).
  3. Hvert fetch-kald sætter både Authorization og evt. X-VDX-AuthorizationId headers manuelt.

Ulemper: Tokens skal håndteres sikkert i browseren, CORS skal aftales pr. miljø, og du bærer selv ansvaret for token-refresh.

Hvad er ikke understøttet

  • OAuth2 Client Credentials Flow (egen system-identitet uden bruger) → afvises med 401. API'et kræver bruger-claims (dk:medcom:email, dk:medcom:organisation_id m.fl.) i token.
  • Token-share mellem apps: Hver app har eget Keycloak-klient-ID (azp); access tokens er bundet til klienten der udstedte dem.

Se også Flow-diagrammer og Kodeeksempler.

Login-flow

Et komplet login-flow består af OIDC mod Keycloak efterfulgt af et obligatorisk profil-kald til VDX Auth API. API'et er single source of truth for organisationskontekst — undlad at udlede organisationGroup, organisationName mv. fra JWT-claims selv.

Anbefalet sekvens (mønster A — backend-til-API)

  1. Initier login: Generér PKCE code_verifier + code_challenge, gem code_verifier og state i en krypteret, kortlivet cookie (eller server-side cache). Redirect brugeren til Keycloak authorization_endpoint med response_type=code, code_challenge_method=S256, scope=openid.
  2. Callback: Modtag code + state fra Keycloak. Valider state mod værdien gemt i trin 1.
  3. Token-veksling: POST til Keycloak token_endpoint med grant_type=authorization_code, code, code_verifier, redirect_uri, client_id. Modtag access_token, refresh_token, expires_in.
  4. Hent brugerprofil (obligatorisk): Kald GET /v1/user straks efter token-veksling med det nye access token. Hvis dette kald fejler (401, 403), skal login afvises — uden et succesfuldt profil-kald har du ikke en gyldig VDX-kontekst. Se GET /v1/user.
  5. Rolle-validering: Læs roles-feltet fra responsen og afvis brugere uden krævede roller (fx vdx-admin, management-admin, management-readonly). Returnér til login-siden med fejlbesked.
  6. Persister tokens og identitet server-side:
  7. Access token + expires_at i session.
  8. Refresh token i en krypteret cookie (HttpOnly, Secure, SameSite=Lax).
  9. Hold authentication-cookien minimal: gem kun identitet + refresh token. Brugerprofilen hentes live fra /v1/user på hvert request (se Best practices).
  10. Redirect til en relevant landing-side i din app (fx den side der viser data for brugerens organisationGroup).

Token-fornyelse

  • Tjek expires_at ved hvert request; hvis access token udløber inden for ~60 sekunder, kald Keycloak token_endpoint med grant_type=refresh_token.
  • Gem rotated refresh token tilbage i persistent storage (Keycloak roterer som standard).
  • Hvis refresh fejler → ryd session, log brugeren ud, redirect til login.

Se Kodeeksempler: Token-refresh for Python/C# snippets.

Genopbygning efter pod-skift / app-restart

Når in-memory session er væk men auth-cookien stadig holder:

  1. Læs refresh_token fra cookie (eller anden persistent storage).
  2. Forny tokens via Keycloak token_endpoint med grant_type=refresh_token.
  3. Gem nye tokens i (ny) session.
  4. Næste request rammer /v1/user og rebuilder brugerprofilen i memory.

Sekvens for mønster B (browser-til-API)

Stort set samme flow, men trin 1–3 udføres af et OIDC-bibliotek i frontenden (fx keycloak-js), trin 4 er et HTTP-kald direkte mod v1/user, og trin 6 reduceres til at gemme tokens i memory + håndtere token-refresh client-side.

Se også Flow-diagrammer, Autentificering og Best practices.

Autorisationsskift

Headeren X-VDX-AuthorizationId styrer hvilken gruppe brugeren arbejder i, når brugeren har management-autorisationer på andre grupper end egen organisation.

Header-værdi Effekt
Mangler / tom / 0 Basis-kontekst (brugerens egen organisation)
Gyldigt id > 0 Effektiv gruppe sættes til autorisationens gruppe

Vælg autorisation

  1. Hent listen med GET /v1/user/authorizations og vis den i UI (fx modal/dropdown). Se endpoint-dokumentation.
  2. Brugeren vælger en post (typisk identificeret ved groupId).
  3. Valider ved at slå id op i listen fra trin 1 (sikrer at brugeren ejer autorisationen).
  4. Verificér mod API'et: Kald GET /v1/user med X-VDX-AuthorizationId: {id}. Hvis kaldet returnerer 403, blev autorisationen afvist — vis fejl uden at skifte kontekst.
  5. Persister valget: Gem {id, groupId, groupName, roles} i cookie, session eller anden persistent storage så det overlever pod-skift.
  6. Invalidér cache for brugerprofilen (eller stol på cache-nøgle der inkluderer autorisations-ID).
  7. Send headeren med på alle efterfølgende kald til /v1/user, /v1/user/group, /v1/user/group/parents.

Eksempel på minimalt persistens-objekt:

{
  "id": 42,
  "groupId": 67890,
  "groupName": "Region Nord",
  "roles": "management-admin"
}

Slip autorisation

  1. Fjern persistens: Slet det gemte autorisations-objekt fra cookie/session/state.
  2. Valgfrit: Kald GET /v1/user uden X-VDX-AuthorizationId (eller med 0) for at sikre basis-kontekst i API'ets session-tracking.
  3. Invalidér cache: Næste /v1/user-kald uden header får frisk respons med isAuthorization = false.
  4. Redirect brugeren til standard-landing (fx egen organisations side).

Profil i autorisations-kontekst

Efter et succesfuldt skift afspejler /v1/user-svaret den nye kontekst:

  • isAuthorization = true
  • authorizationId = det valgte id
  • organisationGroup = autorisationens gruppe-id
  • organisationName = autorisationsgruppens navn

Bemærk: /v1/user/authorizations filtreres ikke af headeren — listen er altid den fulde mængde autorisationer brugeren ejer.

Fejl ved autorisationsskift

HTTP Besked Handling
400 Invalid X-VDX-AuthorizationId Afvis skift, behold eksisterende kontekst
403 Authorization does not belong to user Afvis skift, behold eksisterende kontekst

I begge tilfælde bør backend ikke logge brugeren ud — kun afvise det nye skift.

Se også Kodeeksempler, UserProfile-modellen og Fejlkoder.

Best practices

VDX Auth API er autoritativ for organisationskontekst, gruppe-ID, roller, autorisationsstatus og external user-data. Læg din apps logik op om dette princip.

Hvad du skal bruge fra /v1/user-responsen

Felt Brug
organisationId, organisationName Vis brugerens organisation i UI, beslut adgang til organisations-specifik data
organisationGroup Den effektive gruppe brugeren arbejder i — brug i alle gruppe-relaterede queries
organisationGroupOverride Sat hvis external bruger er flyttet manuelt; afspejlet allerede i organisationGroup
isAuthorization, authorizationId Vis indikator i UI når brugeren arbejder i autorisations-kontekst
hasAvailableAuthorizations Skjul/vis menu-indgang til autorisationsvælger
roles Komma-separeret rolle-liste — brug til adgangstjek i UI og backend
userType local vs. external
sessionId, timestamp Spor/diagnose; medsend i fejlrapporter

Se UserProfile-modellen for komplet felt-reference.

Hvad du ikke skal gøre

  • Ikke udlede organisationskontekst fra JWT-claims direkte. Claims er input; API'et resolver mod databasen, anvender autorisationsskift, external user-override og auto-gruppe-træ.
  • Ikke cache /v1/user-svar længe. Brugerens kontekst kan skifte, og deaktivering i VDX skal slå igennem hurtigt.
  • Ikke behandle 403 fra /v1/user som "retry senere" — det betyder deaktiveret lokal bruger eller ugyldig autorisation. Log brugeren ud.

Anbefalet caching-strategi (mønster A)

  • Kald /v1/user én gang per request — fx via HTTP-middleware/filter før din app-logik.
  • Cache resultatet in-memory med nøgle user_context:{email}:{authorizationId|"none"} og TTL ~30 sekunder.
  • Inklusion af autorisations-ID i nøglen giver automatisk nyt cache-segment ved autorisationsskift.
  • Læg profilen i request-scope storage, så samme request genbruger én reference.
  • Ved fejl (tomt svar eller 403/401) → ryd session, log brugeren ud, redirect til login.

Strikt sikkerhed ved fejl (fail closed)

Status Reaktion
200 Brug data, opdatér cache
401 Token udløbet/ugyldigt → refresh-forsøg, ellers logud
403 Bruger deaktiveret eller autorisation ugyldig → logud + brugerbesked
500 / netværksfejl Logud + besked "Kunne ikke validere session"

Felter til persistent state

Det er kun nødvendigt at gemme to ting persistent:

  1. Refresh token — så session kan genopbygges efter app-restart.
  2. Valgt autorisations-ID — så autorisationsskift overlever pod-skift.

Resten (organisationGroup, organisationName, roles …) hentes live fra API'et via caching ovenfor.

Se også Login-flow og Fejlkoder.