1. Pepe Cuenca
  2. PowerBuilder
  3. Tuesday, 11 January 2022 15:43 PM UTC

Hi all, 

I'm trying to consume a Rest API to get an authentication token.

I have no problemems getting the token on Postman with this configuration:

 


But I cant't do it in PB 2021 with OauthClient, it throws me a -1 error code  (tried also with httpclient, but same result):

I'm using the code from the documentation, changing what I needed:

 

OAuthClient loac_Client
TokenRequest ltr_Request
TokenResponse ltr_Response
OAuthRequest loar_Request
ResourceResponse lrr_Response
String ls_AccessToken
String ls_Body, ls_type, ls_description, ls_uri, ls_state
Long ll_return

loac_Client = Create OAuthClient

//Step 1: Get the RESTful server access token.
ltr_Request.ClearParams()
ltr_Request.tokenlocation = "https://amtrip.amfresh.com/i/connect/token"
ltr_Request.granttype = "client_credentials"
ltr_Request.Method = "POST"
ltr_Request.clientid = "ZZZZZZ"
ltr_Request.clientsecret = "XXXXXX"


ll_Return = loac_Client.AccessToken( ltr_Request, ltr_Response )
If ll_Return = 1 and ltr_Response.GetStatusCode () = 200 Then
ll_Return = ltr_Response.GetBody(ls_Body)
If ll_Return = 1 Then
ls_AccessToken = ltr_Response.GetAccessToken()

End If
Else
ll_Return = ltr_Response.GetTokenError(ls_type, ls_description, ls_uri, ls_state)
MessageBox( "AccessToken Falied", "Return :" + String ( ll_return ) + "~r~n" + ls_description )
End If

If IsValid ( loac_Client ) Then DesTroy ( loac_Client )

return ls_accessToken

 

Could anyone give me a hint on what I'm missing??

Daryl Foster Accepted Answer Pending Moderation
  1. Wednesday, 12 January 2022 04:26 AM UTC
  2. PowerBuilder
  3. # 1

Hi Pepe,

Because ltr_Request is a local variable and hasn't been used I don't think you need the ClearParams call (ltr_Request.ClearParams()). It probably doesn't do any harm, but it looks unnecessary here.

I seem to recall that some people have had issues because they haven't sent a User-Agent header. You could try to add one.

ltr_Request.SetHeader("User-Agent", "PowerBuilder OAuth Program") 

or if you want to mimic Postman

request.SetHeader("User-Agent", "PostmanRuntime/7.28.4") 

If that doesn't work, have you got an example of your httpclient code that returns the same result?

Comment
  1. Pepe Cuenca
  2. Wednesday, 12 January 2022 07:55 AM UTC
Hi Daryl and thanks for the response, Adding user agent header hast't worked.

Here is my approach using httpclient,also from Appeon Documentation:



HttpClient lhc_Client

CoderObject lco_Code

Jsonpackage ljpg_json

String ls_ClientID, ls_Sercet, ls_Auth, ls_Url, ls_PostData, ls_UserName, ls_Password, ls_scope, ls_Body, ls_Error

String ls_Token, ls_TokenType, ls_AccessToken

Blob lblb_data

Long ll_return



lhc_Client = Create HttpClient

lco_Code = Create CoderObject

ljpg_json = Create Jsonpackage



//Step 1: Get the RESTful server access token.

//Url

ls_Url = "https://amtrip.amfresh.com/i/connect/token";

//Authorization

ls_ClientID = "agrox"

ls_Sercet = "*agrox+"

lblb_data = Blob ( ls_ClientID + ":" + ls_Sercet, EncodingUTF8! )

ls_Auth = lco_Code.Base64Encode( lblb_data )

lhc_Client.SetRequestHeader( "Authorization", "Basic " + ls_Auth )

lhc_Client.SetRequestHeader( "Content-Type", "application/x-www-form-urlencoded" )

ls_PostData = "grant_type=client_credentials" + ls_auth



ll_return = lhc_Client.SendRequest( "POST", ls_Url, ls_PostData )

If ll_return = 1 And lhc_Client.GetResponsestatusCode() = 200 Then

lhc_Client.GetResponseBody ( ls_body )

ls_Error = ljpg_json.loadString ( ls_body )

If ls_Error = "" then

ls_TokenType = ljpg_json.GetValue("token_type")

ls_Token = ljpg_json.GetValue("access_token")

ls_AccessToken = ls_TokenType + " " + ls_Token



Else

MessageBox( "Error", ls_Error )

End If

Else

MessageBox( "AccessToken Falied", "Return :" + String ( ll_return ) + "~r~n" + lhc_Client.GetResponsestatusText() )

End If



If IsValid ( lco_Code ) Then DesTroy ( lco_Code )

If IsValid ( ljpg_json ) Then DesTroy ( ljpg_json )

If IsValid ( lhc_Client ) Then DesTroy ( lhc_Client )



return ls_AccessToken



  1. Helpful
  1. Daryl Foster
  2. Wednesday, 12 January 2022 09:41 AM UTC
Hi Pepe, it looks like your http call is a bit wrong. You're using basic auth, but your Postman example doesn't seem to be doing that. Then the form data is also wrong. I'll post an example above that should replicate the call from Postman, just make sure you are using the exact same URL, Client ID and Client Secret as you are from Postman and try it. I haven't got access to Powerbuilder at the moment so I can't tell if my code compiles, but it should be pretty right. It is just the http call to get the token. If you can get that bit working you can add the json parsing for the token afterwards
  1. Helpful
There are no comments made yet.
Kai Zhao @Appeon Accepted Answer Pending Moderation
  1. Wednesday, 12 January 2022 05:00 AM UTC
  2. PowerBuilder
  3. # 2

Hi Pepe,

What's the detailed error you get in PB? With your PB code, the ls_type returns 'invalid_client' on our end, and I get the same error on Postman. It seems to be an incorrect clientid/clientsecret issue.

Did you set anything in the Authorization tab on Postman? If not, it’s possibly not a standard OAUTH token, please try with httpclient/restclient object, for example:

String ls_P028_JWTToken
Integer li_P028_GetJWTTokenReturn
RestClient lrc_P028
lrc_P028 = Create RestClient

lrc_P028.SetRequestHeaders( "Content-Type:application/x-www-form-urlencoded;charset=UTF-8" ) //Sets the request header
//Gets the JWT token. The second parameter provides the value according to the token server request.
li_P028_GetJWTTokenReturn=lrc_P028.GetJWTToken("https://amtrip.amfresh.com/i/connect/token", 'grant_type=client_credentials&Username=ZZZZZZ&Password=XXXXXX', ls_P028_JWTToken)

If li_P028_GetJWTTokenReturn = 1 Then
//Sets the JWT token
lrc_P028.SetJwtToken( ls_P028_JWTToken)
Else
//Prints the GetJWTToken error message if any
End If

messagebox('', ls_P028_JWTToken)


If there is still the issue, could you open a ticket to our Support system at https://www.appeon.com/standardsupport/newbug and provide a complete case there for us to analyze?


Regards,
Kai

Comment
There are no comments made yet.
Daryl Foster Accepted Answer Pending Moderation
  1. Wednesday, 12 January 2022 09:44 AM UTC
  2. PowerBuilder
  3. # 3

Hi Pepe,

Here is a basic example that should replicate the Postman call you've shown above using HttpClient.  You'll need to step through with the debugger or add a message box to see what the call is returning.

HttpClient lhc_Client
String ls_ClientID, ls_Secret, ls_Url, ls_PostData
String ls_ResponseStatusCode, ls_ResponseStatusText, ls_ResponseBody
Long ll_return

lhc_Client = Create HttpClient

ls_Url = "https://amtrip.amfresh.com/i/connect/token";
ls_ClientID = "agrox"
ls_Secret = "*agrox+"

// Set the request headers
lhc_Client.SetRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
lhc_Client.SetRequestHeader('User-Agent', 'PostmanRuntime/7.28.4')
lhc_Client.SetRequestHeader('Accept', '*/*')
lhc_Client.SetRequestHeader('Accept-Encoding', 'gzip, deflate, br')
lhc_Client.SetRequestHeader('Connection', 'keep-alive')

// Create the body
ls_PostData = 'grant_type=client_credentials'
ls_PostData += '&client_id=' + ls_ClientID
ls_PostData += '&client_secret=' + ls_Secret

ll_return = lhc_Client.SendRequest("POST", ls_Url, ls_PostData)

If ll_return = 1 Then
    ls_ResponseStatusCode = lhc_Client.GetResponsestatusCode()
    ls_ResponseStatusText = lhc_Client.GetResponsestatusText()
    lhc_Client.GetResponseBody(ls_ResponseBody)
    // Check here in the debugger for the values or show a messagebox
else
    MessageBox("SendRequest Failed", "Return :" + String ( ll_return ))
end if

destroy lhc_Client

 

 

Comment
  1. Pepe Cuenca
  2. Wednesday, 12 January 2022 13:54 PM UTC
Hi Daryl, an update on this.

Tried it on python and this works:



import requests

import json



url = "https://amtrip.amfresh.com/i/connect/token";



payload='client_id=agrox&client_secret=*agrox%2B&grant_type=client_credentials'

headers = {

'Content-Type': 'application/x-www-form-urlencoded'

}



response = requests.request("POST", url, headers=headers, data=payload)



resp_dict = json.loads(response.text)

ls_token = resp_dict.get('access_token')





file = open("token.txt", "w")

file.write(ls_token)

file.close()



print(ls_token)
  1. Helpful
  1. Daryl Foster
  2. Wednesday, 12 January 2022 22:51 PM UTC
Thanks Pepe, that's an easy fix. I'll post some example code in a reply above to help you on your way. The issue is url encoding of the arguments you send in your form request. Since we are posting the form as application/x-www-form-urlencoded we need to make sure the arguments are url encoded if they have special characters. e.g. + needs to be sent as %2B
  1. Helpful
  1. Kai Zhao @Appeon
  2. Thursday, 13 January 2022 00:41 AM UTC
Pepe, below script work well, please give a try.



String ls_P028_JWTToken

Integer li_P028_GetJWTTokenReturn

RestClient lrc_P028

lrc_P028 = Create RestClient



//client_id=agrox&client_secret=*agrox%2B&grant_type=client_credentials

lrc_P028.SetRequestHeaders( "Content-Type:application/x-www-form-urlencoded;charset=UTF-8" ) //Sets the request header

//Gets the JWT token. The second parameter provides the value according to the token server request.

li_P028_GetJWTTokenReturn=lrc_P028.GetJWTToken("https://amtrip.amfresh.com/i/connect/token";, 'client_id=agrox&client_secret=*agrox%2B&grant_type=client_credentials', ls_P028_JWTToken)



If li_P028_GetJWTTokenReturn = 1 Then

//Sets the JWT token

lrc_P028.SetJwtToken( ls_P028_JWTToken)

Else

//Prints the GetJWTToken error message if any

End If

messagebox('', ls_P028_JWTToken)
  1. Helpful
There are no comments made yet.
Daryl Foster Accepted Answer Pending Moderation
  1. Wednesday, 12 January 2022 23:04 PM UTC
  2. PowerBuilder
  3. # 4

Hi Pepe,

Here is an example using HttpClient to retrieve the json. You still need to parse the json to get the token, I've just shown the request to get the actual json from the token server.  I'll also add an example using the OAuthClient from your original example.  Both had the same issue, the arguments needed to be url encoded. I thought the OAuthClient may have done that automatically but it doesn't seem to. That may be a bug, or an enhancement request that could be made.  In your example only client_secret needed to be url encoded because it contained a + character, but if the client_id had a special character it would also need url encoding.

HttpClient lhc_Client
String ls_ClientID, ls_Secret, ls_Url, ls_PostData
String ls_ResponseStatusText, ls_ResponseBody
Long ll_ResponseStatusCode, ll_return
string ls_headers

lhc_Client = Create HttpClient

ls_Url = "https://amtrip.amfresh.com/i/connect/token";
ls_ClientID = "agrox"
ls_Secret = "*agrox+"

// Need to urlencode the arguments to the form post to handle any special characters (e.g. + converts to %2B)
Blob lblb_ClientID, lblb_Secret
CoderObject lnv_CoderObject
lnv_CoderObject = Create CoderObject

lblb_ClientID = Blob(ls_ClientID, EncodingANSI!)
ls_ClientID = lnv_CoderObject.UrlEncode(lblb_ClientID)
lblb_Secret = Blob(ls_Secret, EncodingANSI!)
ls_Secret = lnv_CoderObject.UrlEncode(lblb_Secret)

Destroy lnv_CoderObject

// Set the request headers
lhc_Client.SetRequestHeader('Content-Type', 'application/x-www-form-urlencoded')

// Create the body (the arguments have been url encoded above)
ls_PostData = 'grant_type=client_credentials'
ls_PostData += '&client_id=' + ls_ClientID
ls_PostData += '&client_secret=' + ls_Secret

ll_return = lhc_Client.SendRequest("POST", ls_Url, ls_PostData)

If ll_return = 1 Then
    ll_ResponseStatusCode = lhc_Client.GetResponsestatusCode()
    ls_ResponseStatusText = lhc_Client.GetResponsestatusText()
    lhc_Client.GetResponseBody(ls_ResponseBody, EncodingUTF8!)
    ls_headers = lhc_Client.GetResponseHeaders()
    MessageBox('Token Response', string(ll_ResponseStatusCode) + ' ' + ls_ResponseStatusText + '~r~n' + ls_ResponseBody)
else
    MessageBox("SendRequest Failed", "Return :" + String ( ll_return ))
end if

destroy lhc_Client

 

This is a modified example of your original OAuthClient code.  You'll just need to tidy it up a bit. Have a look at how the returns are handled. I usually check the return from a http call (e.g. AccessToken() or SendRequest()) separately to the check for ResponseStatusCode.  If the first one fails, it means that it didn't get any return from the server (e.g. timeout, invalid url etc.) which is different from getting a return from the server that is an error (e.g. a 400 Bad Request)

OAuthClient loac_Client
TokenRequest ltr_Request
TokenResponse ltr_Response
OAuthRequest loar_Request
ResourceResponse lrr_Response
String ls_AccessToken
String ls_Body, ls_type, ls_description, ls_state, ls_uri
Long ll_return

String ls_ClientID, ls_Secret, ls_Url

// Setup the arguments for your token request
ls_Url = "https://amtrip.amfresh.com/i/connect/token";
ls_ClientID = "agrox"
ls_Secret = "*agrox+"

// Need to urlencode the arguments to the form post to handle any special characters (e.g. + converts to %2B)
// For this example you really only need it for ls_Secret but I've shown it for both arguments
Blob lblb_ClientID, lblb_Secret
CoderObject lnv_CoderObject
lnv_CoderObject = Create CoderObject

lblb_ClientID = Blob(ls_ClientID, EncodingANSI!)
ls_ClientID = lnv_CoderObject.UrlEncode(lblb_ClientID)
lblb_Secret = Blob(ls_Secret, EncodingANSI!)
ls_Secret = lnv_CoderObject.UrlEncode(lblb_Secret)

Destroy lnv_CoderObject

// Now do your token request
loac_Client = Create OAuthClient

//Step 1: Get the RESTful server access token.
ltr_Request.ClearParams()
ltr_Request.tokenlocation = ls_Url
ltr_Request.granttype = "client_credentials"
ltr_Request.Method = "POST"
ltr_Request.clientid = ls_ClientID
ltr_Request.clientsecret = ls_Secret

ll_Return = loac_Client.AccessToken( ltr_Request, ltr_Response )
If ll_Return = 1 then
    // The Actual Http Call was successful
    long ll_ResponseStatusCode
    string ls_ResponseStatusText
    ll_ResponseStatusCode = ltr_Response.GetStatusCode()
    ls_ResponseStatusText = ltr_Response.GetStatusText()
    ltr_Response.GetBody(ls_Body)
    
    if ll_ResponseStatusCode = 200 Then
        // The Token Request was successful
        ls_AccessToken = ltr_Response.GetAccessToken()
        MessageBox('Token', ls_AccessToken)
    else
        // The Token Request returned an error. This displays the actual html returned
        MessageBox('Token Error', string(ll_ResponseStatusCode) + ' ' + ls_ResponseStatusText + '~r~n' + ls_Body)
        
        // You can have a look at this function. When the secret is wrong ls_type returns "invalid_client"
        ll_Return = ltr_Response.GetTokenError(ls_type, ls_description, ls_uri, ls_state)
        MessageBox( "AccessToken Failed", "Return :" + String ( ll_return ) + "~r~n" + ls_type )
    end if    
Else
    // The Actual Http Call was unsuccessful
    MessageBox( "AccessToken Call Failed", "Return :" + String ( ll_return ))
    
    // From help file the error will be one of the following:
    //    -1 -- A general error occurred
    //    -2 -- Invalid URL
    //    -3 -- Cannot connect to the Internet
    //    -4 -- Timeout
End If

If IsValid ( loac_Client ) Then DesTroy ( loac_Client )

 

 

 

 

 

 

 

Comment
  1. Pepe Cuenca
  2. Thursday, 13 January 2022 11:19 AM UTC
Hi Daryl, I'm using Powerbuilder 2021, yes, it says failed and ll_return = -1
  1. Helpful
  1. Daryl Foster
  2. Thursday, 13 January 2022 11:40 AM UTC
That’s weird. I’ll copy the code and try it in PB 2021 tomorrow. If it’s returned -1, I think that’s a “general error” and the request hasn’t made it to the server correctly.
  1. Helpful
  1. Daryl Foster
  2. Thursday, 13 January 2022 23:59 PM UTC
Hi Pepe, it worked for me in Powerbuilder 2021. I just created a new window, added two buttons cb_httpclient and cb_oauthclient and copied from the browser the two lots of code above then pasted them into the clicked events of the relevant button and they both worked straight away. The HttpClient example returns a 200 OK and json which includes the access_token. If I comment out the line where I urlencode the secret I get a 400 Bad Request which returns json ("error":"invalid_client"}

The only way I can get a -1 return from the HttpClient example is if I have an invalid hostname in my url. I added a typo to the url and changed it from https://amtrip.amfresh.com/i/connect/token to https://aamtrip.amfresh.com/i/connect/token it returns -1, but that is expected. It also returns a -1 if there is a space after the "http://"; in the url.

You can test to see if the HttpClient example is working correctly by using the reqbin server. Try and change the url in the example to:



ls_Url = 'https://reqbin.com/echo/post/json'



and see if that returns a 200 OK response with success json.
  1. Helpful
There are no comments made yet.
Kai Zhao @Appeon Accepted Answer Pending Moderation
  1. Friday, 14 January 2022 00:29 AM UTC
  2. PowerBuilder
  3. # 5

Hi Pepe,

I can get the token via the script below, please give it a try.

String ls_P028_JWTToken
Integer li_P028_GetJWTTokenReturn
RestClient lrc_P028
lrc_P028 = Create RestClient

//client_id=agrox&client_secret=*agrox%2B&grant_type=client_credentials
lrc_P028.SetRequestHeaders( "Content-Type:application/x-www-form-urlencoded;charset=UTF-8" ) //Sets the request header
//Gets the JWT token. The second parameter provides the value according to the token server request.
li_P028_GetJWTTokenReturn=lrc_P028.GetJWTToken("https://amtrip.amfresh.com/i/connect/token", 'client_id=agrox&client_secret=*agrox%2B&grant_type=client_credentials', ls_P028_JWTToken)

If li_P028_GetJWTTokenReturn = 1 Then
//Sets the JWT token
lrc_P028.SetJwtToken( ls_P028_JWTToken)
Else
//Prints the GetJWTToken error message if any
End If

messagebox('', ls_P028_JWTToken)

 

Regards,
Kai

Comment
There are no comments made yet.
Pepe Cuenca Accepted Answer Pending Moderation
  1. Monday, 17 January 2022 11:09 AM UTC
  2. PowerBuilder
  3. # 6

Hi All, 

After a week of testing, all of my/your solutions are correct.

Just tried the code in another environment (Windows Server 2019 vs Windows Server 2008 R2, which was not working) and that was the point. 

I don't know if it is a limitation of WS2008R2.

Thank you very much for your help!!!!

Comment
  1. Miguel Leeuwe
  2. Monday, 17 January 2022 11:13 AM UTC
Thanks for sharing Pepe!
  1. Helpful
  1. Miguel Leeuwe
  2. Monday, 17 January 2022 11:14 AM UTC
Please mark as resolved.
  1. Helpful
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.