OZO FHIR implementation guide
0.1.0 - ci-build

OZO FHIR implementation guide - Local Development build (v0.1.0) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions

Validate Access Tokens and DPoP headers

Step 1: Parsing the request headers

The procedure for parsing the request headers is as follows:

  • Get the Authorization header from the request.
  • Parse the header value according to the following pattern: '{type} {access_token}'. Where type can either be Bearer or DPoP.
    • A regex for extracting the token could look like this: ^(Bearer|DPoP) (.+)$
  • If the type is Bearer, only the access_token should be extracted and validated.
  • If the type is DPoP, the DPoP header should be extracted and validated additionally to the access_token.

Step 2a: Validate the access_token

The access_token can be validated by sending a request to the introspection endpoint of the authorization server. The request should include the access_token as a parameter as part of a application/x-www-form-urlencoded request. The response will contain information about the token, such as its validity and the subject it represents.

Parameters:

  • baseUrl, from internal NUTS url: example https://nuts-node-int.example.com
  • access_token, the access token to validate from the request
export async function introspect(base_url: string, access_token: string) {
    const url = `${base_url}/internal/auth/v2/accesstoken/introspect`
    return await fetch(url, {
        method: "POST",
        body: 'token=' + encodeURIComponent(access_token),
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    }).then((data) => data.json())
}

Response:

{
  "active": true,
  "client_id": "https://nuts-node.example.com/oauth2/roland_test",
  "cnf": {
    "jkt": "fuu....GHQ"
  },
  "exp": 1733852948,
  "iat": 1733852048,
  "iss": "https://nuts-node.example.com/oauth2/ozo",
  "scope": "ozo"
}

Validation of the response

  • The active field should be true, mind that the response status will be 200 even if the token is not valid.
  • The scope field should match the use case of the token.
  • The iss must match the {base_url}/oauth2/{subject} where the base_url is the external URL of the NUTS node and the subject is the OZO system subject.
  • The client_id should match the expected client id.
  • If the response contains as value for cnf the field jkt, the value MUST be used for the DPoP header validation. If no DPoP header is present, an error MUST be thrown and the validation MUST fail.

Step 2a (Conditional): Validate the DPoP header

Parameters:

  • dpop_header, the DPoP header of the request.
  • thumbprint, the thumbprint from the introspection response at the field cnf.jkt.
  • access_token, the access_token from the Authorization header of the request.
  • request_url, the URL of the request.
  • request_method, the HTTP method from the request, such as GET or POST.
export async function valudate_dpop(base_url: string, access_token: string, dpop_header:string, cnfJkt: string, request_url: string, request_method: string) {
    const url = `${base_url}/internal/auth/v2/dpop/validate`
    const data = {
      "dpop_proof": dpop_header,
      "thumbprint": cnfJkt,
      "token": access_token,
      "url": request_url,
      "method": request_method
    }
    return await fetch(url, {
        method: "POST",
        body: JSON.stringify(data),
        headers: {
            'Content-Type': 'application/json'
        }
    }).then((data) => data.json())
}

Response

{
  "valid": true
}

Validation of the response

  • The active field should be true, mind that the response status will be 200 even if the token is not valid.