Backoffice Login: Move access/refresh tokens to secure cookies#20779
Merged
kjac merged 16 commits intorelease/16.4from Nov 13, 2025
Merged
Backoffice Login: Move access/refresh tokens to secure cookies#20779kjac merged 16 commits intorelease/16.4from
kjac merged 16 commits intorelease/16.4from
Conversation
…uests (and sets it by default to true)
…ude credentials by default
…BackOfficeTokenCookieSettings
…ld no longer be used
AndyButland
approved these changes
Nov 10, 2025
Contributor
AndyButland
left a comment
There was a problem hiding this comment.
This looks good to me @kjac. I've tested it on a local site using the build artifacts from this PR. I used the Personalisation Groups package which has it's own management API. and this all looks to work OK (from the backoffice and also via the Swagger UI).
8 tasks
iOvergaard
approved these changes
Nov 11, 2025
Contributor
iOvergaard
left a comment
There was a problem hiding this comment.
Just for good measure, knowing that I created some of the code, I'll put my checkmark here.
I have tested the following:
- ✅ The token-approach (no cookies) still works
- ✅ The new approach works on the compiled backoffice
- ✅ It works on the Vite dev server (if you set SameSite=None)
- ✅ It works with SameSite=Strict, too, if you add the @vite/basic-ssl library to the Vite dev server and run the backend and Vite on https://localhost:xxxx
- ✅ The preview mode still works and carries over the token to a new tab
- ✅ Custom controllers work if they provide the security filter
- ✅ Custom fetch clients work if they provide
credentials: 'include' - ✅ The umbraco-extension template (still) works out-of-the-box (no changes required)
leekelleher
approved these changes
Nov 11, 2025
Member
leekelleher
left a comment
There was a problem hiding this comment.
Tested out with Contentment (v6-beta001), all working as expected! 🚀
kjac
added a commit
that referenced
this pull request
Nov 13, 2025
* feat: adds the `credentials: include` header to all manual requests * feat: adds `credentials: include` as a configurable option to xhr requests (and sets it by default to true) * feat: configures the auto-generated fetch client from hey-api to include credentials by default * Add OpenIddict handler to hide tokens from the back-office client * Make back-office token redaction optional (default false) * Clear back-office token cookies on logout * Add configuration for backoffice cookie settings * Make cookies forcefully secure + move cookie handler enabling to the BackOfficeTokenCookieSettings * Use the "__Host-" prefix for cookie names * docs: adds documentation on cookie settings * build: sets up launch profile for vscode with new cookie recommended settings * docs: adds extra note around SameSite settings * docs: adds extra note around SameSite settings * Respect sites that do not use HTTPS * Explicitly invalidate potentially valid, old refresh tokens that should no longer be used * Removed obsolete const --------- Co-authored-by: Jacob Overgaard <[email protected]>
1 task
kjac
added a commit
that referenced
this pull request
Nov 14, 2025
* Move access/refresh tokens to secure cookies (#20779) * feat: adds the `credentials: include` header to all manual requests * feat: adds `credentials: include` as a configurable option to xhr requests (and sets it by default to true) * feat: configures the auto-generated fetch client from hey-api to include credentials by default * Add OpenIddict handler to hide tokens from the back-office client * Make back-office token redaction optional (default false) * Clear back-office token cookies on logout * Add configuration for backoffice cookie settings * Make cookies forcefully secure + move cookie handler enabling to the BackOfficeTokenCookieSettings * Use the "__Host-" prefix for cookie names * docs: adds documentation on cookie settings * build: sets up launch profile for vscode with new cookie recommended settings * docs: adds extra note around SameSite settings * docs: adds extra note around SameSite settings * Respect sites that do not use HTTPS * Explicitly invalidate potentially valid, old refresh tokens that should no longer be used * Removed obsolete const --------- Co-authored-by: Jacob Overgaard <[email protected]> * Remove configuration option * Invalidate all existing access tokens on upgrade * docs: updates recommended settings for development * build: removes non-existing variable * Skip flaky test * Bumped version of our test helpers to fix failing tests --------- Co-authored-by: Jacob Overgaard <[email protected]> Co-authored-by: Andreas Zerbst <[email protected]>
1 task
8 tasks
|
This pull request has been mentioned on Umbraco community forum. There might be relevant details there: |
This was referenced Jan 26, 2026
This was referenced Mar 11, 2026
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.
Prerequisites
Description
This RFC from IETF outlines best practices for web apps using token based auth moving forward.
To boil it all down, the recommendation is simply: Don't use token based auth in web apps. Or at the very least, don't expose tokens to web apps.
The reason? Malicious scripts and even browser extensions means there's the risk of token theft.
We cannot remove the token based auth from the Umbraco back-office client "just like that", because it would be rather breaking. Instead, we're putting a multi-step plan in motion to first stop exposing the tokens, and then phase them out later on.
This PR is the first step. It adds a configurable option to redact access and refresh tokens from the server.
As the PR targets 16.4, it must be backwards compatible (non-breaking). Thus, the feature is disabled by default. It is of course recommended to enable the feature, and the Umbraco docs will reflect that.
A follow-up PR will cherry-pick the feature into 17.0 and make it mandatory.
New configuration
This PR adds the
BackOfficeTokenCookieconfiguration options toUmbraco:CMS:Security:{ "Umbraco": { "CMS": { "Security": { "BackOfficeTokenCookie": { "Enabled": false, "SameSite": "Strict" // Valid values are "Unspecified", "None", "Lax" and "Strict" (default). } } } } }To enable the feature, set
Enabledtotrue.The
SameSiteconfiguration is necessary when hosting the back-office client on a different host from the Management API. This is a common development practice - we have yet to see anyone doing it in production.Regardless of changing the
SameSiteconfiguration, the generated token cookies still follow the security recommendations from the RFC. Of course,Strictmode (the default) is better thanNone.Note
Some might be running Umbraco without HTTPS in development environments. If this is the case, the cookie cannot be marked as secure, which is in violation with the above-mentioned security recommendations.
It is not possible to run Umbraco without HTTPS in production mode, unless you explicitly add code to prevent the boot time checking of HTTPS, so production environments should always have secure cookies.
Testing this PR
Start by running the site without any configuration changes. It should work as per usual, and you should still see access and refresh tokens in the Management API requests from the back-office client.
Now enable the feature and restart the site. Log out and log back in to ensure the feature is invoked. Everything should still work the same.
All the Management API requests from the back-office client should still contain an
Authorizationheader, but the access token should have the value[redacted].Similarly, refresh token requests should contain a
[redacted]refresh token in the payload. You can trigger a refresh token request by reloading the browser - look for requests to the/tokenendpoint.Lastly, verify that two cookies prefixed with
__Host-umbhave been accepted by the client. They should be secure, HTTP-only session cookies, and they should contain an encrypted representation of access and refresh tokens - not valid tokens in clear text.