I have a fullstack app made up of a React frontend and a FastAPI (python) backend, both of which are currently being served locally on 127.0.0.1 (localhost).
BACKEND:
Here is the code for my backend server, with CORSMiddleware and SessionMiddleware configured:
from fastapi import FastAPI, HTTPException, status, Request
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.sessions import SessionMiddleware
import spotipy
from spotipy.oauth2 import SpotifyOAuth
app = FastAPI()
# generated via cmd: 'openssl rand -hex 32'
SECRET_KEY = "XXX"
origins = [
"http://localhost",
"https://localhost",
"http://localhost:3000",
"https://localhost:3000",
"http://127.0.0.1",
"https://127.0.0.1",
"http://127.0.0.1:3000",
"https://127.0.0.1:3000",
]
# https://www.starlette.io/middleware/#corsmiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# https://www.starlette.io/middleware/#sessionmiddleware
app.add_middleware(
SessionMiddleware,
secret_key=SECRET_KEY,
https_only=True,
max_age=3600, # 3600s = 1hr, the life of spotify access token
same_site="none",
)
CLIENT_ID = "YYYY"
CLIENT_SECRET = "ZZZ"
OAUTH_REDIRECT_URI = "http://127.0.0.1:3000/"
SCOPE = "playlist-read-private playlist-read-collaborative user-read-private user-read-email"
# Docs: https://spotipy.readthedocs.io/en/2.24.0/#module-spotipy.oauth2
sp_oauth = SpotifyOAuth(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, redirect_uri=OAUTH_REDIRECT_URI, scope=SCOPE)
@app.post("/spotify-auth", tags=["auth"])
def exchange_token(code: str, request: Request):
# Exchanges token, with check_cache=False to ensure new users can be registered
token_info = sp_oauth.get_access_token(code=code, check_cache=False)
print(token_info)
# https://www.starlette.io/middleware/#sessionmiddleware
request.session["access_token"] = token_info["access_token"]
request.session["refresh_token"] = token_info["refresh_token"]
return {"tokenSaved": True}
In other endpoints, i.e. /user
, when I try to access request.session.get("access_token")
, I get absolutely nothing back.
FRONTEND:
here is the axios POST request from my frontend (react/typescript) that will hit the /spotify-auth
endpoint on my backend:
const exchangeSpotifyAuthToken = async (
code: string,
): Promise<AccessTokenReponse> => {
const searchParams = new URLSearchParams();
searchParams.append("code", code);
const response = await axios.post(
`${API_URL_BASE}/spotify-auth?` + searchParams.toString(),
{ withCredentials: true },
);
return response.data;
};
But as I said, my attempts to use the access_token in other routes, which should be accessible via request.session.get("access_token")
all fail. Each of my axios requests also include this { withCredentials: true }
config, so that shouldn't be the problem.
THINGS I'VE TRIED:
I've seen a lot of CORS related posts regarding this issue, and I think I have implemented the requirements as follows:
127.0.0.1:3000
)allow_credentials=True
on backendwithCredentials: true
BROWSER TOOLS:
I also checked the browser tools Network tab and saw that my session was being correctly recorded following my POST request to login/authenticate:
But any followup requests to my backend do not contain this session cookie, so they promptly fail my authentication check:
Shouldn't my session cookie show up in those request headers so that my backend can retrieve them? What am I doing wrong?
Thanks!
I must not have been keeping good track of which possible solution I was trying... I think I may have mix and mashed them, which resulted in the cookies not being stored.
Ultimately I had domain=".localhost"
in my SessionMiddleware, like so:
app.add_middleware(
SessionMiddleware,
secret_key=SECRET_KEY,
https_only=True,
max_age=3600, # 3600s = 1hr, the life of spotify access token
same_site="none",
domain=".localhost" # THIS WAS WHAT WAS KILLING ME
)
This was a suggestion I had seen online, but that must have been when I was serving my frontend on localhost
and not 127.0.0.1
.
Now that both my backend and frontend are being served on 127.0.0.1
, I recognized this faulty domain
specification and removed it. The backend session middleware now correctly sends cookies to the default domain (that of the backend, 127.0.0.1
) and my cookies are being correctly stored!!
Still have to confirm that I can actually use the session cookies as intended in my other API endpoints, but I'll just enjoy the moment and worry about that tomorrow :)
Thanks for the comments/suggestions!