Bo.
Bo.

Reputation: 675

Google Analytics: Invalid JWT Signature (invalid_grant) when trying to make authorized API call

I trying to make authorized API call (HTTP/REST) to service account in Google Analytics. using this doc: https://developers.google.com/identity/protocols/OAuth2ServiceAccount

I just use HTTP/REST request to test.

So I have service account's private key file:

{
  "type": "service_account",
  "project_id": "test-x",
  "private_key_id": "some_private_key_id",
  "private_key": "-----BEGIN PRIVATE KEY----- some_private_key -----END PRIVATE KEY-----",
  "client_email": "[email protected]",
  "client_id": "some_client_id",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/test-01%40test-x.iam.gserviceaccount.com"
}

I create JWT based on client_email: [email protected]

Header:

{"alg":"RS256","typ":"JWT"}

Claim set:

{
  "iss": "[email protected]",
  "scope": "https://www.googleapis.com/auth/analytics.readonly",
  "aud": "https://www.googleapis.com/oauth2/v4/token",
  "exp": 1488820112,
  "iat": 1488816522
}

iat - I just set current

exp - current + 1 hour,

I use this service to create signature: https://jwt.io/#debugger

It generates encoded value which I try to use for access token request

When I try to use generated result from "Encoded" field:

curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=JWT_that_has_been_signed' https://www.googleapis.com/oauth2/v4/token

result:

{
 "error": "invalid_grant",
 "error_description": "Invalid JWT Signature."
}

But I not use my private key. Accordingly to documentation for computing the signature I have to use my private key. I not completely understand how to use key in right way to compute the signature correctly.

jwt.io already generates PUBLIC and PRIVATE key...

Probably I'm using jwt.io incorrectly..

Please advise me the correct way of creating JWT or may be another service to create it.

Thanks!

Upvotes: 10

Views: 34171

Answers (5)

Michał Kupczak
Michał Kupczak

Reputation: 1

In my case I had a wrong time zone on my system.

I reinstalled my system and did not spot that the time was not set correctly.

Upvotes: 0

Viktor Reinok
Viktor Reinok

Reputation: 130

In my case I used wrong service account ID.

You are able to find correct on under: Google console -> Service Account -> Detail -> Unique ID

I must be a long number like: 113334445551115554321

Upvotes: 1

abetwothree
abetwothree

Reputation: 577

For anyone exploring this issue, I have a set of code in PHP to get the token with the JSON service account file. This PHP snippet will get you a token to use to get GA data.

    // assume $config is your json service account file converted to array
    $config = "YOUR JSON FILE CONVERTED TO ARRAY";
    $jwt_header = '{"alg":"RS256","typ":"JWT"}';
    $jwt_header_cryph = urlencode(base64_encode($jwt_header));
    
    $jwt_claim = '{
                "iss": "'.$config['client_email'].'",
                "scope":"https://www.googleapis.com/auth/analytics.readonly",
                "aud":"https://www.googleapis.com/oauth2/v4/token",
                "exp":'.(time()+3600).',
                "iat":'.time().'
            }';
    
    $jwt_claim_cryph = urlencode(base64_encode($jwt_claim));
    
    $data = $jwt_header_cryph.".".$jwt_claim_cryph;
    
    $binary_signature = "";
    
    $algo = "SHA256";
    openssl_sign($data, $binary_signature, $config['private_key'], $algo);
    $jws_signature_cryph = urlencode(base64_encode($binary_signature));
    
    //concatenating the jwt request to complete it
    $complete_request = $jwt_header_cryph.".".$jwt_claim_cryph.".".$jws_signature_cryph;
    
    //build CURL request with post method
    $url = 'https://www.googleapis.com/oauth2/v4/token';
    
    //set POST variables
    $fields = array(
        'grant_type' => urlencode('urn:ietf:params:oauth:grant-type:jwt-bearer'),
        'assertion' => $complete_request
    );
    
    $fields_string = "";
    //url-ify the data for the POST
    foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
    rtrim($fields_string, '&');
    
    //open connection
    $ch = curl_init();
    
    //set the url, number of POST vars, POST data
    curl_setopt($ch,CURLOPT_URL, $url);
    curl_setopt($ch,CURLOPT_POST, count($fields));
    curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
    curl_setopt($ch,CURLOPT_RETURNTRANSFER, true );
    
    //execute post
    $result = curl_exec($ch);
    
    //close connection
    curl_close($ch);
    
    $token = json_decode($result, true);

The variable $token will have the following content

    [
      "access_token" => "your-access-token-string"
      "expires_in" => 3599
      "token_type" => "Bearer"
    ]

Use the access_token string to ping GA for analytics data.

Hopefully this helps someone along as this is not well explained much anywhere and it took some digging to figure out the encryption properly.

Upvotes: 3

Raj Oberoi
Raj Oberoi

Reputation: 774

I got this error when the secrets key was no longer valid. I generated a new service account key and it started working.

Upvotes: 7

Nadeem Qasmi
Nadeem Qasmi

Reputation: 2437

I had a same issue , I just create a new private key from google cloud platform

  IAM & admin -> Service accounts 

Service accounts for project "Project_Name"

already created your Service account,click on the actions-> Create Key

and update the private key on your project. Hope it will help you.

Upvotes: 1

Related Questions