400 Bad Request and "Invalid authorization code" in Code Flow Authentication

  • 1
  • Problem
  • Updated 1 year ago
  • In Progress
  • (Edited)
First day in production. First authentication was successful; second failed returning 400 and the following error

{ "error" : "invalid_grant", "errors" : [ { "errorCode" : "OAU-106", "message" : "Invalid authorization code" } ], "error_description" : "Invalid authorization code" }


during the last leg of Code Flow Authentication when the code is exchanged for a pair of tokens.

I am using PHP and cURL.

Interesting that the code works randomly. 1 out of 10 attempts succeed.


I also noticed that expires_in parameter is not included when authorization code is passed to my callback as documentation suggests (https://developer.ringcentral.com/api-docs/latest/index.html#!#AuthorizationFlows.html)

Desperately need help.

Here is my code

$req='https://platform.ringcentral.com/restapi/oauth/token';
$post = array (
'grant_type' => 'authorization_code',
'code' => $code , 'redirect_uri' => 'https://xxxxxxxx.io/rc/query.php');

$ch = curl_init($req);

curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0 Y/2');

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$apiKey = base64_encode($APPKEY);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Authorization: Basic ' . $apiKey,
'Accept: application/json',
'Content-Type: application/x-www-form-urlencoded'));

curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);


$response = curl_exec($ch);
curl_close($ch);
$r=json_decode($response, true);
Photo of Alec Dfour

Alec Dfour

  • 340 Points 250 badge 2x thumb

Posted 2 years ago

  • 1
Photo of Benjamin Dean

Benjamin Dean, Alum

  • 8,622 Points 5k badge 2x thumb
Here are the two questions I see being asked in this post:

1. The API documentation says I should expect the `expires_in` property for a valid authorization response to be part of the payload, but you are not receiving `expires_in` in the authorization code server response.

Could you please provide a complete response for both a successful (HTTP 200) and failed request (HTTP 400) containing the full header and body of the request/response?


2. 90% of responses are HTTP 4000 while trying to receive successful `access_token` from requests during the second leg of the Authorization Flow (trading `code` for `token`), and need help determining the cause. The error being provided is:
{ "error" : "invalid_grant", "errors" : [ { "errorCode" : "OAU-106", "message" : "Invalid authorization code" } ], "error_description" : "Invalid authorization code" }
A few questions to help me diagnose the issue before responding:
  • What is the interval between auth-code and access_token requests please?
  • How are you managing your sessions please?
  • How are you constructing the redirect please?
As a suggestion, I would highly recommend using the Official RingCentral PHP SDK available on Github. It handles all of this for you and provides a reusable tool for building PHP-based applications and integrations with RingCentral API for Authorization Flows. Additionally, then I could easily reference you this example of how to implement 3-legged (Authorization Flow) auth in your PHP application: https://github.com/grokify/ringcentral-demos-oauth/tree/master/php.
Photo of Alec Dfour

Alec Dfour

  • 340 Points 250 badge 2x thumb
Here is the complete info when exchange code for token is successful.

Please note that the expires_in parameter is not present 


[19:55:41 ] 1. Redirected Here With Auth Code ---------------------------
[19:55:41 ]  QUERY - state=6e39c7b7-8241-49d9-9164-07c0c9367ab9&code=SUFEMDFQMDRQQVMwMXxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXZUhNV1pUVDRRd29iQ0JmZnNBY0tTTU1TT2E5Vmk1QTlweWo3ai1TdzNZTXxpQkFMWEF8UzZxc0MtLVBzRGIxTGgyT2p5T1NkZw
[19:55:41 ] Header - Content-Type: 
[19:55:41 ] Header - Content-Length: 0
[19:55:41 ] Header - Dnt: 1
[19:55:41 ] Header - User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/601.2.7 (KHTML, like Gecko) Version/9.0.1 Safari/601.2.7
[19:55:41 ] Header - Referer: https://service.ringcentral.com/login/authorize.html?session=5413271938559800423&responseType=co...
[19:55:41 ] Header - Host: xxxxxxx.io
[19:55:41 ] Header - Accept-Language: en-us
[19:55:41 ] Header - Accept-Encoding: gzip, deflate
[19:55:41 ] Header - Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
[19:55:41 ] Header - Connection: keep-alive
[19:55:41 ] code=SUFEMDFQMDRQQVMwMXxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXZUhNV1pUVDRRd29iQ0JmZnNBY0tTTU1TT2E5Vmk1QTlweWo3ai1TdzNZTXxpQkFMWEF8UzZxc0MtLVBzRGIxTGgyT2p5T1NkZw expires_in =
[19:55:41 ] -------------------------------------------------
[19:55:41 ] 2. getting auth/token using SUFEMDFQMDRQQVMwMXxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXZUhNV1pUVDRRd29iQ0JmZnNBY0tTTU1TT2E5Vmk1QTlweWo3ai1TdzNZTXxpQkFMWEF8UzZxc0MtLVBzRGIxTGgyT2p5T1NkZw
[19:55:41 ] POST to https://platform.ringcentral.com/restapi/oauth/token
[19:55:41 ] Array
(
    [grant_type] => authorization_code
    [code] => SUFEMDFQMDRQQVMwMXxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXZUhNV1pUVDRRd29iQ0JmZnNBY0tTTU1TT2E5Vmk1QTlweWo3ai1TdzNZTXxpQkFMWEF8UzZxc0MtLVBzRGIxTGgyT2p5T1NkZw
    [redirect_uri] => https://xxxxxxx.io/rc/query.php
)

[19:55:42 ] Header - HTTP/1.1 200 OK

[19:55:42 ] Header - Server: nginx/1.8.0

[19:55:42 ] Header - Date: Tue, 25 Oct 2016 18:55:43 GMT

[19:55:42 ] Header - Content-Type: application/json;charset=UTF-8

[19:55:42 ] Header - Transfer-Encoding: chunked

[19:55:42 ] Header - Connection: keep-alive

[19:55:42 ] Header - Set-Cookie: RCRoutingInfo=B:YWC8T7z3w9Cqy2gbIJPZpheXobyxqbzarP2tjAeKidj30hU2AF1U/i0FeIIlgIpc; Path=/

[19:55:42 ] Header - X-Application-Context: application:docker:8080

[19:55:42 ] Header - X-Rate-Limit-Limit: 5

[19:55:42 ] Header - X-Rate-Limit-Window: 60

[19:55:42 ] Header - X-Rate-Limit-Group: auth

[19:55:42 ] Header - X-Rate-Limit-Remaining: 4

[19:55:42 ] Header - AceRoutingKey: sjc01-c01-ace01.34a9ce59-9440-11e6-974f-0050568d7553

[19:55:42 ] Header - X-Server-HLB: sjc01-c01-hlb01.c01.ringcentral.com

[19:55:42 ] Header - X-Upstream-Service: sjc01-c01-ace01-8080

[19:55:42 ] Header - X-Request-Time: 0.430

[19:55:42 ] Header - X-Upstream-Server: 10.13.121.174:31822

[19:55:42 ] Header - X-Upstream-HTime: 0.430

[19:55:42 ] Header - X-Upstream-RTime: 1477421743.130

[19:55:42 ] Header - X-Upstream-Status: 200

[19:55:42 ] Header - X-Tcpinfo: 1000, 500, 10, 28960

[19:55:42 ] Header - x-throttling-group: Auth

[19:55:42 ] Header - RCRequestId: a1553bfc-9ae4-11e6-9b5b-005056977b15

[19:55:42 ] Header - X-AGW-Host: sjc01-c01-agw11.c01.ringcentral.com

[19:55:42 ] Header - 

[19:55:42 ] httpcode = 200
[19:55:42 ] RESPONSE from /restapi/oauth/token:{
  "access_token" : "SUFEMDFQMDRQQVMwMHxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXTERTemJsa0oxajdZZHRCM3JzLTE3Qndld0RwSDR5TTMtYTc0ck10MG9Dd09GdlBRY2lmQTNyS3RBS2l3T2l6THFrV2xzRkpQbWV4UFMwNWhEU2NyMEF8aUJBTFhBfG9fNHIzQkNhSkpRaFdLcHJoeGFpTVE",
  "token_type" : "bearer",
  "expires_in" : 3600,
  "refresh_token" : "SUFEMDFQMDRQQVMwMHxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXZ3FHaTlZY1JCZC0xREV6T3c3V19rbkhxcXY0Rm1fRFUtYTc0ck10MG9Dd09GdlBRY2lmQTNyS3RBS2l3T2l6TDJIWWtSV00tSnBDdVdIaVB3NmhUV3d8aUJBTFhBfDRRak95YW4yMnRwZjVXY3BxRlZsZkE",
  "refresh_token_expires_in" : 604800,
  "scope" : "ReadMessages ReadPresence ReadAccounts",
  "owner_id" : "1357525011",
  "endpoint_id" : "mMkJ22yOS6CJI8sjx3sFnQ"
}
[19:55:42 ] atoken=SUFEMDFQMDRQQVMwMHxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXTERTemJsa0oxajdZZHRCM3JzLTE3Qndld0RwSDR5TTMtYTc0ck10MG9Dd09GdlBRY2lmQTNyS3RBS2l3T2l6THFrV2xzRkpQbWV4UFMwNWhEU2NyMEF8aUJBTFhBfG9fNHIzQkNhSkpRaFdLcHJoeGFpTVE 


//WE GOT BOTH TOKENS


--------------------------------Here is the one that failed
----------------------------------
[20:27:14] 1. Redirected Here With Auth Code ---------------------------

[20:27:14]  QUERY - state=6e39c7b7-8241-49d9-9164-07c0c9367ab9&code=SUFEMDFQMDRQQVMwMXxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXTmE3N1BXUVhpOUQxSWJNT3dtRDhnOERuOE91ZVY5ODRzeWhDOGdJRjVSZ3xpQkFMWEF8bUdzRmdzWXFYNWFuNmlFRU1NcG93UQ
[20:27:14] Header - Content-Type: 
[20:27:14] Header - Content-Length: 0
[20:27:14] Header - Dnt: 1
[20:27:14] Header - User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/601.2.7 (KHTML, like Gecko) Version/9.0.1 Safari/601.2.7
[20:27:14] Header - Referer: https://service.ringcentral.com/login/authorize.html?session=-5162010770277200910&responseType=c...
[20:27:14] Header - Host: xxxxxxxx.io
[20:27:14] Header - Accept-Language: en-us
[20:27:14] Header - Accept-Encoding: gzip, deflate
[20:27:14] Header - Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
[20:27:14] Header - Connection: keep-alive
[20:27:14] code=SUFEMDFQMDRQQVMwMXxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXTmE3N1BXUVhpOUQxSWJNT3dtRDhnOERuOE91ZVY5ODRzeWhDOGdJRjVSZ3xpQkFMWEF8bUdzRmdzWXFYNWFuNmlFRU1NcG93UQ expires_in =
[20:27:14] -------------------------------------------------
[20:27:14] 2. getting auth/token using SUFEMDFQMDRQQVMwMXxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXTmE3N1BXUVhpOUQxSWJNT3dtRDhnOERuOE91ZVY5ODRzeWhDOGdJRjVSZ3xpQkFMWEF8bUdzRmdzWXFYNWFuNmlFRU1NcG93UQ
[20:27:14] POST to https://platform.ringcentral.com/restapi/oauth/token
[20:27:14] Array
(
    [grant_type] => authorization_code
    [code] => SUFEMDFQMDRQQVMwMXxBQUNQM0JuMUVMSGdIdHFFLWl2OTBMakF5MG1ZeEtCc0lhNzhrOU5HTy1WdWtzcXlpQm1JUHo5ZURwd0MtSUlGS0JnSEhDYm8zUFgwc2llTDFsQV9wXzNXTmE3N1BXUVhpOUQxSWJNT3dtRDhnOERuOE91ZVY5ODRzeWhDOGdJRjVSZ3xpQkFMWEF8bUdzRmdzWXFYNWFuNmlFRU1NcG93UQ
    [redirect_uri] => https://xxxxxx.io/rc/query.php
)

[20:27:15] Header - HTTP/1.1 400 Bad Request

[20:27:15] Header - Server: nginx/1.8.0

[20:27:15] Header - Date: Tue, 25 Oct 2016 19:27:16 GMT

[20:27:15] Header - Content-Type: application/json;charset=UTF-8

[20:27:15] Header - Transfer-Encoding: chunked

[20:27:15] Header - Connection: keep-alive

[20:27:15] Header - Set-Cookie: RCRoutingInfo=B:YWC8T7z3w9Cqy2gbIJPZpheXobyxqbzan5hMpsmoK9UwIkLMehywU6ZOUM4fcXvp; Path=/

[20:27:15] Header - X-Application-Context: application:docker:8080

[20:27:15] Header - Content-Language: en

[20:27:15] Header - WWW-Authenticate: Bearer realm="RingCentral REST API", error="invalid_grant", error_description="Invalid authorization code"

[20:27:15] Header - AceRoutingKey: sjc01-c01-ace01.34a9ce59-9440-11e6-974f-0050568d7553

[20:27:15] Header - X-Server-HLB: sjc01-c01-hlb01.c01.ringcentral.com

[20:27:15] Header - X-Upstream-Service: sjc01-c01-ace01-8080

[20:27:15] Header - X-Request-Time: 0.280

[20:27:15] Header - X-Upstream-Server: 10.13.121.174:31822

[20:27:15] Header - X-Upstream-HTime: 0.280

[20:27:15] Header - X-Upstream-RTime: 1477423635.901

[20:27:15] Header - X-Upstream-Status: 400

[20:27:15] Header - X-Tcpinfo: 0, 0, 10, 28960

[20:27:15] Header - x-throttling-group: Auth

[20:27:15] Header - RCRequestId: 0982fdaa-9ae9-11e6-9fba-0050569792e2

[20:27:15] Header - X-AGW-Host: sjc01-c01-agw12.c01.ringcentral.com

[20:27:15] Header - 

[20:27:15] httpcode = 400
[20:27:15] RESPONSE from /restapi/oauth/token:{
  "error" : "invalid_grant",
  "errors" : [ {
    "errorCode" : "OAU-106",
    "message" : "Invalid authorization code"
  } ],
  "error_description" : "Invalid authorization code"
}
(Edited)
Photo of Alec Dfour

Alec Dfour

  • 340 Points 250 badge 2x thumb
A few questions to help me diagnose the issue before responding:
  • What is the interval between auth-code and access_token requests please?
  • How are you managing your sessions please?
  • How are you constructing the redirect please?
As a suggestion, I would highly recommend using the Official RingCentral PHP SDK available on Github. It handles all of this for you and provides a reusable tool for building PHP-based applications and integrations with RingCentral API for Authorization Flows. Additionally, then I could easily reference you this example of how to implement 3-legged (Authorization Flow) auth in your PHP application: https://github.com/grokify/ringcentral-demos-oauth/tree/master/php.


What is the interval between auth-code and access_token requests please?

Immediately

How are you managing your sessions please?

Similar to an example you mentioned. In fact, my code and PHP callback.php in https://github.com/grokify/ringcentral-demos-oauth/tree/master/php is almost identical except php redirection is used rather than JS to initiate login.

How are you constructing the redirect please?

<?php

$guid='6e39c7b7-8241-49d9-9164-07c0c9367ab9';

$r ='https://platform.ringcentral.com/restapi/oauth/authorize?response_type=code&state=' . $guid . '&redirect_uri=https://xxxxxx.io/rc/query.php&client_id=pvDQNFEVTauJk_nyB91BQw';
header("Location: $r");
die();

?>
Photo of Benjamin Dean

Benjamin Dean, Alum

  • 8,622 Points 5k badge 2x thumb
Okay. That whittles things down a bit since I can see the output is the same for both the failed and successful flows on your redirect page. That leads me to ask about the inputs...

1. Are you using the same RingCentral account credentials for each of the above scenarios (success/failure)?

2. If you are NOT using the same RingCentral account credentials, do the credentials you are using both part of the Sandbox account you're using for development (meaning you can see both verified users in the Sandbox Online Account Portal while logged in as an admin for the Sandbox account)?

3. If you are NOT using the same RingCentral account credentials, what are the roles for each of these accounts?

Sorry about all the questions, just trying to make sure I provide accurate information.

After looking at this, I see the answer to your question about the missing `expires_in` property value while receiving the [authorization] `code`. It is a very very short window for use (I'm asking our engineering team for the min/max/default TTL), but the fact you say you're using the `code` query param provided to your redirect URL immediately is a good approach.

I spoke to our Platform Architect about this, and he said there is a known issue which is causing intermittent failures of trading `code` for a token. He recommended that if your application receives a failure as you are, to retry the request after 2-3 seconds (until such time as we have resolved this issue). He also informed me that the `expires_in` property should have a value attached, and he will confirm if he is receiving the same results and log a bug to fix this if needed. He said the default time for using the `code` is 60 seconds from response time.
(Edited)
Photo of Alec Dfour

Alec Dfour

  • 340 Points 250 badge 2x thumb
Are you using the same RingCentral account credentials for each of the above scenarios :
Yes, the same account.

The app was working just fine in the sandbox except web hooks that are fixed now.

I fact, I created new app and configured the code ( that fails in production) to work in a sandbox for that new app. It works just fine. No failures in trading the code for tokens

I spoke to our Platform Architect about this, and he said there is a known issue which is causing intermittent failures of trading `code` for a token.

I see intermittent success. Not very encouraging for a production ready app.

So there is a know issue with training code for a token. When you expect to fix it? Do you have a list of known problems? 
 
Photo of Benjamin Dean

Benjamin Dean, Alum

  • 8,622 Points 5k badge 2x thumb
Your encouragement is valid and understood by our team, but our platform is still in "beta" to allow us some time to iron out these kinks before we say the platform is GA.

While I know it is not optimal by any means, could you implement a 2-3 second delay upon a failure response and re-issue the request to exchange the authorization code for a token? Then let us know if that resolves the issue as a workaround in the interim?

Currently we do not have a public list of known issues, but I will surface your request to our team for consideration as a future feature release (and because being transparent with developers is a critical component of a solid platform).

This issue is currently set to be resolved in the next 2-3 weeks.

As for the `expires_in` query parameter being null, after our Platform Architect investigated the OAuth Authorization Flow spec, he informed me that `expires_in` is not passed as part of the spec. In the example output you provided, I see it is listed, but it is not in a standard query string format...is that your code's output/interpretation of the query parameters...or a pass-through of the query string your redirect handler is being provided please?
Photo of Alec Dfour

Alec Dfour

  • 340 Points 250 badge 2x thumb
is that your code's output/interpretation of the query parameters...or a pass-through of the query string your redirect handler is being provided please?

No query doesn't contain that parameter. it is a code output.

Anyway

I implemented a 3 seconds delay before a retry and it works on the second attempt. Then I simply added 3 sec delay after I got the code and it works on the first attempt.

It looks like I am using the authorization code too soon.

I don't know your architecture but it seems to me that there a race condition exists in your system. 

2-3 weeks. Please let me know
(Edited)
Photo of Benjamin Dean

Benjamin Dean, Alum

  • 8,622 Points 5k badge 2x thumb
So you have a workaround in place that will get your application back in order until the official fix is deployed, correct?

Our engineers have been working on this issue, as it was impacting other customers as well, and the fix is coming.

I will try to remember to post here when this fix is deployed, but check back if I don't respond to this thread within the next three weeks please. :)
Photo of Alec Dfour

Alec Dfour

  • 340 Points 250 badge 2x thumb
Thank you very much for your help. Looking forward for the fix
Photo of Benjamin Dean

Benjamin Dean, Alum

  • 8,622 Points 5k badge 2x thumb
Could you please re-test and see if this issue still exists please?
Photo of Tayyab Sajjad

Tayyab Sajjad

  • 364 Points 250 badge 2x thumb
i am facing an error related to this post, when i authorize the app and get Code after that i use code for getting token then i am having this Error...
'error' => string 'invalid_client' (length=14)
  'error_description' => string 'Invalid client: ' (length=16)
  'errors' => 
    array (size=1)
      0 => 
        array (size=3)
          'errorCode' => string 'OAU-153' (length=7) 
          'message' => string 'Invalid client: ' (length=16)