Skip to content

Backoffice Login: Move access/refresh tokens to secure cookies#20779

Merged
kjac merged 16 commits intorelease/16.4from
v16/feature/cookie-based-auth
Nov 13, 2025
Merged

Backoffice Login: Move access/refresh tokens to secure cookies#20779
kjac merged 16 commits intorelease/16.4from
v16/feature/cookie-based-auth

Conversation

@kjac
Copy link
Contributor

@kjac kjac commented Nov 10, 2025

Prerequisites

  • I have added steps to test this contribution in the description below

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 BackOfficeTokenCookie configuration options to Umbraco:CMS:Security:

{
  "Umbraco": {
    "CMS": {
      "Security": {
        "BackOfficeTokenCookie": {
          "Enabled": false,
          "SameSite": "Strict" // Valid values are "Unspecified", "None", "Lax" and "Strict" (default).
        }
      }
    }
  }
}

To enable the feature, set Enabled to true.

The SameSite configuration 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 SameSite configuration, the generated token cookies still follow the security recommendations from the RFC. Of course, Strict mode (the default) is better than None.

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 Authorization header, 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 /token endpoint.

Lastly, verify that two cookies prefixed with __Host-umb have 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.

Copy link
Contributor

@AndyButland AndyButland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copy link
Contributor

@iOvergaard iOvergaard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 leekelleher self-requested a review November 11, 2025 10:08
Copy link
Member

@leekelleher leekelleher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested out with Contentment (v6-beta001), all working as expected! 🚀

@kjac kjac merged commit 49ba89c into release/16.4 Nov 13, 2025
23 of 24 checks passed
@kjac kjac deleted the v16/feature/cookie-based-auth branch November 13, 2025 07:19
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]>
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]>
@iOvergaard iOvergaard changed the title Move access/refresh tokens to secure cookies Backoffice Login: Move access/refresh tokens to secure cookies Nov 17, 2025
@umbracocommunity
Copy link

This pull request has been mentioned on Umbraco community forum. There might be relevant details there:

https://forum.umbraco.com/t/backoffice-login/7075/2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants