Authorization Flow

Every request to Kaapana, whether a browser loading the landing page or a script calling an API, passes through the same chain of components before it reaches a backend service. This page explains how that chain authenticates a user, decides what they are allowed to do, and where the current design still has rough edges.

Authentication vs. authorization

Authentication identifies who is making a request; authorization decides what they are allowed to do with it. Authentication has to happen first – an authorization decision is meaningless without an identity to evaluate it against.

Kaapana’s authentication is standardized on OAuth2 and its identity layer, OpenID Connect (OIDC): a client proves it acts on behalf of a user to an Identity Provider (IdP), which returns a JSON Web Token (JWT) describing that user (claims such as username, roles, and scopes).

The players

  • Keycloak – the Identity Provider. Issues and validates tokens, and (see Keycloak user groups) maps users onto the user, project-manager and admin roles that the rest of the platform authorizes against.

  • OAuth2-Proxy – sits in front of the platform as the browser-facing login gate; it drives the Authorization Code Grant against Keycloak and turns a successful login into a session cookie.

  • Traefik – the internal reverse proxy and ingress in front of every cluster service.

Current flow

sequenceDiagram participant B as Browser participant O as OAuth2-Proxy participant T as Traefik participant K as Keycloak (IdP) participant A as AII (Access Information Interface) participant F as Auth Backend participant P as OPA participant S as Backend Service alt User requests HTTP B->>T: HTTP Request T-->>B: Redirect to HTTPS end B->>O: HTTPS Request alt Authorization Code Grant (first visit) O-->>B: Redirect to Keycloak Login B->>O: Login at Keycloak (user credentials) O->>T: Proxy to upstream T->>K: Proxy to Keycloak K->>A: Fetch user attributes for claim enrichment A-->>K: User info / access metadata K-->>B: Authorization code + redirect B->>O: Authorization code O->>K: Token exchange K-->>O: Access token / claims O-->>B: Set session cookie end B->>O: Request with session cookie + project cookie O->>T: Forward + x-forwarded headers T->>F: ForwardAuth check (headers, cookies, path) alt Project cookie present F->>A: Fetch project resource (Project ID) A-->>F: Project details (JSON) end F->>P: Evaluate policy (user, project, resource) alt denied P-->>F: Deny F-->>T: 403 T-->>O: 403 Forbidden O-->>B: 403 else allowed P-->>F: Allow F-->>T: 200 OK + Project header T->>S: Forward request + Project header S-->>T: Response T-->>O: Response O-->>B: Response end

Four things happen, in order, on every authenticated request:

  1. Authentication at the edge. OAuth2-Proxy terminates the Authorization Code Grant with Keycloak and reduces the result to a session cookie – everything after this step works with the cookie, not with Keycloak directly.

  2. Claim enrichment. During login, Keycloak calls the Access Information Interface (AII) to attach project- and role-related attributes to the token as claims, rather than each backend service looking them up independently.

  3. Policy enforcement at Traefik. Traefik acts as the Policy Enforcement Point (PEP): it forwards every request to an Auth Backend, which decodes the token, optionally fetches project details from the AII if a project cookie is set, and asks the Open Policy Agent (OPA) – the Policy Decision Point (PDP) – for an allow/deny decision.

  4. Header-based trust downstream. Once allowed, Traefik attaches the resolved project (and other claims) as headers on the forwarded request; backend services trust these headers rather than re-deriving them.

Important

The web interface – and any authorization decision that depends on a project – only works correctly once a project is selected, because the project cookie is what triggers step 3’s project-scoped policy evaluation.

Known limitations of this design

  • Runtime coupling. If the AII, Auth Backend, or OPA is unavailable, the request chain breaks – there is no fallback path, and the extra hops add latency to every request.

  • Design-time coupling. Keycloak depends on the AII during login; the Auth Backend depends on both the AII and a client-supplied project cookie; backend services depend on a header set by the Auth Backend. A change to any one link can silently break the others.

  • Scattered authorization data. Project-to-data mappings exist in more than one place (kaapana-backend, the AII, the DicomWebFilter), so there is more than one place that can disagree about who owns what.

  • No standard pattern for in-cluster, service-to-service calls – today’s design is built around a browser session, not a service calling another service without a human in the loop.

Future Direction

A proposal for hardening authentication (independent of the authorization redesign, which is still open) narrows the platform to four OAuth2 grants, chosen by how a client is acting on a user’s behalf, rather than leaving every grant type enabled:

Scenario

Example

Grant

Browser login

Logging in to the landing page

Authorization Code Grant

Client acting for a user, with the user present

Dev environments, third-party clients

Device Authorization Grant (already used today by programmatic API access)

Backend-to-backend

Init-jobs, service DAGs

Client Credentials Grant

Client acting for a user, without the user present

processing-containers

Refresh Token Grant / Client Credentials Grant with OpenID offline access

The Resource Owner Password Credentials Grant and the Implicit Grant are proposed for removal – both are deprecated in the OAuth 2.1 draft and are a needless risk once the grants above cover every real scenario. Authorization itself – untangling the coupling described above – is explicitly out of scope for this proposal and remains an open design problem.