Skip to content

Conversation

@ZohebShaikh
Copy link
Contributor

@ZohebShaikh ZohebShaikh commented Jan 19, 2026

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 via ixx-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 blueapi would 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-blueapi uses 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-blueapi client:

  • "standard.token.exchange.enabled": "true"
  • "standard.token.exchange.enableRefreshRequestedTokenType": "SAME_SESSION"

With token exchange:

  • A valid user access token is required to perform the exchange.
  • The resulting token grants only the permissions the user already has.
  • Even if the ixx-blueapi client 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 in identity-test and 1 minute in authn

Implications for blueapi-cli

To support this, the blueapi-cli Keycloak client must behave like other clients(argocd,etc) in the realm. In particular:

  • It must have the same session permissions. ( No offline_access)
  • Users will be logged out after ~30 minutes of idle time.

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.

“Token exchange never creates a new user session. In case that requested_token_type is a refresh token, it may eventually create a new client session in the user session for the requester client (if the client session was not yet created).”

We will need device flow client (ixx-blueapi-cli) with:

  • Audience set to ixx-blueapi (the private client backing ixx-blueapi.diamond.ac.uk)
  • Support for token exchange against ixx-blueapi

I 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:

playwright install

Alternatively, you can comment out the Playwright login and perform a manual login using:

blueapi -c tests/system_tests/config-cli.yaml login

References


Related PR in Tiled

There is also a related PR in Tiled adding support for custom authentication in the Tiled client:
bluesky/tiled#1269

@ZohebShaikh ZohebShaikh changed the title Token exchange feat: Token exchange for tiled insertion Jan 19, 2026
@codecov
Copy link

codecov bot commented Jan 19, 2026

Codecov Report

❌ Patch coverage is 51.00000% with 49 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.44%. Comparing base (ffab203) to head (c8f22ff).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/blueapi/service/authentication.py 41.55% 45 Missing ⚠️
src/blueapi/core/context.py 60.00% 2 Missing ⚠️
src/blueapi/service/interface.py 80.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1342      +/-   ##
==========================================
- Coverage   95.00%   93.44%   -1.56%     
==========================================
  Files          42       43       +1     
  Lines        2762     2854      +92     
==========================================
+ Hits         2624     2667      +43     
- Misses        138      187      +49     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants