You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Authenticate Relay with Managed Identity token (#103)
In production, replace locally-stored SAS token in websockets URL with with Azure-generated Managed Identity JWT token in HTTP header.
In non-production environments, use a SAS token if set in the environment, otherwise default to DefaultAzureCredential.
# ADR-005: Use Managed Identity for Azure Relay authentication
2
+
3
+
Date: 2026-04-28
4
+
5
+
Status: Accepted
6
+
7
+
## Context
8
+
9
+
The gateway connects to Azure Relay Hybrid Connections to receive worklist actions from Manage Breast Screening. A connection must be authenticated to Azure Relay.
10
+
11
+
The initial implementation used Shared Access Signature (SAS) tokens. These are HMAC-SHA256 signatures computed from a shared secret key, embedded in the WebSocket connection URL as a query parameter (`sb-hc-token`). This required:
12
+
13
+
- A shared access key to be provisioned and stored as an environment variable (`AZURE_RELAY_SHARED_ACCESS_KEY`)
14
+
- The key name to be configured separately (`AZURE_RELAY_KEY_NAME`)
15
+
- Manual key rotation when keys needed to change
16
+
17
+
As the gateway runs inside the hospital network but is provisioned via Azure Arc, it can be assigned a managed identity through Arc-enabled infrastructure. Storing a long-lived shared secret in the environment is therefore unnecessary operational overhead and a potential security risk.
18
+
19
+
That said, setting up a working Relay connection locally is already complex. Mandating managed identity for all environments would add further friction for developers, who would need Azure CLI credentials with a Relay Listener role assignment before they could run the service.
20
+
21
+
## Decision
22
+
23
+
In **production** (`ENVIRONMENT=prod`), the gateway uses `ManagedIdentityCredential` exclusively. The SAS token path is unavailable regardless of what environment variables are set. The gateway's managed identity must be assigned the **Azure Relay Listener** role on the hybrid connection resource in Azure.
24
+
25
+
In **non-production** environments, the auth method is determined by whether `AZURE_RELAY_SHARED_ACCESS_KEY` is set:
26
+
27
+
- If set, a SAS token is generated and embedded in the WebSocket URL (`sb-hc-token`), preserving the simpler local development setup.
28
+
- If absent, `DefaultAzureCredential` is used, which works with Azure CLI credentials (`az login`) for developers who have the Listener role assigned to their identity.
29
+
30
+
The token is passed as an `Authorization: Bearer` HTTP header on the WebSocket upgrade request for managed identity paths. Azure Relay validates it against Azure AD.
31
+
32
+
A startup credential check (`verify_credentials()`) runs before the listen loop. In production this will raise `ClientAuthenticationError` immediately if the managed identity is not correctly configured. In non-production with a SAS key it logs the auth method and continues.
33
+
34
+
`ManagedIdentityCredential` is preferred over `DefaultAzureCredential` in production because it is predictable — it only attempts the IMDS endpoint and fails clearly, rather than traversing a credential chain that could succeed unexpectedly via another mechanism.
35
+
36
+
## Consequences
37
+
38
+
### Positive Consequences
39
+
40
+
-**No secrets in production:** No shared key to store, rotate, or accidentally leak in deployed environments
41
+
-**Fail-fast on misconfiguration:** Startup validation raises `ClientAuthenticationError` immediately rather than failing silently in the reconnect loop
42
+
-**Preserved local developer experience:** SAS tokens continue to work locally when `AZURE_RELAY_SHARED_ACCESS_KEY` is set
43
+
-**Consistent with platform direction:** Aligns with how the gateway already authenticates to the DICOM API
44
+
45
+
### Negative Consequences
46
+
47
+
-**Azure infrastructure dependency in production:** The managed identity and its role assignment must exist before the service can start
0 commit comments