fastapisession-cookieshttpcookieflyfastapi-middleware

Failing to set cookie for SessionMiddleware due to 'invalid domain' (separate frontend/backend)


I set up Session Middleware with my FastAPI backend to authenticate my React frontend users, which worked with domain=127.0.0.1. Now that I've deployed both my frontend and my backend to two separate servers using Fly.io, the session cookies are no longer being accepted.

My frontend is deployed at XXX-ui.fly.dev, and my backend is deployed at XXX-api.fly.dev.

Here is the CORS config and Session Middleware config for my backend:

origins = [
"https://XXX-ui.fly.dev",
"https://XXX-ui.fly.dev:3000",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.add_middleware(
    SessionMiddleware,
    secret_key=SECRET_KEY,
    https_only=True,
    max_age=3600,
    same_site="none",
    domain="XXX-ui.fly.dev",
)

Checking the devtools when I make the request, you can see the attempt is made to store the cookie, as shown by this screenshot:

attempt to store cookie

The error I get is: Cookie “session” has been rejected for invalid domain.

It says in the SessionMiddleware docs for Starlette that session cookies can be configured for cross-domain requests, so long as the origins are specified (not * wildcard char).

So what am I missing here?

Thanks!!


Solution

  • After reading these MDN cookie docs, I realized that you simply cannot store cookies on cross-domain requests (xxx-UI.fly.dev cannot save a cookie from xxx-API.fly.dev).

    The solution was to purchase my own unique domain, and then use DNS to route traffic to either the UI (domain) or the API (a subdomain of my purchased domain). HTTP only cookies can be set for subdomains, but not cross-domain.

    This ultimately resulted in the following domain scheme:

    the subdomain strategy works well.

    For the FastAPI Session Middleware config, it looked like this:

    app.add_middleware(
        SessionMiddleware,
        secret_key=SECRET_KEY,
        https_only=True,
        # 3600s = 1hr
        max_age=3600,
        same_site="none",
        domain=".my-app.com",
    )
    

    The . prefixing the domain in .my-app.com allows all subdomains of my-app.com to be accepted as well.