feat: Token exchange for tiled insertion #1342
Draft
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Option 1: Service account with write access
One approach is to use a service account that is allowed to write.
For example, we could create a service account (e.g.
ixx-tiled-writer) that has permissions for the ixx beamline only. This service account would be able to write to all ixx beamline sessions exposed viaixx-blueapi.diamond.ac.uk, but would not be able to write to sessions belonging to other beamlines.We will need to add AuthZ in blueapi to make sure that the person writing to the session has access to it.This would require creating a dedicated Keycloak client (
ixx-tiled-writer) with its own client ID and secret.This information will be added as a hard coded token claims as
{ "fedid": "ixx-tiled-writer", "permissions": ["ixx-admin"] }Downside:
If the client ID and secret are leaked, a malicious actor would gain read/write access to all ixx beamline sessions (though still not delete access). While scoped, this is still a significant risk.
An alternative service-account approach is to not hardcode any beamline permissions into the token at all. In this model, a single service account will have write to any beamline session, and
blueapiwould perform an explicit authorization check before allowing writes, ensuring the caller has access to the target beamline session.This is effectively equivalent to an API-key style integration with Tiled.
Security concern:
If this single service account is leaked, the attacker would gain read/write access to all beamline data (again, excluding delete). This is a much larger blast radius and therefore a serious vulnerability.
The core reason this problem exists is that, in this approach, we are not propagating end-user identity (
fedid) through to Tiled.While this approach is simpler to implement, it is significantly less secure, as a malicious actor could read data that is not intended for them.
Note: I couldn’t find a approach where we encode the fedid that we have got from the blueapi token into the service account token dynamically.(might not be possible because you will be able to impersonate anyone)
Option 2: Token exchange preserving user identity (
fedid)The second approach is more complex but significantly more secure: using Keycloak token exchange to preserve the original user identity and permissions.
In this model,
ixx-blueapiuses its client secret to exchange a user access token for another token that is scoped for Tiled. Importantly, the exchanged token retains the same user identity and permissions as the original token.This requires enabling the following settings on the
ixx-blueapiclient:"standard.token.exchange.enabled": "true""standard.token.exchange.enableRefreshRequestedTokenType": "SAME_SESSION"With token exchange:
ixx-blueapiclient secret is leaked, it cannot be used to gain additional access to Tiled data + You will need a access token with a valid session and access token only lasts for 5 mins inidentity-testand 1 minute inauthnImplications for
blueapi-cliTo support this, the
blueapi-cliKeycloak client must behave like other clients(argocd,etc) in the realm. In particular:offline_access)Because token exchange never creates a new user session, a service account alone cannot be used here. We therefore need a client that can establish a user session.
We will need device flow client (
ixx-blueapi-cli) with:ixx-blueapi(the private client backingixx-blueapi.diamond.ac.uk)ixx-blueapiI have verified that long scans continue to work correctly: the session remains active because the token is continually exchanged and the user is actively interacting with
blueapi, even though this happens in a machine-to-machine style workflow.Will this have any impact on the GDA side ,as far as I know GDA is per beamline so it could easily have a device flow client per beamline. ?
Testing changes
System tests were updated to use device flow login via Playwright, which opens a browser and performs a real login. This works correctly in CI.
This change was necessary because service accounts do not have user sessions and therefore cannot perform token exchange.
When using Playwright, you must run:
Alternatively, you can comment out the Playwright login and perform a manual login using:
References
https://www.keycloak.org/securing-apps/token-exchange
Related PR in Tiled
There is also a related PR in Tiled adding support for custom authentication in the Tiled client:
bluesky/tiled#1269