Refresh Tokens - #1278
Open
Scullyon wants to merge 21 commits into
Open
Refresh Tokens#1278Scullyon wants to merge 21 commits into
Scullyon wants to merge 21 commits into
Conversation
The identity provider now accepts RS256 alone, so the assertion this service proves itself with is signed by an RSA key rather than an elliptic curve one. The keypair must be rotated with the provider in step: it holds the public half as OIDC_RUNNER_JWKS, and sign-in fails while the two are different kinds of key. Generate a matched pair with the generate-client-keypair script in forms-identity-ui.
The sign in now names forms-submission-api as the resource it wants a token for, on the authorization request and the token request alike. Naming it at the token endpoint alone returns an opaque token and no error, so both carry it. The token is kept in the session, where it is read to call that API and never reaches the browser. A token bound to a resource cannot reach the userinfo endpoint, so the provider puts the scope's claims in the ID token instead and the email is read from there.
The homepage now lists the forms the signed-in citizen saved and has not yet submitted, soonest to expire first, with the reference number, an in progress tag, when it was last saved and when it expires. A citizen who has saved nothing is told so rather than shown an empty table. The access token in the session names the citizen to forms-submission-api, so the request carries no identifier of its own. Dates are formatted in the route rather than the template, so they are covered by a test.
Both dates in the table now read as GOV.UK writes them, for example "10 September 2026 at 14:00". They are read in UK time. A timestamp is stored in UTC, so the day it falls on depends on where it is read: half past eleven at night in UTC is already the next day in British Summer Time. Naming the zone keeps every server showing a citizen the same day, which matters most for the date their saved form expires.
The saved forms table needs more than the default 960px, so the homepage carries a body class that widens every container on the page: the header, the service navigation, the cookie banner, the main content and the footer. Widening the main content alone leaves the header and footer at the old width, and the seam shows. The service navigation and the cookie banner hardcode their container, so there is no parameter to pass them. The width is set on the homepage rather than the layout, which is shared with every form page rendered by forms-engine-plugin, where a shorter line length is easier to read. The class is built with the govuk-width-container mixin, which brings the responsive margins and safe area insets with it.
The forms service reads form metadata and definitions from the manager. The save-and-exit calls and the saved forms list read from the submission API, so they sit together in their own service.
The submission API sends timestamps as strings, so the helper takes a string rather than a string or a date.
Drops the elliptic curve key case and the userinfo expectation in the sign-out test, since the client signs with RS256 and reads the email from the ID token. Reads the token endpoint parameters by index, as the other mock call assertions do.
It calls the submission API, so it sits with the other submission API calls. The forms service now reads only from the manager.
The Identity typedef gained an access token, so the session value and the request credentials declare it as well, and the fixtures carry one.
The citizen-session scheme always puts a full identity on the credentials, and every route that reads them requires sign in. The hapi typings now say so, so that routes read the fields directly rather than cast each one.
The homepage table shows a red "Expired" tag for a saved form whose expiry time has passed, and the teal "In progress" tag for the others. The route chooses each form's status, so the choice is tested with the route and more statuses can be added in one place. Each status is also the translation key of its tag text, and the template maps it to the tag class.
cy.json had two top-level "signIn" keys. JSON keeps only the last one, so the Welsh caption, start button, preview banner and account menu fell back to English. The table strings now sit in the first signIn.homepage block, the same layout as en-GB.json.
formatDateTime takes the page language, and Intl supplies the month name and the word that joins the date to the time, for example "10 Medi 2026 am 14:00". The route passes the language getFormTranslator resolves, so a form with no Welsh translation keeps English dates. The language is a required argument rather than a default, so that each caller states which language it formats for.
…ssion, but instead in a separate Redis record to avoid an expired refresh token accidentally being written back to the session (if two simultaneous requests) and so that the record can be locked whilst tokens are exchanged during a refresh.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




Implementation of refresh tokens.
Tokens are no longer stored in the session, but instead separately in Redis and the key stored in the session. This is so that: