Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

I followed this guide and it states the following to convert the auth code to access token, which is by using this library:

After your backend platform receives an authorization code from Google and verifies the request, use the auth code to obtain access and refresh tokens from Google to make API calls.

Follow the instructions starting at Step 5: Exchange authorization code for refresh and access tokens of the Using OAuth 2.0 for Web Server Applications guide.

However, I don't know what to set on my redirect_uri. If I use a registered redirect url and use flow.fetch_token(code=authoriztion_code), I get a bad request.

My current flow is to request an authorization code in my frontend (Vue) and pass it to my backend (Django) and have it verified. Then, convert it to access token, and make request to the api. Is this okay?

You must be logged in to vote

Replies: 1 comment · 5 replies

Comment options

I do not think redirect_uri is the problem. Step 5: Exchange authorization code for refresh and access tokens says "One of the redirect URIs listed for your project in the API Console Credentials page for the given client_id. " You seem to be doing exactly that.

Is there more details in the error message?

Also, could you double check whether you are passing in the auth code and not the authorization response?

You must be logged in to vote
5 replies
@abcd-arl
Comment options

Thank you so much for responding.

It's giving me (invalid_grant) Bad Request. And yes, I'm even hardcoding the auth code.

Below is my code:

def post(self, request):
        authoriztion_code = request.data.get("authorization_code")
        if authoriztion_code is None:
            return Response(
                {"error": "Please provide a valid authorization code"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        flow = Flow.from_client_secrets_file(
            "path/to/client_secret.json",
            scopes=[
                "https://www.googleapis.com/auth/userinfo.profile",
                "https://www.googleapis.com/auth/userinfo.email",
                "https://www.googleapis.com/auth/user.birthday.read",
            ],
            redirect_uri="http://127.0.0.1:8000/google-authorize/",
        )

        flow.fetch_token(code=authoriztion_code)
        credential = flow.credentials

        print(credential)
       # planning to make the API calls here

Also, is redirect necessary? I think I don't need to redirect the user as I am using a separate client app for my frontend. Sorry, this is my first time implementing this.

@sai-sunder-s
Comment options

As per the linked documentation, if it is invalid grant error, it means that something is wrong with the auth code. I do not think there is anything wrong with the code you have shared here.

Can you share the code you used to get the authorization code?

@abcd-arl
Comment options

@sai-sunder-s Sure. Basically,

  1. I am trying to get the id_token from the "Sign in with Google" button.
  2. I would then pass it to my backend to verify and decode the token (the decoded info is what I use to login or register the user).
  3. If everything is fine, I will then try to get the auth code with initCodeClient and pass it to my server to make the request for the birthday of the user.
<script setup>
import { onMounted } from "vue";

onMounted(() => {
  const script = document.createElement("script");
  script.src = "https://accounts.google.com/gsi/client";
  script.async = true;
  document.body.insertBefore(script, document.body.firstChild);

  script.onload = () => {
    window.onload = function () {
      google.accounts.id.initialize({
        client_id: import.meta.env.VITE_GOOGLE_CLIENT_ID,
        callback: handleCredentialResponse,
        auto_select: false,
        login_uri: "http://localhost:5173",
      });
      google.accounts.id.renderButton(document.getElementById("buttonDiv"), {
        theme: "outline",
        size: "large",
      });
    };

    const getAuthorizationCode = () => {
      const client = google.accounts.oauth2.initCodeClient({
        client_id: import.meta.env.VITE_GOOGLE_CLIENT_ID,
        scope: "https://www.googleapis.com/auth/user.birthday.read",
        ux_mode: "popup",
        callback: (response) => {
          console.log("Auth code response: ", response);
          // planning to pass the code to the backend here
        },
        select_account: false,
        login_hint: "myaccount@gmail.com", // my email here
      });

      client.requestCode();
    };

    const login = async (credential) => {
      const response = await fetch("http://localhost:8000/account/google/", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          credential: credential,
        }),
      });

      if (response.ok) {
        const data = await response.json();
        console.log("Authentication in the server: ", data);
        getAuthorizationCode(credential);
      } else {
        console.error("Failed to login with Google");
      }
    };

    function handleCredentialResponse(response) {
      console.log("Credential response: ", response);
      login(response.credential);
    }
  };

  script.onerror = (error) => {
    console.error("Failed to load Google Identity Services library", error);
  };
});
</script>

Apologies if you find it messy. I am yet to figure out how I could write it cleaner and more proper. I've been experimenting how I could make the user only prompt to login once (currently twice, one for authentication and one for authorization). But I believe I am getting the right auth code correctly. The following is the types of response I get:

Credential response: {
    "clientId": "286817242560-3elpadlen86leq....2i.apps.googleusercontent.com",
    "client_id": "286817242560-3elpadlen86le....v02i.apps.googleusercontent.com",
    "credential": "....", // random characters
    "select_by": "btn"
}

Authentication in the server:  {detail: 'Success'}

Auth code response: {
    "code": "4/0AeaYSHAEhUuiyGSwqCQi4V....",
    "scope": "email profile https://www.googleapis.com/auth/user.birthday.read openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
    "authuser": "0",
    "prompt": "consent"
}

Edit: I am passing only the 'code' property from auth code response to the 'fetch_token'.

@sai-sunder-s
Comment options

Not sure if I understand the code correctly. But looking at your responses, if you are taking only the code part of the response and using it to fetch the token, that should be ok.

Maybe you could post a question in Stack Overflow with google-oauth tag.

Also, if your goal is to get both idtoken and access token, you do not need to make the user login twice. While trying to get access token, in addition to the birthday scope, request for openid as well.

@criiptico
Comment options

Any updates on this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
🙏
Q&A
Labels
None yet
3 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.