back arrowBack to Blog


Manage Sessions Across Domains With Descope and Cloudflare Workers

CF Workers session management blog thumbnail

Businesses often host multiple apps and products across multiple domains. However, managing user sessions consistently across these different domainscan become a real challenge. 

Speaking from a Descope perspective, there is typically one custom domain set per Descope Project, since many of our customers like to separate the apps they have hosted on different domains into different Descope Projects. Also, in a B2B environment, it makes sense to configure different custom domains for each specific customer so that their own custom domain is always shown to each of their various customers.

However, in some cases, you might want to use the same Descope Project and the same flows with multiple different apps hosted on different domains. In this blog, we will discuss how you can use Cloudflare Workers and Descope to seamlessly unify session management across multiple domains.

Introduction to Cloudflare Workers

Cloudflare Workers are lightweight, serverless functions that run on Cloudflare's vast global network. They are perfect for intercepting, modifying, and forwarding web requests, making them a fantastic tool to create a proxy between client requests and the Descope service irrespective of the domain they originate from.

How does the proxy work?

At its core, the Cloudflare Worker:

  1. Constructs a proxy URL: It changes the hostname of the incoming request to the Descope service's hostname.

  2. Handles cookies: It modifies the set-cookie headers from the Descope response to match the original request's domain. This ensures session cookies are correctly set for the client's domain.

  3. Manages CORS: It adjusts the CORS headers in the response to ensure the browser permits the sharing of resources between the originating domain and the target domain.

Getting started with setup

To get started with Cloudflare Workers, you can clone our cf-worker repository from GitHub.

  1. Preparation

    • Sign up for a Cloudflare account and configure your domain.

    • Install the wrangler CLI tool with npm install -g @cloudflare/wrangler.

    • Authenticate the wrangler to your Cloudflare account using wrangler login.

  2. Configure your Cloudflare Worker

    • In the repository you’ve cloned, run yarn install.

    • Then, make sure that your Cloudflare account ID is present in the wrangler.toml file.

    • The environment variables for the BASE_URL and so on should already be present in the script, so there’s nothing left for you to do with the configuration

  3. Deploy

    • Once everything is set, deploy your Worker using wrangler publish.

The code

import { deserialize, serialize } from "./cookieParser";

export default {
    async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
        return handleRequest(request, env);

export const handleRequest = async (request: Request, env: Env) => {
    const url = new URL(request.url);
    const proxyUrl = new URL(url);
    proxyUrl.hostname = new URL(env.DESCOPE_BASE_URL).hostname;

    const res = await fetch(proxyUrl, request);
    const headers = new Headers(res.headers);
    const cookies = headers.getAll("set-cookie");
    cookies.forEach((value) => {
        const cookie = deserialize(value);
        if ([env.DESCOPE_SESSION_COOKIE, env.DESCOPE_SESSION_REFRESH_COOKIE].includes( && cookie.options) {
            cookie.options.domain = url.hostname;
        headers.append("set-cookie", serialize(cookie));
    const cors = headers.get("access-control-allow-origin");
    if (cors && cors === new URL(env.DESCOPE_BASE_URL).origin) {
        headers.set("access-control-allow-origin", request.headers.get("origin") ?? cors);
    headers.set("x-descope-worker-proxy-url", proxyUrl.href);
    return new Response(res.body, {

Let’s now break this code down.

Cloudflare Worker Handler

The fetch method is the Cloudflare Worker's primary entry point. It receives the incoming request and processes it. Our main logic resides in the handleRequest() function.

Proxy logic in handleRequest()

  1. Constructing the proxy URL: The function modifies the hostname of the incoming request to match the Descope's service hostname.

  2. Fetching the proxied request: The request is forwarded to the modified URL and the response is captured.

  3. Cookie handling: We need to ensure cookies returned by Descope are correctly set for the requesting domain. The function alters the set-cookie headers from the Descope response to be compatible with the domain of the original request.

  4. CORS management: The CORS headers in the response are adjusted to ensure cross-domain requests are permitted. This is essential when serving requests from different origins.


Finally, the Worker returns the modified response, ensuring that the end user's browser sees it as originating from the domain they accessed, even though the actual session management logic is unified and handled by Descope.

As you can see, with Cloudflare Workers and Descope, you can keep your refresh tokens secure in cookies and use them across all of your apps, even if you’re using different domains.


Managing user sessions across multiple domains can be a daunting task. However, integrating Descope into your infrastructure not only simplifies the backend complexities, but also provides a seamless frontend experience. 

With Descope, there are many ways you can handle sessions across different domains, such as: 

  • Creating multiple Projects with distinct user pools.

  • Using OpenID Connect

  • Using Cloudflare Workers as showcased in this blog. 

With the combined strength of Cloudflare Workers and Descope, we've shown how you can streamline the session management process, ensuring a uniform user experience across all of your web and mobile applications.

If you have more questions about session management or authentication in general, we’d love to have you over at AuthTown, our open user community for developers to learn more about identity and authentication.