14

I got the authorization code following this document. But when I tried to get access token, I always got errors. Can anyone help me ?

public String AccessToken()
{
    String accessToken = "";
    StringBuilder strBuild = new StringBuilder();

    String authURL = "https://accounts.google.com/o/oauth2/token?";
    String code = "4/SVisuz_x*********************";
    String client_id = "******************e.apps.googleusercontent.com";
    String client_secret = "*******************";
    String redirect_uri = "urn:ietf:wg:oauth:2.0:oob";
    String grant_type="authorization_code";
    strBuild.append("code=").append(code)
            .append("&client_id=").append(client_id)
            .append("&client_secret=").append(client_secret)
            .append("&redirect_uri=").append(redirect_uri)
            .append("&grant_type=").append(grant_type);
    System.out.println(strBuild.toString());
    try{
        URL obj = new URL(authURL);
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();
        con.setDoOutput(true);
        con.setRequestMethod("POST");

        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        con.setRequestProperty("Host", "www.googleapis.com");

        //BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(con.getOutputStream()));
        //bw.write(strBuild.toString());
        //bw.close();

        DataOutputStream wr = new DataOutputStream(con.getOutputStream());
        wr.writeBytes(strBuild.toString());
        wr.flush();
        wr.close();

        //OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream());
        System.out.println(con.getResponseCode());
        System.out.println(con.getResponseMessage());             

    } catch (Exception e)
    {
        System.out.println("Error.");
    }
    return "";
}

when I ran this code, the output is: 400 Bad Request

tqjustc
  • 2,984
  • 6
  • 25
  • 41
  • What errors do you get? – Jay Lee Mar 25 '16 at 13:15
  • @JayLee the output is one word: `Error` – tqjustc Mar 25 '16 at 18:25
  • do you see msg "Invalid string value" in your output? – Bhunnu Baba Mar 28 '16 at 05:27
  • @tqjustc Would you please provide **e.printStackTrace();** from your catch block? It will help to give you proper solution. – SkyWalker Mar 29 '16 at 18:13
  • @SkyWalker It still printed: 400, Bad request – tqjustc Mar 30 '16 at 04:40
  • @tqjustc Would you please check my updated answer? I have provided 2 running code as sample, some precautions and token related info. Hope it will help you. – SkyWalker Apr 01 '16 at 02:18
  • Why don't you want to use google's Gmail Java API? https://developers.google.com/gmail/api/quickstart/java – SlavaSt Apr 01 '16 at 11:19
  • I am facing the same error that is { "error": "invalid_grant", "error_description": "Bad Request" } I am using the endpoint via postman can anyone tell me what i should use in grant_type parameter ? – Tabish Mar 28 '20 at 04:34
  • @tqjustc I am trying to make Rest calls to the gmail api server without the gmail client library and without the user prompt to send emails in gmail. I see you are making a similar call in native java. In your java code above, where did your value for the "code" url parameter? Also the since I am programming the REST request to gmail api from my ionic/angular2 mobile(android/ios) app, and the link you have says "The client_secret is not applicable to requests from clients registered as Android, iOS, or Chrome applications." do I skip the "client_secret" or do I replace it with something else? – Computer Science May 11 '20 at 23:27

7 Answers7

16

How to get access token using gmail api?

Ans: As per your following tutorial, you are using OAuth 2.0. So there is a basic pattern for accessing a Google API using OAuth 2.0. It follows 4 steps:

  1. Obtain OAuth 2.0 credentials from the Google Developers Console.
  2. Obtain an access token from the Google Authorization Server.
  3. Send the access token to an API.
  4. Refresh the access token, if necessary.

For details, you can follow the tutorial - Using OAuth 2.0 to Access Google APIs

You have to visit the Google Developers Console to obtain OAuth 2.0 credentials such as a client ID and client secret that are known to both Google and your application


Root Cause Analysis:

Issue-1:

After studying your code, some lacking are found. If your code runs smoothly, then the code always give an empty string. Because your AccessToken() method always return return "";

Issue-2:

 catch (Exception e)
    {
        System.out.println("Error.");
    }

Your try catch block is going exception block. Because, it seems that you have not completed your code properly. You have missed encoding as well as using JSONObject which prepares the access token. So it is giving output as

Error.

Solution:

I got that your code is similar with this tutorial

As your code needs more changes to solve your issue. So I offer you to use LinkedHashMap or ArrayList. Those will provide easier way to make solution. So I give you 2 sample code to make your life easier. You can choose any of them. You need to change refresh_token, client id, client secret and grant type as yours.

private String getAccessToken()
{
    try
    {
        Map<String,Object> params = new LinkedHashMap<>();
        params.put("grant_type","refresh_token");
        params.put("client_id",[YOUR CLIENT ID]);
        params.put("client_secret",[YOUR CLIENT SECRET]);
        params.put("refresh_token",[YOUR REFRESH TOKEN]);

        StringBuilder postData = new StringBuilder();
        for(Map.Entry<String,Object> param : params.entrySet())
        {
            if(postData.length() != 0)
            {
                postData.append('&');
            }
            postData.append(URLEncoder.encode(param.getKey(),"UTF-8"));
            postData.append('=');
            postData.append(URLEncoder.encode(String.valueOf(param.getValue()),"UTF-8"));
        }
        byte[] postDataBytes = postData.toString().getBytes("UTF-8");

        URL url = new URL("https://accounts.google.com/o/oauth2/token");
        HttpURLConnection con = (HttpURLConnection)url.openConnection();
        con.setDoOutput(true);
        con.setUseCaches(false);
        con.setRequestMethod("POST");
        con.getOutputStream().write(postDataBytes);

        BufferedReader  reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
        StringBuffer buffer = new StringBuffer();
        for (String line = reader.readLine(); line != null; line = reader.readLine())
        {
            buffer.append(line);
        }

        JSONObject json = new JSONObject(buffer.toString());
        String accessToken = json.getString("access_token");
        return accessToken;
    }
    catch (Exception ex)
    {
        ex.printStackTrace(); 
    }
    return null;
}

For accessing google play android developer api, you need to pass the previous refresh token to get access token

private String getAccessToken(String refreshToken){

HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/token");
try 
{
    List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(4);
    nameValuePairs.add(new BasicNameValuePair("grant_type",    "refresh_token"));
    nameValuePairs.add(new BasicNameValuePair("client_id",     GOOGLE_CLIENT_ID));
    nameValuePairs.add(new BasicNameValuePair("client_secret", GOOGLE_CLIENT_SECRET));
    nameValuePairs.add(new BasicNameValuePair("refresh_token", refreshToken));
    post.setEntity(new UrlEncodedFormEntity(nameValuePairs));

    org.apache.http.HttpResponse response = client.execute(post);
    BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
    StringBuffer buffer = new StringBuffer();
    for (String line = reader.readLine(); line != null; line = reader.readLine())
    {
        buffer.append(line);
    }

    JSONObject json = new JSONObject(buffer.toString());
    String accessToken = json.getString("access_token");

    return accessToken;

}
catch (IOException e) { e.printStackTrace(); }

return null;
}

Resource Link:

Hope that, this samples and resource link will help you to solve your issue and get access of access token.


What is 400 bad request?

Ans: It indicates that the query was invalid. Parent ID was missing or the combination of dimensions or metrics requested was not valid.

Recommended Action: You need to make changes to the API query in order for it to work.

For HTTP/1.1 400 Bad Request error, you can go through my another answer. It will help you to make sense about which host you need to use and which conditions you need to apply.

Why token expires? What is the limit of token?

A token might stop working for one of these reasons:

  1. The user has revoked access.
  2. The token has not been used for six months.
  3. The user changed passwords and the token contains Gmail, Calendar, Contacts, or Hangouts scopes.
  4. The user account has exceeded a certain number of token requests.

There is currently a limit of 25 refresh tokens per user account per client. If the limit is reached, creating a new token automatically invalidates the oldest token without warning. This limit does not apply to service accounts.

Which precautions should be followed?

Precautions - 1:

Some requests require an authentication step where the user logs in with their Google account. After logging in, the user is asked whether they are willing to grant the permissions that your application is requesting. This process is called user consent.

If the user grants the permission, the Google Authorization Server sends your application an access token (or an authorization code that your application can use to obtain an access token). If the user does not grant the permission, the server returns an error.

Precautions - 2:

If an access token is issued for the Google+ API, it does not grant access to the Google Contacts API. You can, however, send that access token to the Google+ API multiple times for similar operations.

Precautions - 3:

An access token typically has an expiration date of 1 hour, after which you will get an error if you try to use it. Google Credential takes care of automatically "refreshing" the token, which simply means getting a new access token.

Save refresh tokens in secure long-term storage and continue to use them as long as they remain valid. Limits apply to the number of refresh tokens that are issued per client-user combination, and per user across all clients, and these limits are different. If your application requests enough refresh tokens to go over one of the limits, older refresh tokens stop working.

Community
  • 1
  • 1
SkyWalker
  • 24,796
  • 7
  • 62
  • 118
  • Right now the problem is that I even cannot print out http message. It always get error. I checked your another answer. But I still cannot get the correct answer – tqjustc Mar 30 '16 at 04:37
  • @tqjustc I have update my answer with some samples and resource link. Please check and provide me update so that I can give you better feedback. – SkyWalker Mar 30 '16 at 07:57
3

You are not using the right endpoint. Try to change the authURL to https://www.googleapis.com/oauth2/v4/token

From the documentation:

To make this token request, send an HTTP POST request to the /oauth2/v4/token endpoint

The actual request might look like the following:

POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/v6xr77ewYqhvHSyW6UJ1w7jKwAzu&
client_id=8819981768.apps.googleusercontent.com&
client_secret=your_client_secret&
redirect_uri=https://oauth2-login-demo.appspot.com/code&
grant_type=authorization_code

Reference https://developers.google.com/identity/protocols/OAuth2InstalledApp#handlingtheresponse

newhouse
  • 896
  • 8
  • 11
0

For me your request is fine, I tried it using Curl, I also get a 'HTTP/1.1 400 Bad Request' with the reason why it failed 'invalid_grant' :

curl -X POST https://www.googleapis.com/oauth2/v4/token -d 'code=4/SVisuz_x*********************&client_id=*******************7vet.apps.googleusercontent.com&client_secret=***************&redirect_uri=https://oauth2-login-demo.appspot.com/code&grant_type=authorization_code'

I receive (HTTP/1.1 400 Bad Request) :

{
 "error": "invalid_grant",
 "error_description": "Code was already redeemed."
}

Now using HttpClient from Apache :

URL obj = new URL(authURL);
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(authURL);
post.addHeader("Content-Type", "application/x-www-form-urlencoded");
post.addHeader("Host", "www.googleapis.com");
post.setEntity(new StringEntity(strBuild.toString()));

HttpResponse resp = client.execute(post);
System.out.println(resp.getStatusLine());
System.out.println(EntityUtils.toString(resp.getEntity()));

I see in my console :

HTTP/1.1 400 Bad Request
{
 "error": "invalid_grant",
 "error_description": "Code was already redeemed."
}

Are you sure the code you are using is still valid ? Can you try with a new one ?

Benoit Vanalderweireldt
  • 2,773
  • 2
  • 16
  • 29
0

Firstly, you must look this page :

https://developers.google.com/gmail/api/auth/web-server#create_a_client_id_and_client_secret

The value you see in the query parameter code is a string you have to post to google in order to get the access token.

After the web server receives the authorization code, it may exchange the authorization code for an access token and a refresh token. This request is an HTTPS POST to the URL https://www.googleapis.com/oauth2/v3/token POST /oauth2/v3/token HTTP/1.1 content-type: application/x-www-form-urlencoded

code=4/v4-CqVXkhiTkn9uapv6V0iqUmelHNnbLRr1EbErzkQw#&redirect_uri=&client_id=&scope=&client_secret=************&grant_type=authorization_code https://developers.google.com/identity/protocols/OAuth2WebServer

0

I think I understand what's wrong:

  1. as @newhouse said, you should POST to https://www.googleapis.com/oauth2/v4/token and not https://accounts.google.com/o/oauth2/token (@newhouse I gave you a +1 :) )

    (https://www.googleapis.com/oauth2/v4/token is for getting the authorization_code and https://accounts.google.com/o/oauth2/token is for getting the code).

  2. You can't use the same code more than once.

    Everything else seems in order so, if you keep getting 400, you are probably trying to use the code you got more than one time (then you'll get 400 every time, again and again).

* You should also lose the con.setRequestProperty("Host", "www.googleapis.com");

Community
  • 1
  • 1
Yoav Aharoni
  • 2,632
  • 11
  • 18
  • when you say the https://accounts.google.com/o/oauth2/token is for getting the code, is the "code" the access token? Also after the two request https://www.googleapis.com/oauth2/v4/token and https://accounts.google.com/o/oauth2/token , would there be a third request to send the email contents to actually send the email with the email? Which one the the Scenario diagrams in this link https://developers.google.com/identity/protocols/oauth2 is the full exchange that we need to follow to send email with this gmail api? – Computer Science May 12 '20 at 04:49
0

Refer : https://developers.google.com/android-publisher/authorization

You already have authorization code that is called "refresh token". Please keep it in safe place. You can use "refresh token" to generate "access token".

To get "access token", please make a post request to following URL

https://accounts.google.com/o/oauth2/token

Parameters:

  • grant_type
  • client_id
  • client_secret
  • refresh_token

where "grant_type" should be "refresh_token"

We are using PHP to do same, here is PHP's code for your reference

    $curl = curl_init();

    curl_setopt_array($curl, array(
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_URL => 'https://accounts.google.com/o/oauth2/token',
    CURLOPT_USERAGENT => 'Pocket Experts Services',
    CURLOPT_POST => 1,
    CURLOPT_POSTFIELDS => array(
    "grant_type" => "refresh_token",
    "client_id" => $GOOGLE_CLIENT_ID,
    "client_secret" => $GOOGLE_CLIENT_SECRET,
    "refresh_token" => $GOOGLE_REFRESH_TOKEN,
    )));


    // Send the request & save response to $resp
    $resp = curl_exec($curl);

Hope it will help you.

Umang Joshi
  • 171
  • 1
  • 2
0

the low security methode was temporary and i couldn't use it in production but I found an article that made it easier using node here with an example code and it works perfect