[Python] How to set up authorization with OAuth2 (React + FastAPI)

Discussão em 'Python' iniciado por Stack, Julho 3, 2025.

  1. Stack

    Stack Membro Participativo

    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...

Compartilhe esta Página