1255: add Authorization header to scratch requests by DNR500 · Pull Request #1408 · RaspberryPiFoundation/editor-ui
Issue: 1255
Summary
This PR adds the Authorization header to Scratch requests when authKey is used, and improves how authentication is handled during iframe startup.
As this impacts the Scratch initialisation flow, care has been taken to ensure it is as resilient as possible. The implementation accounts for timing edge cases and aims to behave consistently across environments. Test coverage has also been added to help guard against regressions and maintain the stability of the initialisation process over time.
auth-header-scratch.mov
Behaviour
authKeysignals that authentication is expected- It tells Scratch that the session should be authenticated
- The access token comes from the parent app
- Stored in
user.access_token - Managed outside the iframe
- Stored in
Request handling
- If a token exists
- Requests include:
Authorization: <access_token>
- Requests include:
- If no token exists and authKey is not set
- Scratch still loads normally
- Requests are sent without an
Authorizationheader
This allows authenticated flows when a user is logged in and unauthenticated flows when auth is not required.
Passing the token to the iframe
Although the token may exist in localStorage during authenticated flows, we pass it to the iframe using a postMessage handshake rather than reading it directly from storage.
Why this approach
- localStorage is scoped to a single origin
- If the iframe runs on a different domain or environment, it cannot access it
- This can lead to silent failures or mismatched auth state
Using postMessage
- Keeps the parent app as the single source of truth
- Sends the token only when the iframe is ready
- Uses origin checks and a nonce to validate messages
This makes startup and authentication behaviour more predictable across cross-origin deployments than relying on shared localStorage access.
Nonce usage
A unique nonce is generated for each iframe instance and included in the postMessage handshake
This allows us to:
- Match each
scratch-gui-readymessage with the correctscratch-gui-set-tokenresponse - Ignore stale or unrelated messages
Why this matters
- The message channel can be noisy in development (e.g. webpack/HMR)
- Iframes may be recreated, but old messages can still arrive
Nonce validation ensures only messages for the current iframe instance are processed so cross-talk between instances is prevented. Can help guard against race conditions and is accounted for inthe retries.
Retry behaviour
We added a bounded retry mechanism to improve startup reliability.
- Scratch begins by sending a
scratch-gui-readymessage - In development or remount scenarios, this message can be missed
To handle this
- The iframe retries sending the ready message for a short, fixed period
- This gives the host multiple chances to respond with
scratch-gui-set-token - Retries are capped to avoid infinite polling
Combined with nonce validation, this makes the handshake more reliable and gives some allowance in timing mismatches.
Auth guardrail
We added a safeguard to ensure Scratch only initialises with credentials when authentication is expected.
- If authKey is present, a valid token is required before mounting
- If the iframe starts before the token is ready, it waits rather than initialising unauthenticated
- This is currently a very unlikely scenario, but it represents a potential race condition. Guarding against it now ensures that any future changes (for example, delays in token hydration or lifecycle changes) are already handled safely
- If a token never arrives, the process times out cleanly
This should help prevent subtle timing-related bugs