Skip to content

Force an Asset Manager handshake on the draft stack [WHIT-3983] - #5788

Closed
ChrisBAshton wants to merge 7 commits into
mainfrom
force-asset-manager-preflight-login
Closed

Force an Asset Manager handshake on the draft stack [WHIT-3983]#5788
ChrisBAshton wants to merge 7 commits into
mainfrom
force-asset-manager-preflight-login

Conversation

@ChrisBAshton

@ChrisBAshton ChrisBAshton commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Superseded by #5789, but kept here as a record of all of the things we tried in advance of that.


What

Include a small blocking external JS script request in the <head> of all draft documents, to force an Asset Manager session to establish, before attempting to render the rest of the page.

Why

As of alphagov/whitehall#11667, images can now be uploaded to the draft stack, meaning Frontend needs to be able to render the draft images in the page when on draft preview.

Unlike live images, draft images require the user to be authenticated (via Signon) and for an Asset Manager session to exist. When rendering a draft asset, the request for the asset from Asset Manager is redirected through a chain of calls including Authenticating Proxy and Signon, and if 1) the user is logged into Signon, and 2) they have permission to view the asset, the asset is then downloaded. Subsequent attempts to render any asset avoids the OAuth round-trip, as the user already has a warm Asset Manager session cookie by then.

The problem arises when more than one draft asset is included in a page, and a user is coming to the page cold. When rendering the page, every draft asset begins its own authentication journey, and the various callbacks and tokens will often arrive out of order, meaning no session takes hold. The images fail to load, and subsequent refreshes of the page rarely help (unless the order of requests sent and received happens in a very particular order). Visiting a draft asset in isolation, e.g. on a new tab, forces the session cookie and then subsequent attempts to render a page full of draft assets will succeed.

In #5783 we explored adding a preflight mechanism to Frontend whereby 'cold' users are redirected to a page containing a single asset, and then redirected back to their original page. Unfortunately our nginx config would require that the single-asset-page be added to an allowlist of pages that are allowed to set cookies, and even then, subsequent pages would not be allowed to read the cookie, which is then clobbered to an empty string:
https://github.com/alphagov/govuk-helm-charts/blob/3f15020fa87e412e8e67ac9c1e1339030c4ef440/charts/app-config/templates/router-nginx-config.tpl#L116-L149

The solution is to force an asset to download at the beginning of the page, before even attempting to render the rest of the page. Various Javascript attempts were looked into here, but there was no reliable means of blocking rendering and waiting for the asset request to complete. Instead, we can rely on the native behaviour of the browser: treat the image as a JavaScript source instead and attempt to download that in the head. That is a blocking operation, and the full OAuth redirect chain is followed before eventually the image is returned. That clearly isn't JavaScript, so the console will error, but as the 'script' is external, its error doesn't affect any other JavaScript in the page, so the console syntax error is a no-op.

The blast radius is small - we only inject this request on the draft stack, so only publishers will see it, and the real world ramifications are perhaps a very slight delay in the rendering of the page.

The asset chosen is the default lead image placeholder referenced throughout Frontend. It is the same asset used in a similar solution in Whitehall: alphagov/whitehall#11775

Alongside the change to the layout, I've added tests for all of the environments listed in https://docs.publishing.service.gov.uk/manual/environments.html#determining-your-environment

Jira: https://gov-uk.atlassian.net/browse/WHIT-3983

Visual changes

N/A

⚠️ This repo is Continuously Deployed: make sure you follow the guidance ⚠️

@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 06:39 Inactive
@ChrisBAshton
ChrisBAshton force-pushed the force-asset-manager-preflight-login branch from d8a309c to 44d6b64 Compare September 10, 2026 07:50
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 07:50 Inactive
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 08:07 Inactive
@govuk-ci
govuk-ci had a problem deploying to govuk-frontend-app-pr-5788 September 10, 2026 08:36 Failure
@ChrisBAshton
ChrisBAshton force-pushed the force-asset-manager-preflight-login branch from 7803b71 to 93f7357 Compare September 10, 2026 08:40
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 08:40 Inactive
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 09:24 Inactive
As of alphagov/whitehall#11667, images
can now be uploaded to the draft stack, meaning Frontend needs to
be able to render the draft images in the page when on draft preview.

Unlike live images, draft images require the user to be authenticated
(via Signon) and for an Asset Manager session to exist. When rendering
a draft asset, the request for the asset from Asset Manager is redirected
through a chain of calls including Authenticating Proxy and Signon,
and if 1) the user is logged into Signon, and 2) they have permission
to view the asset, the asset is then downloaded. Subsequent attempts
to render any asset avoids the OAuth round-trip, as the user already
has a warm Asset Manager session cookie by then.

The problem arises when more than one draft asset is included in a
page, and a user is coming to the page cold. When rendering the page,
every draft asset begins its own authentication journey, and the
various callbacks and tokens will often arrive out of order, meaning
no session takes hold. The images fail to load, and subsequent
refreshes of the page rarely help (unless the order of requests sent
and received happens in a very particular order). Visiting a draft
asset in isolation, e.g. on a new tab, forces the session cookie and
then subsequent attempts to render a page full of draft assets will
succeed.

In #5783 we explored adding a preflight mechanism to Frontend whereby
'cold' users are redirected to a page containing a single asset, and
then redirected back to their original page. Unfortunately our nginx
config would require that the single-asset-page be added to an
allowlist of pages that are allowed to set cookies, and even then,
subsequent pages would not be allowed to read the cookie, which is
then clobbered to an empty string:
https://github.com/alphagov/govuk-helm-charts/blob/3f15020fa87e412e8e67ac9c1e1339030c4ef440/charts/app-config/templates/router-nginx-config.tpl#L116-L149

The solution is to force an asset to download at the beginning of
the page, before even attempting to render the rest of the page.
Various Javascript attempts were looked into here, but there was
no reliable means of blocking rendering and waiting for the asset
request to complete. Instead, we can rely on the native behaviour
of the browser: treat the image as a JavaScript source instead and
attempt to download that in the head. That is a blocking operation,
and the full OAuth redirect chain is followed before eventually the
image is returned. That clearly isn't JavaScript, so the console will
error, but as the 'script' is external, its error doesn't affect
any other JavaScript in the page, so the console syntax error is a
no-op.

The blast radius is small - we only inject this request on the
draft stack, so only publishers will see it, and the real world
ramifications are perhaps a very slight delay in the rendering
of the page.

The asset chosen is the default lead image placeholder referenced
throughout Frontend. It is the same asset used in a similar
solution in Whitehall: alphagov/whitehall#11775

Alongside the change to the layout, I've added tests for all of the environments listed in
https://docs.publishing.service.gov.uk/manual/environments.html#determining-your-environment

Jira: https://gov-uk.atlassian.net/browse/WHIT-3983
This reverts commit 2897605.

The 'nonce' worked, but the moment the image tag redirected to
'https://draft-assets.integration.publishing.service.gov.uk/auth/gds'
it failed, so the CSP was only passing for the first part of the
request chain.
See previous commit - `nonce` didn't work because it only applied
to the initial external request, and then was blocked when that
external request redirected. So let's just blanket-allow all
external script calls to this domain.
…tack"

This reverts commit 93f7357.

The script does have a Location redirect, but it doesn't get followed
so the auth flow never completes.
This reverts commit 44d6b64.

The approach taken in that commit won't work - we don't seem to be
able to block rendering of the page from the <head>. What we can
do instead is tidy up bad assets at the END of the page - which
is what we'll try in the next commit.
@eYinka
eYinka force-pushed the force-asset-manager-preflight-login branch from 3008426 to 2a80160 Compare September 10, 2026 10:13
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 10:13 Inactive
@eYinka
eYinka force-pushed the force-asset-manager-preflight-login branch 2 times, most recently from 2efbea2 to 5f12b29 Compare September 10, 2026 10:15
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 10:15 Inactive
@eYinka
eYinka force-pushed the force-asset-manager-preflight-login branch from 5f12b29 to 90faea5 Compare September 10, 2026 10:20
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 10:20 Inactive
@eYinka
eYinka force-pushed the force-asset-manager-preflight-login branch 2 times, most recently from 27fe7ba to d9e7d22 Compare September 10, 2026 10:43
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 10:43 Inactive
@eYinka
eYinka force-pushed the force-asset-manager-preflight-login branch from d9e7d22 to 06c8889 Compare September 10, 2026 10:49
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 10:49 Inactive
If 2 or more draft assets are in a draft document, we force a load
of our placeholder asset (to guarantee there's a working Asset
Manager session) then reload all of the potentially bad images.
@eYinka
eYinka force-pushed the force-asset-manager-preflight-login branch from 06c8889 to b867df7 Compare September 10, 2026 11:01
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5788 September 10, 2026 11:01 Inactive
@govuk-ci
govuk-ci temporarily deployed to govuk-frontend-app-pr-5789 September 10, 2026 11:22 Inactive
@ChrisBAshton

Copy link
Copy Markdown
Contributor Author

Closing in favour of #5789.

Something in our deployment pipeline got corrupted on this branch name (we suspect a bad build somewhere, which we were unable to overwrite despite force pushing / rebasing / redeploying the branch - see https://gds.slack.com/archives/C013F737737/p1789038229774019?thread_ts=1789031300.263719&cid=C013F737737). We ended up having to spin up a new branch for a 'clean' build. But it's sort of useful in a way, because now we can leave this PR closed with all of the commits showing the history of what we tried.

Our attempts to block loading of the page until an Asset Manager session succeeded, were not successful, as this isn't really possible in JS alone and the mechanism we fell back to instead - requesting the draft image as a <script> tag hack - didn't work either, as the auth flow of redirects wasn't followed.

In the next PR, we pivot to injecting JS at the end of the page, cleaning up the images in the DOM after page load.

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