diff --git a/proxies/live/apiproxy/policies/MirrorRequestHeaders.xml b/proxies/live/apiproxy/policies/MirrorRequestHeaders.xml new file mode 100644 index 00000000..829b9f2a --- /dev/null +++ b/proxies/live/apiproxy/policies/MirrorRequestHeaders.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/proxies/live/apiproxy/policies/TranslateRequestHeaders.xml b/proxies/live/apiproxy/policies/TranslateRequestHeaders.xml new file mode 100644 index 00000000..160b92df --- /dev/null +++ b/proxies/live/apiproxy/policies/TranslateRequestHeaders.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/proxies/live/apiproxy/resources/py/mirror-request-headers.py b/proxies/live/apiproxy/resources/py/mirror-request-headers.py new file mode 100644 index 00000000..8ee52123 --- /dev/null +++ b/proxies/live/apiproxy/resources/py/mirror-request-headers.py @@ -0,0 +1,10 @@ +translated_header_names = ["X-Request-ID", "X-Correlation-ID"] +for translated_header_name in translated_header_names: + # Access original request headers dictionary + original_request_header = flow.getVariable("original_request_header." + translated_header_name) + if original_request_header: + original_request_header = original_request_header.split("=") + # Remove original response header + flow.removeVariable("response.header." + translated_header_name) + # Mirror original request header + flow.setVariable("response.header." + original_request_header[0], original_request_header[1]) diff --git a/proxies/live/apiproxy/resources/py/translate-request-headers.py b/proxies/live/apiproxy/resources/py/translate-request-headers.py new file mode 100644 index 00000000..61d8094e --- /dev/null +++ b/proxies/live/apiproxy/resources/py/translate-request-headers.py @@ -0,0 +1,27 @@ +def get_request_headers(): + request_header_names = flow.getVariable("request.headers.names") + request_headers = {} + for name in request_header_names: + request_headers[name] = flow.getVariable("request.header." + name) + + return request_headers + + +# Access request headers dictionary +request_headers = get_request_headers() + +# Map of lowercase header name to desired parcel case header name +request_header_translation = {"x-request-id": "X-Request-ID", "x-correlation-id": "X-Correlation-ID"} + +# Loop through request headers +for original_header_name, header_value in request_headers.items(): + desired_header_name = request_header_translation.get(original_header_name.lower()) + if desired_header_name: + # Store original header against the desired name for mirroring in response + flow.setVariable( + "original_request_header." + desired_header_name, "%s=%s" % (original_header_name, header_value) + ) + # Remove original request header + flow.removeVariable("request.header." + original_header_name) + # Set header with correct casing + flow.setVariable("request.header." + desired_header_name, header_value) diff --git a/proxies/live/apiproxy/targets/target.xml b/proxies/live/apiproxy/targets/target.xml index f779224d..37dce55c 100644 --- a/proxies/live/apiproxy/targets/target.xml +++ b/proxies/live/apiproxy/targets/target.xml @@ -8,6 +8,9 @@ FlowCallout.ApplyRateLimiting + + TranslateRequestHeaders + AddDeveloperAppData @@ -45,7 +48,7 @@ - + accesstoken.auth_type = "user" @@ -64,28 +67,44 @@ - SetMimeType + + MirrorRequestHeaders + + + + MirrorRequestHeaders + + + + MirrorRequestHeaders + RaiseFault.401Unauthorized oauthV2.OauthV2.VerifyAccessToken.failed = true or fault.name = "invalid_access_token" or fault.name = "InvalidAccessToken" or fault.name = "access_token_not_approved" or fault.name = "apiresource_doesnot_exist" or fault.name = "InvalidAPICallAsNo" or fault.name = "ApiProductMatchFound" or fault.name = "access_token_expired" + + MirrorRequestHeaders + RaiseFault.404NotFound response.header.x-amzn-ErrorType = "IncompleteSignatureException" + + MirrorRequestHeaders + RaiseFault.ApplicationOperationOutcome response.header.x-amzn-RequestId != null @@ -101,4 +120,4 @@ User-Agent,Referer,Accept-Language - + \ No newline at end of file diff --git a/specification/validated-relationships-service-api.yaml b/specification/validated-relationships-service-api.yaml index a06da7e3..8dab3159 100644 --- a/specification/validated-relationships-service-api.yaml +++ b/specification/validated-relationships-service-api.yaml @@ -87,6 +87,9 @@ info: |[NHS login - combined authentication and authorisation](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/user-restricted-restful-apis-nhs-login-combined-authentication-and-authorisation) |OAuth 2.0 authorisation code with API key and secret |No need to integrate and onboard separately with NHS login. |No access to user information. | |[NHS login - separate authentication and authorisation](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/user-restricted-restful-apis-nhs-login-separate-authentication-and-authorisation) |OAuth 2.0 token exchange with signed JWT |Gives access to user information. |Need to integrate and onboard separately with NHS login. | + ## Headers + This API is case-insensitive when processing request headers, meaning it will accept headers regardless of the letter casing used. (e.g. X-Request-Id, x-request-id are treated the same). When sending headers back in the response, we preserve the exact casing as received in the original request. + ## Errors We use standard HTTP status codes to show whether an API request succeeded or not. They are usually in the range: