admin管理员组

文章数量:1355670

In my fastapi app, I have an endpoint "/refresh" which is called automatically from the flutter mobile app when a previous call (with the access token in the header) to a protected endpoint failed because the access token was expired. "/refresh" is called with the refresh_token in the request body, the endpoint looks like this:

@router.post("/refresh/", response_model=TokenResponse)
async def refresh_token(
    refresh_request: RefreshRequest,
    supabase: Client = Depends(get_supabase_client),
):
    if refresh_request.refresh_token is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Refresh token is required",
        )

    try:
        print(f"Received refresh token: {refresh_request.refresh_token}")
        response = supabase.auth.refresh_session(refresh_request.refresh_token)

        if response.session:
            print(f"New refresh token: {response.session.refresh_token}")
            return TokenResponse(
                access_token=response.session.access_token,
                refresh_token=response.session.refresh_token,
                token_type="bearer",
                expires_in=response.session.expires_in,
                expires_at=response.session.expires_at or 0,
            )
        else:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid refresh token",
            )
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=f"Token refresh failed: {str(e)}",
        )

Context: Calling the endpoint "/login" returns access token and refresh token. While the access token is valid, I can call "/refresh" and all other protected routes with the access token and it works fine. But as soon as the access token is expired, and I call "/refresh" with the refresh token, then i get this error:

{
  "detail": "Token refresh failed: Invalid Refresh Token: Already Used"
}

This behavior is the same when i test locally with Postman/ Bruno.

EDIT:

When I islotate the issue to a separate python script and set in supabase access token expiration time to 30 seconds: The script first logs in, then waits 30 seconds, then tries to refresh, then fails ONLY when i pass the refresh token to SUPABASE.auth.refresh_session(token.refresh_token). without the token.refresh_token argument it works fine. but this obviously can not work with fastapi or where should supabase then know for which user to refresh the session?

The script:

class TokenResponse(BaseModel):
    access_token: str
    refresh_token: str
    token_type: str
    expires_in: int
    expires_at: int


def countdown(seconds):
    for i in range(seconds, 0, -1):
        print(f"{i} seconds remaining")
        time.sleep(1)
    print("Time's up!")


def login_and_refresh():
    SUPABASE = create_client(
        SUPABASE_URL,
        SUPABASE_KEY,
    )

    print("Logging in...")
    login_response = SUPABASE.auth.sign_in_with_password(
        {
            "email": "[email protected]",
            "password": "super-secret",
        }
    )

    if not login_response.user or not login_response.session:
        print("Invalid credentials")
        return

    print("LOGGED IN!")
    token = TokenResponse(
        access_token=login_response.session.access_token,
        refresh_token=login_response.session.refresh_token,
        token_type="bearer",
        expires_in=login_response.session.expires_in,
        expires_at=login_response.session.expires_at or 0,
    )

    print("Initial access_token:", token.access_token[:10] + "...")
    print("Initial refresh_token:", token.refresh_token[:10] + "...")

    # Wait for access token to be close to expiration
    countdown(31)  # Wait for 31 seconds

    print("Attempting to refresh token...")
    try:
        user = SUPABASE.auth.get_user(token.access_token)
        print("user", user)
        refresh_response = SUPABASE.auth.refresh_session(token.refresh_token)
        if not refresh_response.session:
            print("Failed to refresh session: No session returned")
            return

        new_token = TokenResponse(
            access_token=refresh_response.session.access_token,
            refresh_token=refresh_response.session.refresh_token,
            token_type="bearer",
            expires_in=refresh_response.session.expires_in,
            expires_at=refresh_response.session.expires_at or 0,
        )

        print("Token refreshed successfully")
        print("New access_token:", new_token.access_token[:10] + "...")
        print("New refresh_token:", new_token.refresh_token[:10] + "...")


    except Exception as e:
        print(f"Error refreshing token: {str(e)}") 

if __name__ == "__main__":
    login_and_refresh()

What am I doing wrong? How can I return a valid pair of access and refresh token using the old refresh token once the access token is expired?

Thanks for your help!

本文标签: