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

  • authKey signals 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

Request handling

  • If a token exists
    • Requests include: Authorization: <access_token>
  • If no token exists and authKey is not set
    • Scratch still loads normally
    • Requests are sent without an Authorization header

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-ready message with the correct scratch-gui-set-token response
  • 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-ready message
  • 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