Search code examples
oauth-2.0google-oauthfastapigoogle-api-python-client

Google OAuth2.0 - Unintended redirecting to http


I'm trying to setup a Google OAuth for my FARM stack app.

My problem is: My flow and code is working when I'm running the app in my local. But It's not working in production. (Because Google doesn't allow me to enter a redirect url in "http" scheme if my OAuth app is set to Production mode.) I have all the requirements satisfied, e.g ClientID, Redirect URI's.

Here's the steps I follow:

1- Created ClientId for Web Application. 2- Added the required redirect URIs. 3- In FastAPI I have this code:

# config["CLIENT_URL"] equals to my React App's url.
@router.get('/google_cb')  # 1
async def login(request: Request):
    redirect_uri = request.url_for('google')
    return await oauth.google.authorize_redirect(request, redirect_uri)


@router.get("/google", name="google")  # 2
async def google_oauth(request: Request):
    try:
        access_token = await oauth.google.authorize_access_token(request)
        user = user_service.find_one(
            {"email": access_token['userinfo']['email'], "authentication_method": AuthenticationMethod.GOOGLE})
        if user:
            access_token_expires = timedelta(minutes=config["Auth"]["token_expiry"])
            access_token = create_access_token(
                data={"sub": user.id, "scopes": []},
                expires_delta=access_token_expires,
            )
            return RedirectResponse(url=f'{config["CLIENT_URL"]}/oauthcb/' + access_token)
        invite = invites_repository.find_one({"email": access_token['userinfo']['email'], "status": "Pending"})
        if not invite:
            raise HTTPException(status_code=404, detail="Invite not found")
        created_user = user_service.create_user(
            {"email": invite["email"], "hashed_password": "google_auth",
             "tenant_id": invite["tenant_id"], "disabled": False, "first_name": access_token['userinfo']['given_name'],
             "last_name": access_token['userinfo']['family_name'], "is_admin": False, "fireflies_id": None,
             "authentication_method": "Google", "external_auth_data": access_token})
        access_token_expires = timedelta(minutes=config["Auth"]["token_expiry"])
        access_token = create_access_token(
            data={"sub": str(created_user.inserted_id), "scopes": []},
            expires_delta=access_token_expires,
        )
        invites_repository.delete({"_id": invite["_id"]})
        return RedirectResponse(url=f'{config["CLIENT_URL"]}/oauthcb/' + access_token)
    except OAuthError as e:
        print(e)
        return RedirectResponse(url=f'{config["CLIENT_URL"]}')

4- In React I have this code:

process.env.REACT_APP_API_URL is set to https://my.api.url
<Button
            icon={<GoogleOutlined />}
            onClick={() => {
              window.location.replace(
                `${process.env.REACT_APP_API_URL}/oauth/google_cb`
              );
            }}
          >
            Login with Google
          </Button>

5- Flow: User presses Login With Google button in React App, user hits the https://my.api.url/google_cb endpoint in the backend. The backend redirects the user to the page that google provides to enter their email and password. After they log in, google should redirect the user to https://my.api.url/google endpoint and I do my process here.

Problem is: Although I'm redirecting the user from React to the https://my.api.endpoint/google_cb I'm getting an error "redirect_url_mismatch", and in the error it says "redirect_url=http://my.api.endpoint/google_cb" And this causes my problem. I'm sure about this because I swithced the mode for my OAuth App from Google Console to "Test" from "Production" and It worked.

In production mode Google doesn't allow me to enter http url. They just allow me to enter https.

But I'm pretty pretty sure that I'm redirecting the user to https instead of http.

I wonder if anyone faced with the same issue before.


Solution

  • Solved, the issue is request.url_for function returns the url as "http" instead of "https".

    Updated my code as:

    async def login(request: Request):
        redirect_uri = request.url_for('google')
        if request.base_url.hostname == 'localhost': 
    # I consider about localhost for testing purposes, you may not have this.
            redirect_uri = redirect_uri.replace('https', 'http')
        else:
            redirect_uri = redirect_uri.replace('http', 'https')
        return await oauth.google.authorize_redirect(request, redirect_uri)
    

    Make sure your backend redirects to the correct url!