On the frontend I fetch my /whoami endpoint. // App.tsx try { const response = await axios.get(`${BACKEND_URL}/auth/whoami`, { withCredentials: true, }); return response.data; } catch (error) { if (axios.isAxiosError(error)) { if (error.response?.status && [400, 401, 403].includes(error.response.status)) { setAuthError('no_access') } if (error.response?.status && [500, 502].includes(error.response.status)) { setAuthError("service_unavailable"); } } return null } This endpoint checks cookie and if its not there, redirecting to next /login endpoint # endpoints.py @auth_router.get("/whoami") async def get_user_data( request: Request, user: User | RedirectResponse = Depends(get_current_user) ) -> User | RedirectResponse: return user @auth_router.get("/login") async def authenticate( redirect_url: str = "/", idam_service: IDAMService = Depends(get_idam_service), ) -> RedirectResponse: if not redirect_url.startswith(("*MY_SITE_URL*", "/")): raise HTTPException(400, "Invalid redirect_url") response = RedirectResponse(idam_service.code_url) response.set_cookie("redirect_url", redirect_url, secure=True, httponly=True, samesite="lax") return response @auth_router.get("/sso") async def code_handling( request: Request, code: str | None = None, idam_service: IDAMService = Depends(get_idam_service), token_service: TokenService = Depends(get_token_service), ) -> RedirectResponse: try: if code is None: raise HTTPException(status_code=400, detail="No code") token = await idam_service.request_token(code) token_data = idam_service.decode_token(token) user = idam_service.create_user(token_data) user_token = token_service.generate_token(user.model_dump()) redirect_url = request.cookies.get("redirect_url", "/") if redirect_url.startswith("*MY_SITE_URL*"): response = RedirectResponse(redirect_url) response.set_cookie( "idam_user", user_token, httponly=True, secure=True, samesite="lax", ) else: response = RedirectResponse(f"{redirect_url}?token={user_token}") return response except InvalidAuthorizationCode: raise HTTPException(status_code=400, detail="wrong code") except (InvalidTokenError, UserValidationError): raise HTTPException(status_code=401, detail="auth error") except IDAMConnectionError: raise HTTPException(status_code=502, detail="auth service is unavailable") except IDAMException: raise HTTPException(status_code=500, detail="internal service error") # dependencies.py def get_current_user( request: Request, token_service: TokenService = Depends(get_token_service) ) -> User | RedirectResponse: referer = request.headers.get("referer") origin = request.headers.get("origin") redirect_url = referer or origin or "*MY_SITE_URL*" if not redirect_url.startswith("*MY_SITE_URL*"): redirect_url = "*MY_SITE_URL*" if not (token := request.cookies.get("idam_user")): return RedirectResponse(f"/api/auth/login?redirect_url={redirect_url}") try: user = token_service.decode_token(token) request.state.user = user return user except jwt.ExpiredSignatureError: return RedirectResponse(f"/api/auth/login?redirect_url={redirect_url}") except jwt.InvalidTokenError: raise HTTPException(403, "Invalid token") Login endpoint redirects to auth provider which sends authorization code, then auth provider redirects to /sso endpoint, where the code is exchanged to token and token is decoded. Also get_current_user is a dependency, which I use to defend my other endpoints. The problem is, that auth provider doesn't redirect to my redirect_url naturally, but it sends html page with js script, which already is redirecting. But that script can't run because I send xhr requests (via axios). If I try to redirect with window.location.href = "...", I won't be able to catch backend exceptions (token, code handling, etc.). Also if I go this way, I can't realize how to defend my routes with my dependency, because I always request my backend through axios and if token expires, it also won't be able to run js response from auth provider. I guess my case is pretty common, but somehow I really ran into difficulties. Which way I should go? Thank you. Continue reading...