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:
Constructs a proxy URL: It changes the hostname of the incoming request to the Descope service's hostname.
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.
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.
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
.
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
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");
headers.delete("set-cookie");
cookies.forEach((value) => {
const cookie = deserialize(value);
if ([env.DESCOPE_SESSION_COOKIE, env.DESCOPE_SESSION_REFRESH_COOKIE].includes(cookie.name) && cookie.options) {
cookie.options.domain = url.hostname;
headers.append("x-descope-worker-proxy-cookie-modified", cookie.name);
}
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, {
...res,
headers,
});
};
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()
Constructing the proxy URL: The function modifies the hostname of the incoming request to match the Descope's service hostname.
Fetching the proxied request: The request is forwarded to the modified URL and the response is captured.
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.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.
Response
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.
Conclusion
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.