Hi there. I'm having an annoying issue during local development and I can't figure it out after Goog...
a
Hi there. I'm having an annoying issue during local development and I can't figure it out after Googling and searching here.
sIRTFrontend
keeps getting reset to
remove
after logging in. I'm using code from the Vue.js example included in the web-js repo (https://github.com/supertokens/supertokens-web-js/tree/master/examples/vuejs/with-thirdpartyemailpassword), but with EmailPassword instead of ThirdPartyEmailPassword. That's the only code change from the sample> I'm using a Python backend using FastAPI, running on
http://localhost:8000
. Frontend is running at
http://localhost:8080
. Here are my settings: Frontend:
Copy code
js
VITE_API_URL=http://localhost:8000
VITE_API_BASEPATH=/api/v1/auth
VITE_WEB_URL=http://localhost:8080
Copy code
js
const apiDomain = import.meta.env.VITE_API_URL;
const apiBasePath = import.meta.env.VITE_API_BASEPATH;

SuperTokens.init({
  appInfo: {
    appName: "Test",
    apiDomain,
    apiBasePath,
  },
  recipeList: [Session.init(), EmailPassword.init()],
  // enableDebugLogs: true,
});
Backend:
Copy code
python
SUPERTOKENS_URL = os.environ.get("SUPERTOKENS_URL", "http://localhost:3567")
SUPERTOKENS_API_KEY = os.environ.get("SUPERTOKENS_API_KEY", "someRandomKey")
SUPERTOKENS_WEBSITE_DOMAIN = os.environ.get(
    "SUPERTOKENS_WEBSITE_DOMAIN", "http://localhost:8080"
)
SUPERTOKENS_API_DOMAIN = os.environ.get(
    "SUPERTOKENS_API_DOMAIN", "http://localhost:8000"
)
Copy code
python
supertokens_init(
    app_info=InputAppInfo(
        app_name="Test",
        api_domain=SUPERTOKENS_API_DOMAIN,
        website_domain=SUPERTOKENS_WEBSITE_DOMAIN,
        api_base_path="/api/v1/auth",  # Remember to set this on the frontend as well
    ),
    supertokens_config=SupertokensConfig(
        connection_uri=SUPERTOKENS_URL, api_key=SUPERTOKENS_API_KEY
    ),
    framework="fastapi",
    recipe_list=[
        st_session.init(),  # initializes session features
        st_emailpassword.init(),
        st_userroles.init(),
    ],
    mode="asgi",
)
Adding CORS as well, after routes:
Copy code
python
origins = [SUPERTOKENS_WEBSITE_DOMAIN]
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    # allow_methods=["*"],
    allow_methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
    expose_headers=["X-Process-Time"],
    # allow_headers=["*"],
    allow_headers=get_all_cors_headers(),
)
It seems to be a CORS issue, since starting a browser with CORS disabled (via
open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security
under macOS) makes sessions work just fine, no issues. But I'd like to develop using my regular browser, so I'm trying to figure out what's going on.
I get a
401
at
/api/v1/auth/session/refresh
when refreshing the webapp (after clearing cookies), so naturally
sIRTFrontend
gets set to
remove
again.
Response from the server at
/api/v1/auth/signin
is 200 and contains the
id-refresh-token
header.
I'm sure everything will work in production when using https, but the developer experience is frustrating. I'm sure it's just me, as others seem to be doing just fine using the React sample.
r
Hey!
Does the browser console have info about what CORS error you get?
a
No CORS errors in the console
r
Ok. Can you enable debug logs on the backend and show me the output when the refresh api is called?
There should be a troubleshooting section in the docs
Which shows you how to enable debug logs
a
Copy code
python
com.supertokens {"t": "2022-08-17T16:52:03.231Z", "sdkVer": "0.10.2", "message": "middleware: Started", "file": "supertokens.py:394"}

com.supertokens {"t": "2022-08-17T16:52:03.231Z", "sdkVer": "0.10.2", "message": "middleware: requestRID is: session", "file": "supertokens.py:407"}

com.supertokens {"t": "2022-08-17T16:52:03.231Z", "sdkVer": "0.10.2", "message": "middleware: Checking recipe ID for match: session", "file": "supertokens.py:418"}

com.supertokens {"t": "2022-08-17T16:52:03.231Z", "sdkVer": "0.10.2", "message": "middleware: Matched with recipe ID: session", "file": "supertokens.py:440"}

com.supertokens {"t": "2022-08-17T16:52:03.231Z", "sdkVer": "0.10.2", "message": "middleware: Request being handled by recipe. ID is: /session/refresh", "file": "supertokens.py:452"}

com.supertokens {"t": "2022-08-17T16:52:03.232Z", "sdkVer": "0.10.2", "message": "refreshSession: Started", "file": "recipe/session/recipe_implementation.py:245"}
Copy code
python


com.supertokens {"t": "2022-08-17T16:52:03.232Z", "sdkVer": "0.10.2", "message": "refreshSession: UNAUTHORISED because idRefreshToken from cookies is undefined", "file": "recipe/session/recipe_implementation.py:249"}

com.supertokens {"t": "2022-08-17T16:52:03.232Z", "sdkVer": "0.10.2", "message": "errorHandler: Started", "file": "supertokens.py:468"}

com.supertokens {"t": "2022-08-17T16:52:03.232Z", "sdkVer": "0.10.2", "message": "errorHandler: Error is from SuperTokens recipe. Message: Session does not exist. Are you sending the session tokens in the request as cookies?", "file": "supertokens.py:469"}

com.supertokens {"t": "2022-08-17T16:52:03.232Z", "sdkVer": "0.10.2", "message": "errorHandler: Checking recipe for match: session", "file": "supertokens.py:480"}

com.supertokens {"t": "2022-08-17T16:52:03.232Z", "sdkVer": "0.10.2", "message": "errorHandler: Matched with recipeID: session", "file": "supertokens.py:486"}

com.supertokens {"t": "2022-08-17T16:52:03.232Z", "sdkVer": "0.10.2", "message": "errorHandler: returning UNAUTHORISED", "file": "recipe/session/recipe.py:210"}

com.supertokens {"t": "2022-08-17T16:52:03.232Z", "sdkVer": "0.10.2", "message": "Sending response to client with status code: 401", "file": "utils.py:123"}

INFO:     127.0.0.1:55406 - "POST /api/v1/auth/session/refresh HTTP/1.1" 401 Unauthorized
Cookie sent with that request:
Copy code
Cookie: sRefreshToken="9ESH0dKRXoHfx/HYSqiJm0pOtExnDCi5djAzg3okc/y4kyFZ6U5IQ5jYEFYO7ocdXGL3xDMG10QnVChpH2l7DZdIsRitFCWCjmDAnsGOtadjBcOZecngXPzkH3fm3xkV/t75McfHWmQ6eSjbhVo8tYFhX/F2OMa2kal5W54H20Y/kjW6tcNKhAjlP64ekDSvj11NYzgAJ1qJ6Gz%2BwOq1wi0wYxwGWz50DHBQuzHGnWMbWHuaf0R%2BPBSmL3TcZjuvidJM0l0N%2BJs8NDq9qBZY.b63b955f264501e6e0f383647bf279090ad2bac85be78de7aa92f42c90af9560.V2"
(this is after clearing all cookies and refreshing the webapp)
r
right. So one more cookies needs to go along with it, which is called sIdRefreshToken cookie. That is also set when you login. Did you remove that cookies as well?
a
Yup, used this button here
r
see the cookie store on your API domain.
not website domain
a
I have no idea how the first cookie got sent, after I cleared them 😱
r
so navigate to the api domain on your browser and see the cookie store there.
a
That's the only one
r
What is your API domain?
localhost:8080?
hmm right. I just saw above
a
API is 8000, frontend is 8080
r
visit
localhost:8000/api/v1/auth/session/refresh
-> do you see a refresh token in the cookie store?
a
Ooh, I do! (That URL returned a 404, as expected)
r
right.. so this is strange. Cause if sRefreshToken is there, then the other token should also be there. Can you manually delete the sRefreshToken and try to login again? And then see the cookies?
a
I tried, I do see the other cookies at the API refresh URL, but the frontend still returns 401 on refresh (but 200 on signing)
r
right. So this time, what are the cookies sent in the refresh request?
a
Wait, the cookies went away on the API!
Hold on, let me start fresh. Deleting cookies on both frontend and the API route, then visiting the webapp home.
r
yup
a
Ok, no cookie session sent by the frontend on the first refresh request
r
right. So we need to find out why that
why that's the case.
a
sIRTFrontend set to remove on the frontend
... and on the API route now
r
Can I see a screenshot of the the set-cookie headers from the sign in API?
a
I'm running Supertokens Core in docker, if that's relevant
Maybe I missed some setting there?
r
no.. that shouldn't be the issue.. it's most likely an issue with the appInfo config or session.init config.
this please.
a
r
not the refresh API, the sign in API
a
Ah, sorry. Let me do that next step
r
the response headers.
yup
a
Adding response now
r
seems right.
a
Hmm,
Path=/
on sIdRefreshToken?
r
yes thats' expected
a
Ah, right
So the web SDK must be doing something funky
r
can you make a regular API call that uses the verify_session function on the backend?
does that send the sAccessToken and the sIdRefreshToken header in the request?
a
The API endpoint now has a full set of cookies
r
yes. This is correct.
a
I'll have to write that, didn't even get to that point yet...
r
ok. So how are you triggering the refresh flow right now?
a
And the client seems fine except for the remove
I'm not, the only time I've seen the SDk hit /refresh is when I remove all cookies and visit the webapp home
r
oh right.. i see. Are you calling the sign in API yourself? Or using the web-js SDK?
a
Web-js SDK, unchanged from the Vue example other than replacing ThirdParty with only EmailPassword
So it's fetch and the interceptors do trigger, according to debugging
r
hmm.
a
It's gremlins
r
Can you enable frontend logs and then call the sign in API?
you should see several logs on the browse console
a
It's big
Doing crazy stuff like replacing
sIRTFrontend
with the
sIdRefreshToken
value only sorta works. The home route doesn't redirect to /auth anymore, but neither does it progress past
Session.doesSessionExist()
r
oh i think i know whats wrong
The signin response should have
Access-Control-Expose-Headers: front-token, id-refresh-token
in the response headers.
But it doesn't
I think the:
Copy code
origins = [SUPERTOKENS_WEBSITE_DOMAIN]
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    # allow_methods=["*"],
    allow_methods=["GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH"],
    expose_headers=["X-Process-Time"],
    # allow_headers=["*"],
    allow_headers=get_all_cors_headers(),
)
code is setting the
Access-Control-Expose-Headers
to always only have
X-Process-Time
regardless of what the SDK sets in the response for the API that it controls.
a
Ah, right!, It's overwriting!
I wonder if there's a way to append, or if I should add the CORSMiddleware twice
r
try changing to:
Copy code
expose_headers=["X-Process-Time", "front-token", "id-refresh-token"],
a
You're awesome!
Thank you for catching that, would've never imagined
r
If you are adding
CORSMiddleware
, then why do you also need to add the other items in the code?
a
I had
X-Process-Time
as a frontend-facing performance metric
r
hmm i see.
a
Copy code
python
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(round(process_time, 3))
    return response
r
great! im glad it worked out 🙂
a
Thanks for your help!
r
it's there in the list already:
Copy code
Access control expose headers not set properly - which prevents frontend from reading the id-refresh-token, which prevents setting that state on the frontend.
it is a rather long list 😅 .. browsers make it hard to use cookies hehe..
a
That's true. I just tested, leaving out
expose_headers
works with my custom header, whereas
expose_headers = ["*"]
doesn't work, browsers hate the star on insecure connections. Anyway, thanks for sorting me out. Have a great day!
r
Happy to help 🙂