Page 1 of 1

Client sites rejects request for new bearer token after existing bearer token has Expired.

Posted: Wed Jul 17, 2024 6:05 pm
by DaleSigwart
Hi

Hope I can find an answer to a vexing question:

we are Connecting using HTTPAPI to a provider that requires OAuth2 bearer tokens. These are set to expire after 1799 seconds (approx. 30 minutes).

In my program I add bearer token life to current time to get and expiration data time. On each subsequent transaction within the half hour the bear token works fine to allow the JSON Posts to occur. Once the first transaction exceeds the bearer expiration time, process goes out and attempts to get a new bear token but fails with an HTTP 500 error.

SetError() #13: HTTP/1.1 500 Internal Server Error
recvresp(): end with 500
recvdoc parms: identity 401
header_load_cookies() entered
recvdoc(): entered
SetError() #0:
recvdoc(): Receiving 401 bytes.
{"type":"https://developers.onesourcetax.com/err ... horization for basic authentication decode policy is not valid","detail":"request.header.Authorization for basic authentication decode policy is not valid","status":500,"instance":"/oauth2/v1/token","api_requestId":"rrt-00332702c3bc1390a-b-ea-31619-37254072-1"}
SetError() #13: HTTP/1.1 500 Internal Server Error
http_close(): entered


Unfortunately, these are interactive billing job so the application can easily stay active for over 30 minutes,


Code routines related to getting bearer code validate bearer code has not expired

Code: Select all

 
  *************************************************************************
  * Get API token
  *************************************************************************
        If PrApiSuccess = 0;

           Exsr NewToken;

           IF newTknNeeded;
             Exsr GenToken;

             //Load token
             Exsr YAJL_Start;
             Exsr GetTknVar;
           Endif;

        Elseif PrApiSuccess <> 0;
           *inlr = *on;
           return;
        endif;


        If SndJson = 'Y' and TokenDs.token <> *blanks;

          If prAction = 'VLD';
            ExSr SendJSON;
          Elseif prAction = 'CMT';
            ExSr SendCOMT;
          Else;
            msg = 'Transaction is not validation of Commitment. +
                   Transaction rejected';
             // dsply msg;
            prApiSuccess = 2;
            prErrorCode = 'Non-Comm-ERROR';
            PrErrorMsg = 'Non-Communication Error' + %trim(Msg) ;

            wkRspTmsp = %Timestamp;
            wkRspTmsp = %Timestamp;
            // Generate Error log entries
            Exsr ErrorLog;
          Endif;

        Else;
           msg = 'Bearer token for transaction not found Transaction rejected';
           //dsply msg;
           prApiSuccess = 1;
           prErrorCode = 'Comm-ERROR';
           PrErrorMsg = 'Communication Error' + %trim(Msg) ;

           wkRspTmsp = %Timestamp;
           wkRspTmsp = %Timestamp;
           // Generate Error log entries
           Exsr ErrorLog;
        ENDIF;

        return;

        *inlr = *on;
                               
      ********************************************************************
      *  Get token Vaiables
      *      get Token varaible from Respons string using Yajl
      ********************************************************************
          BegSR NewToken;

             NewTknNeeded = *off;

dfcr6        // Add timestamp to get current time instead of just relying on %time()
dfct6        CurTmsp  = %Timestamp;
dfct6        currTime = %subst(%char(%time(CurTmsp):*ISO) : 1 :2) + ':' +
dfct6                   %subst(%char(%time(CurTmsp):*ISO) : 4 :2) + ':' +
dfct6                   %subst(%char(%time(CurTmsp):*ISO) : 7 :2) ;

             if currTime > BearerTime;
                NewTknNeeded = *on;
             ENDIF;

          ENDSR;     



  *************************************************************************
      * GenToken -- Build HTTP Header and Post to Generate Bearer Token
      *************************************************************************
        BegSr GenToken;

            //URL = %trim(txuri);  //+ crlf ;

            URL = 'https://api-uat.onesourcetax.com/oauth2/v1/token';

            // Get encrypted client secret from config file
dfc8        Exec Sql
dfc8         SELECT DECRYPT_char(Tx_client_secret, 'JS#AP1$fLd#022024')
dfc8           Into :wkTxClentSec
dfc8           FROM txoapicfg
dfc8         Where  txapitask  = :webAction
dfc8           and  txparcmp   = :prCompany;

dfc8         if SQLCODE <> 0 ;
dfc8            prErrorCode = 'Comm-ERROR';
dfc8            PrErrorMsg = 'Could not extract encrypted Client secret ' +
dfc8                         ' For Company ' + %trim(PrCompany) + '.' +
dfc8                         ' Transaction Failed. Call IT Support.' ;
dfc8            // Generate Error log entries
dfc8            Exsr ErrorLog;
dfc8            rc= https_cleanup();
efc8            Return;
dfc8         Endif;

            request = '{ "client_id" : ' + %trim(txclientid) + ',' +
                      ' "scopes" : ' + %trim(txScopes) + ' , ' +
                      ' "grant_type" : ' + %trim(txgrnttype) + ',' +
                      ' "client_secret" : ' + %trim(wktxclentsec) + ' }' ;

           rc = http_setAuth(HTTP_AUTH_NONE
                             : ' '
                             : ' ' );

           // capture time before sending API
           wkReqTmsp = %Timestamp;
           wkPostType =  %TRIM(TXHTTPTYPE);

           tknResponseJSON = '/OneSource/tokens/respToken '
                           + %trim(prCORID) + 'json' ;

           rc = http_req('POST'
                         : URL
                         : tknResponseJSON      //--'/OneSource/tokens/respToken.json'
                         : *omit
                         : *omit
                         : request
                         : 'application/json');

           // HTTP failures  and communication failures
           if rc <> 1;
              msg = http_error();
               // dsply msg;
              wkRspTmsp = %Timestamp;

              prApiSuccess = 1;
dfc5          ExSr GetHttpErr;
              prErrorCode = 'Comm-ERROR';
dfc5          PrErrorMsg = 'Communication Error' + %trim(Msg) +
dfc5                       ' title '  + %trim(TknErrDs.Error2) +
dfc5                       ' detail ' + %trim(TknErrDs.Error3) +
dfc5                       ' ApiID '  + %trim(TknErrDs.apiId);

              // Generate Error log entries
              Exsr ErrorLog;
              rc= https_cleanup();
              Return;
           EndIf;



            rc= https_cleanup();

          EndSR;


HTTP Log response from provider

Code: Select all

HTTPAPI Ver 1.45 released 2021-09-20
NTLM Ver 1.4.0 released 2014-12-22
OS/400 Ver V7R4M0

https_init(): entered
QSSLPCL = *TLSV1 *TLSV1.1 *TLSV1.2
SSL version 2 support disabled
SSL version 3 support disabled
Old interface to TLS version 1.0 support disabled
TLS version 1.0 support disabled
TLS version 1.1 support disabled
TLS version 1.2 support enabled
TLS version 1.3 support enabled
initializing GSK environment
GSK Environment now available
-------------------------------------------------------------------------------------
Dump of local-side certificate information:
-------------------------------------------------------------------------------------
http_setauth(): entered
http_persist_open(): entered
http_long_ParseURL(): entered
DNS resolver retrans: 2
DNS resolver retry  : 2
DNS resolver options: x'00000136'
DNS default domain: BEIS.COM
DNS server found: 172.16.5.11
DNS server found: 172.16.5.12
Nagle's algorithm (TCP_NODELAY) disabled.
SNI hostname set to: api-uat.onesourcetax.com
-------------------------------------------------------------------------------------
Dump of server-side certificate information:
-------------------------------------------------------------------------------------
Cert Validation Code = 6000
-----BEGIN CERTIFICATE-----
MIIHBTCCBe2gAwIBAgIRAOnnQJq1fnNvu7Y27144EBAwDQYJKoZIhvcNAQELBQAw
g
-----END CERTIFICATE-----
Serial Number: 00:E9:E7:40:9A:B5:7E:73:6F:BB:B6:36:EF:5E:38:10:10
Common Name: api-uat.onesourcetax.com
Country: CA
State/Province: Ontario
Org Unit: Thomson Reuters Corporation
Issuer CN: COMODO RSA Organization Validation Secure Server CA
Issuer Country: GB
Issuer State/Province: Greater Manchester
Issuer Locality: Salford
Issuer Org: COMODO CA Limited
Version: 3
not before: 20240420190000
Unknown Field: 19:00:00 20-04-2024
not after: 20250421185959
Unknown Field: 18:59:59 21-04-2025
pub key alg: 1.2.840.113549.1.1.1
signature algorithm: 1.2.840.113549.1.1.11
Unknown Field: 0382010F003082010A0282010100C519DD9E9AAB77021AB198A846DC35CB035492F560DE1D3D70E20BC2B14E8F137462B9589FDF23A72916EA23F573899E0FBD64A3C692F4B54AFDD6398C9859F2FAB19218830E5DCE1857A23D245D0B557D0217127CF9F6F1DC1E19780C66706978BDE4410AB85BC953766F73BDFD05F6C982027AF8121202A934BB52D7D08F618902CB7A0F23697114B693D2105EBBDDFFD1FFEBC52CD4D9D411E07415E391D3C1871A64E8F715179DF3671B9ED79A92A52727A13FDE739F5F1F9AAF84F15F43E0CC655EDF0758959630AE4510CDA0AC3E19ED9389F8BCD548C172ACB574892987BADD17F607D38840BCD73759EE4241FE8B1835B0CC6FEAD599BBFFE558005F0203010001
Unknown Field: 2048
Unknown Field: 9DB81CFE516DD69BE1A3CC21FAE916DE
Unknown Field: 1.2.840.113549.2.5
Unknown Field: 21031039EF39C48F8BA61E34DE6468976BFB4C69
Unknown Field: 309E5F8728530B6D779563BCB1B4F6DD40D88698ECB2D0F6535CCFA209D4AE0D
Unknown Field: 5
Unknown Field: api-uat.onesourcetax.com
Unknown Field: 0
Unknown Field: 1.3.6.1.5.5.7.3.2
Unknown Field: 1.3.6.1.5.5.7.3.1
Unknown Field: 2.23.140.1.2.2
Unknown Field: 1.3.6.1.4.1.6449.1.2.1.3.4
Unknown Field: http://ocsp.comodoca.com

Protocol Used: TLS Version 1.2
http_persist_req(POST) entered.
http_long_ParseURL(): entered
http_long_ParseURL(): entered
do_oper(POST): entered
There are 1 cookies in the cache
POST /oauth2/v1/token HTTP/1.1
Host: api-uat.onesourcetax.com
User-Agent: http-api/1.45
Content-Type: application/json
Content-Length: 194
Cookie: $Version=0; JSESSIONID=2FB27DAB29F303F96B8CEF1599AB86F7; $Path=/;
Authorization : Bearer nk5TDbxTojkiGupSTrUlhClJO8k7
Correlation-Id: 665b7003-7307-1ac0-a18c-0004ac1d3df8


senddoc(): entered
{ "client_id" : "8*****", "scopes" : "urn:tr:onesource:auth:api:IndirectTaxDetermination" ,  "grant_type" : "client_credentials", "client_secret" : "z***R" }
recvresp(): entered
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json
Content-Length: 401
Connection: keep-alive
Date: Wed, 17 Jul 2024 17:21:49 GMT
Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS, HEAD
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: 
Access-Control-Max-Age: 3628800
X-Cache: Error from cloudfront
Via: 1.1 9f67823435998c04ac9be5fb7449a3c4.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: ATL59-P8
X-Amz-Cf-Id: vFubShwq42XYNArr6a03KN-VzxYAKBs5YtWR6F61vcvQsIyJTLZtuw==


SetError() #13: HTTP/1.1 500 Internal Server Error
recvresp(): end with 500
recvdoc parms: identity 401
header_load_cookies() entered
recvdoc(): entered
SetError() #0:
recvdoc(): Receiving 401 bytes.
{"type":"https://developers.onesourcetax.com/errors/#steps.basicauthentication.invalidbasicauthenticationsource","title":"request.header.Authorization for basic authentication decode policy is not valid","detail":"request.header.Authorization for basic authentication decode policy is not valid","status":500,"instance":"/oauth2/v1/token","api_requestId":"rrt-00332702c3bc1390a-b-ea-31619-37254072-1"}
SetError() #13: HTTP/1.1 500 Internal Server Error
http_close(): entered

Any Ideas and help are appreciated

Thanks,
Dale

Re: Client sites rejects request for new bearer token after existing bearer token has Expired.

Posted: Wed Jul 17, 2024 7:04 pm
by Scott Klement
Okay, let's take a minute and review what is happening here since you sent a LOT of code, and it can be hard to find the needle in the haystack.

You are making the following HTTP request (from the log file).

Code: Select all

POST /oauth2/v1/token HTTP/1.1
Host: api-uat.onesourcetax.com       
User-Agent: http-api/1.45
Content-Type: application/json
Content-Length: 194
Cookie: $Version=0; JSESSIONID=2FB27DAB29F303F96B8CEF1599AB86F7; $Path=/;
Authorization : Bearer nk5TDbxTojkiGupSTrUlhClJO8k7
Correlation-Id: 665b7003-7307-1ac0-a18c-0004ac1d3df8

{ "client_id" : "8*****", "scopes" : "urn:tr:onesource:auth:api:IndirectTaxDetermination" ,  "grant_type" : "client_credentials", "client_secret" : "z***R" }
You are receiving the following response from the HTTP server (Also from the log file)

Code: Select all

HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json
Content-Length: 401
Connection: keep-alive
Date: Wed, 17 Jul 2024 17:21:49 GMT
Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS, HEAD
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: 
Access-Control-Max-Age: 3628800
X-Cache: Error from cloudfront
Via: 1.1 9f67823435998c04ac9be5fb7449a3c4.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: ATL59-P8
X-Amz-Cf-Id: vFubShwq42XYNArr6a03KN-VzxYAKBs5YtWR6F61vcvQsIyJTLZtuw==

{"type":"https://developers.onesourcetax.com/errors/#steps.basicauthentication.invalidbasicauthenticationsource","title":"request.header.Authorization for basic authentication decode policy is not valid","detail":"request.header.Authorization for basic authentication decode policy is not valid","status":500,"instance":"/oauth2/v1/token","api_requestId":"rrt-00332702c3bc1390a-b-ea-31619-37254072-1"}
So it is complaining about the Authorization HTTP header. I don't know what the phrase "authentication decode policy not valid" means, but I think it's safe to say that it somehow means "I didn't understand your authorization header".

So lets examine the authorization header:

Code: Select all

Authorization : Bearer nk5TDbxTojkiGupSTrUlhClJO8k7
Firstly, this is not a valid HTTP header. There should never be a space between the header name (Authorization) and the colon that separates the header from it's value. None of the other headers have that... where this is space come from? Looking back at the RPG code, I see nothing at all that sets an authorization header.

Despite getting an error with the authorization header, you didn't send us the code that sets that header???!!!

Also.. I'm not familiar with this specific API, but in my experience it is unusual to send a bearer token to request a new token. You either use basic authentication for a token, or you send the login details in the request body, or you use a refresh token. It could be that this particular API needs the the refresh token sent as a bearer token... so I guess that's possible, here... But are we sure that this is the refresh token and not the auth token?

Most of the code in your program seems related to formatting timestamps and calling the DB2 decryption routines... if you're getting an error with auth tokens, why aren't you sending code related to the auth token?

Re: Client sites rejects request for new bearer token after existing bearer token has Expired.

Posted: Wed Jul 17, 2024 8:38 pm
by DaleSigwart
Hi Scott,

Thanks for the quick reply!

Here is my header routine:
//-------------------------------------------------------------------------------
// add_Bearer_Headers -- Subprocedure
//-------------------------------------------------------------------------------
dcl-proc add_Bearer_Headers;
dcl-pi *n;
headers varchar(32767);
end-pi;
headers =
'Authorization : Bearer ' + %Trim(TokenDs.token) + crlf +
'Correlation-Id: ' + %trim(CORID) + crlf;


end-proc;
*

Sorry I missed it before. I will try your suggestion of eliminating the extra space after Authorization in the header subprocedure and let you know.

Thanks,
Dale

Re: Client sites rejects request for new bearer token after existing bearer token has Expired.

Posted: Wed Jul 17, 2024 8:56 pm
by Scott Klement
Okay, so that is where the extra space is coming from. Please remove the extra space after "Authorization".

Code: Select all

headers =
'Authorization: Bearer ' + %Trim(TokenDs.token) + crlf +
'Correlation-Id: ' + %trim(CORID) + crlf;
Technically that space is not valid. However, i don't think it's directly causing the problem. Nevertheless, it should be removed.

Now... did you mean to send this header with the bearer token when you requested the new token? Or... how is this supposed to work?

Re: Client sites rejects request for new bearer token after existing bearer token has Expired.

Posted: Wed Jul 17, 2024 9:33 pm
by DaleSigwart
I changed the header

//-------------------------------------------------------------------------------
// add_Bearer_Headers -- Subprocedure
//-------------------------------------------------------------------------------
dcl-proc add_Bearer_Headers;
dcl-pi *n;
headers varchar(32767);
end-pi;
headers =
'Authorization: Bearer ' + %Trim(TokenDs.token) + crlf +
'Correlation-Id: ' + %trim(CORID) + crlf;


end-proc;

I got the same HTTP 500 error after 30 minutes,

My understanding is that i need to get a new bearer token after the 30 minutes expiration period as a json request
"You either use basic authentication for a token, or you send the login details in the request body, or you use a refresh token.."

It send the HTTP 500 when trying to get the new bearer token. I'm not sure what a refresh token is

Anythoughts?

Thanks Again for your quick response

Re: Client sites rejects request for new bearer token after existing bearer token has Expired.

Posted: Wed Jul 17, 2024 9:48 pm
by Scott Klement
DaleSigwart wrote: Wed Jul 17, 2024 9:33 pm My understanding is that i need to get a new bearer token after the 30 minutes expiration period as a json request
"You either use basic authentication for a token, or you send the login details in the request body, or you use a refresh token.."
The sentence you quoted here was me asking you how this is meant to work. I'm telling you options that I've seen in other APIs... but please understand that I'm not familiar with this specific API. I can tell you how to use HTTPAPI, but I can't tell you how to use "api-uat.onesourcetax.com "
DaleSigwart wrote: Wed Jul 17, 2024 9:33 pm It send the HTTP 500 when trying to get the new bearer token. I'm not sure what a refresh token is
When you log into certain sites they provide two tokens. One that provides authentication for their REST APIs, usually these expire in a short time (say 5-30 minutes) and another that lasts for a longer time (often 24 hours) called a "refresh" token. The refresh token cannot be used to call their REST APIs, but can only be used to generate a new authentication token.

I'm not saying this is the case with the API you're calling. Again, I don't know anything about the API you're calling. I'm just saying that I've seen this in the past where you can get a new bearer token using the refresh token. Yours may not work that way.

Find out what you need to do, and I'll help you understand how to do it with HTTPAPI.

Re: Client sites rejects request for new bearer token after existing bearer token has Expired.

Posted: Wed Jul 17, 2024 10:14 pm
by DaleSigwart
Hi Scott,

Thanks for the Clarification on the purpose of the Access Token. I verified that the One Source does provide the access token.
I ignored it before because I didn't understand its purpose.

SetError() #13: HTTP/1.1 200 OK
recvresp(): end with 200
recvdoc parms: identity 253
header_load_cookies() entered
recvdoc(): entered
SetError() #0:
recvdoc(): Receiving 253 bytes.
{
"success": "approved",
"token": "N6cCpTpgA4CuWjvh1UWzMe3OoQFB",
"access_token": "N6cCpTpgA4CuWjvh1UWzMe3OoQFB",
"token_type": "Bearer",
"expires_in": "1799",
"issued_at": "1721254038543",
"client_id": "8EyUnaA3pEElY16XmIxY4MX3j25omIWB"
}
http_close(): entered
HTTPAPI Ver 1.45 released 2021-09-20
NTLM Ver 1.4.0 released 2014-12-22
OS/400 Ver V7R4M0

Assuming I can use the Access_token to generate a new bearer token, what would be the process in HTTPAPI.

Thanks again,

Dale

Re: Client sites rejects request for new bearer token after existing bearer token has Expired.

Posted: Wed Jul 17, 2024 10:46 pm
by Scott Klement
Is "access token" the same as refresh token?

Again, I'm not familiar with the API. Please give me info on what you want to do, and I can tell you how to do it with HTTPAPI. There are 1000 ways to design this... I need to know how they designed it to work for this API.