Force an Asset Manager handshake on the draft stack [WHIT-3983] - #5788
Force an Asset Manager handshake on the draft stack [WHIT-3983]#5788ChrisBAshton wants to merge 7 commits into
Conversation
d8a309c to
44d6b64
Compare
7803b71 to
93f7357
Compare
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.
3008426 to
2a80160
Compare
2efbea2 to
5f12b29
Compare
5f12b29 to
90faea5
Compare
27fe7ba to
d9e7d22
Compare
d9e7d22 to
06c8889
Compare
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.
06c8889 to
b867df7
Compare
|
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 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. |
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