Page 1 of 1

OAUTH2

Posted: Fri Dec 13, 2024 8:52 am
by twisteddisco
Hi,

I need some pointers for anybody who has used HTTPAPI to work with OAUTH2.

I need to work with OAUTH2 to get the token before processing further requests.

So what I need to do is:
1.) Get a token from a given URL using client id and secret

Information from the vendor:

Code: Select all

curl --request POST --url https://xxxxx-xxx.xxxxi.xx/xxx/xxxxx/oauth/v2/token --header 'content-type: application/x-www-form-urlencoded' --data grant_type=client_credentials --data client_id=<your_client_id> --data client_secret=<your_client_secret>
Answer:

Code: Select all

{"access_token": "<a_valid_token>", "token_type": "Bearer", "expires_in": 3600}
Looking at other examples I am guessing something like:

Code: Select all

 rc = http_req( 'POST'
              :'https://xxxxx-xxx.xxxxi.xx/xxx/xxxxx/oauth/v2/token'
              : *omit
              : resultStr // String to receive the results
              : *omit
              : 'grant_type=client_credentials'
              : 'application/x-www-form-urlencoded'); 
How do I pass the grant_type etc?

2.) Make use of the token for further processing

What I am struggling with at the moment is the calls I need to make to a.) get the token and b.) how to make use of the token in the program.

IS there anybody kind enough to give me some pointers on what processes I need to call to just get the token and then set the token up to be used.

Many thanks all

Re: OAUTH2

Posted: Fri Dec 13, 2024 9:22 pm
by Scott Klement
Hello,

The concept here is that you are calling an authentication API. You might think of this as "signing on to the application." It will generate a temporary access token (basically a cryptographically generated key) that you can use temporarily to show that you are logged in. This lets you access the resources that the API provides by identifying your logged in session.

So first you get the token like this:

Code: Select all


dcl-s client_id      varchar(4096) inz('<your client id>');
dcl-s client_secret  varchar(4096) inz('<your client secret');
dcl-s sendData       varchar(16384);

dcl-ds authResult qualified;
  access_token varchar(4096);
  token_type   varchar(20);
  expires_in   int(10);
end-ds;

sendData = 'grant_type=client_credentials'
         + '&client_id=' + http_urlEncode(client_id)
         + '&client_secret=' + http_urlEncode(client_secret);

 rc = http_req( 'POST'
              :'https://xxxxx-xxx.xxxxi.xx/xxx/xxxxx/oauth/v2/token'
              : *omit
              : resultStr // String to receive the results
              : *omit
              : sendData
              : 'application/x-www-form-urlencoded'); 

// FIXME: check RC for error. etc

data-into authResult %DATA(resultStr)
                     %PARSER('YAJLINTO');
client_id and client_secret are strings that will be provided by whomever is in charge of the login server, but basically they are the userid/password that you are logging into their system with. (They may be random codes -- but you can think of them like a userid/password.)

The output of this is the access token that you can pass to other APIs, it provides info about who you are and what authorities you should have, and so forth. To pass it, you do something like this (you didn't provide specs for this part)

Code: Select all

// now you have authResult structure that contains the 
// access token.

// When you need to call an API using that token, you do this:

HTTP_setAuth( HTTP_AUTH_BEARER: '': authResult.access_token);

rc = http_req( ... etc ... )
If you are feeling "in over your head", I'd be happy to consult on this project. I can help with writing the code, teaching your developers, etc. That way you will have an experienced person to help you get started. If that interests you, please let me know.

Re: OAUTH2

Posted: Tue Jan 14, 2025 6:28 pm
by twisteddisco
Thankyou for the reply Scott.

I have one more question and excuse the old school RPG code, not a 'free' programming company where I am.

So I have all of the code in place and its looks good so far, the code is:

Code: Select all


D OauthData       ds                  qualified  
D   access_token              4096a              
D   token_type                  20a              
D   expires_in                  10i 0            

D resultStr       S               a   Len(16000000) Varying

http_setOption('timeout': '30');                                            
http_setOption('network-ccsid': '1208' );                                   
http_use_cookies(*OFF);                                                     
                                                                            
tokenendpoint = %Trim(wAuthUrl);                                            
resultStr     = *Blanks;                                                    
sendData = 'grant_type=client_credentials'                                  
         + '&client_id=' + http_urlEncode(client_id)                        
         + '&client_secret=' + http_urlEncode(client_secret);               
                                                                            
rc = http_req( 'POST'                    // Type                            
              : tokenendpoint            // Url                             
              : *omit                    // Result Stmf                     
              : resultStr                // String to receive the results   
              : *omit                    // Send Stmf                       
              : sendData                 // Send String                     
              : 'application/x-www-form-urlencoded'); // Content            
              
If rc <> 1;      
  wAuthMsg    = http_error(*omit: wErrorStatus);                                     
  Oauth2Error = 'Y';                                                                 
Else;                                                                                
  data-into authResult %DATA(resultStr) %PARSER('YAJLINTO');                         
  Oauth2Error = *Blanks;                                                             
Endif;                                                                               
I am happy that all of the above is fine and we can see the content of the various fields below used in the call, the fields have been anonymized for security etc:

Code: Select all

TOKENENDPOINT =                                                        
          ....5...10...15...20...25...30...35...40...45...50...55...60 
     1   'https://xxxx-xxx.xxx.xx/xxxxx/oauth/v2/token         '
    61   '                                                            '
    
SENDDATA =                                                             
          ....5...10...15...20...25...30...35...40...45...50...55...60 
     1   'grant_type=client_credentials&client_id=xxxx-xxx-xx-9'
    61   '8cd-xxxxxxx++++&client_secret=xxxx-xxx-xxx-xx-a'
   121   'xxxxx++++                                             '
On the return from the call to http_req I get the response of -1, when it drops down to get the error I get "This page requires a user-id & password "

I have read other threads and I am a little confused. Does this actually mean I need a user i.d and password to be able to get the Oauth2 request to work?

Here is the log:

Code: Select all

HTTPAPI Ver 1.49 released 2024-04-16
NTLM Ver 1.4.0 released 2014-12-22
OS/400 Ver V7R5M0

2025-01-14-19.18.34.546252: recvdoc parms: identity 46
2025-01-14-19.18.34.546272: SetError() £36: This page requires a user-id & password
2025-01-14-19.18.34.546333: AuthPlugin_mustReceiceAuthErrorPage(): entered
2025-01-14-19.18.34.546356: recvdoc(): entered
2025-01-14-19.18.34.546376: SetError() £0:
2025-01-14-19.18.34.546395: recvdoc(): Receiving 46 bytes.
[b]{   "errmsg":"unauthorized",   "errcode":401 }[/b]
2025-01-14-19.18.34.546433: recvdoc(): have 46 of 46
[b]2025-01-14-19.18.34.546452: SetError() £36: This page requires a user-id & password[/b]
2025-01-14-19.18.34.546489: http_close(): entered


Re: OAUTH2

Posted: Tue Jan 14, 2025 9:15 pm
by Scott Klement
twisteddisco wrote: Tue Jan 14, 2025 6:28 pm On the return from the call to http_req I get the response of -1, when it drops down to get the error I get "This page requires a user-id & password "

I have read other threads and I am a little confused. Does this actually mean I need a user i.d and password to be able to get the Oauth2 request to work?
This server requires you to log in somehow. The most common way to do that is with a userid/password, but its possible to get this error code with other authentication types as well. (In which case the error message may be slightly misleading.)

Right now you're just connecting and not telling who you are. You are expecting it to grant you access to it's auth tokens, but you've done nothing to establish who you are.

Every site is different. But most of them require you to tell it who you are by sending "basic auth", which in HTTPAPI is provided with the http_setAuth() subprocedure. And the most common data to provide to establish your identity is a userid/password. You aren't doing that in your example, so it makes perfect sense that you're getting this message.

Re: OAUTH2

Posted: Wed Jan 15, 2025 9:50 am
by emaxt6
Beware that you left sensible information in the log above, any one can use the service as you, especially if it is payment processor system.
Please remove it, or rotate keys, you have published to the whole internet.

Secondly, use VARCHAR or VARYING in interfacing with web layers, that is usually the expected thing to do, simplify code and avoid TRIM fest explosion, less work, less bugs.

Here for example you are URL encoding the spaces in the credentials, trailing space get encoded as "+".
If the backend, post URL decoding, is possible that blank gets used as if part of the credentials. Just a thing to check.

Re: OAUTH2

Posted: Wed Jan 15, 2025 10:01 am
by emaxt6
twisteddisco wrote: Tue Jan 14, 2025 6:28 pm not a 'free' programming company where I am.
... just an advice, and it is not a matter of taste or programming language philosophy. It more of efficiency and getting things done with less money.

If you start interfacing with complex web services REST/JSON , do yourself a favour, switch to free format.
It is objectively easier and cleaner, less headaches, especially when you start navigating and referencing deep DS structures with long names, internal arrays etc. typical of this kind of services.

When using RPG-free, with Scott tools for HTTPAPI and JSON, I find even simpler to interface to modern REST/JSON and with more robust results and easier to maintain than even other languages (RPG is straight to the point, with less noise, and you have always a database at your disposal).
The JSON <-> DS isomorphism is pretty neat using Scott JSON tools, even IMHO better than using the DB2 functions.

Re: OAUTH2

Posted: Thu Jan 16, 2025 10:32 am
by twisteddisco
Points taken on board! Thankyou