Page 1 of 1

Query on OAuth and Authentication Token for Webservice API as a provider

Posted: Tue May 06, 2025 4:33 pm
by sshaila
Hello,

Created a webservice API as an provider using Scott's YAJL services. This http url webservice API was tested in PostMan using Basic Auth (user/password). This works fine locally. This endpoint/url will be provided to Vendor to issue a "Post" API which will place the JSON file in AS400 IFS folder and processed further by RPGLE program.

For implementing this, Vendor has mandated two things - OAuth to be performed using a Certificate generated by us, which will be valid for at least one year. Also provide an additional endpoint at which Vendor can retrieve a new authentication token whenever a previous token expires. I need help on how to generate a OAuth and ideas on how to create additional endpoint for authentication token - should it be a separate source code - url webservice that I need to provide that has the Authentication token generator?

Source code included below'

Code: Select all

       ctl-opt
       Main(GETDSTRCMP)
       pgminfo(*PCML:*MODULE:*DCLCASE)
       option(*srcstmt:*nodebugio:*noshowcpy)
       decedit('0.')
       bnddir('YAJL':'QC2LE')
    
      // debug
        dcl-f QPRINT printer(132) usropn;
        dcl-ds line len(132) inz qualified;
        PrintString char(132);
        end-ds;

        dcl-s method varchar(10);
        dcl-s request char(500);
        dcl-s env pointer;
        dcl-s errMsg varchar(500) inz('Error occurred during processing');
        dcl-s webservice varchar(500);
        dcl-s debug char(1);

        // include the YAJL magic (this srcmbr lives in YAJL/QRPGLESRC by default)
       /copy qcpysrc,yajlproc

        // declare a data structure that is used to store the SUCCESS/FAIL status and
        // the ERRORMSG value. This template is referenced through this program.
        dcl-ds rtnCode_Template qualified template;
        success ind inz(*off);
        errorMsg varchar(500) inz('');
        end-ds;

        // Global Data Structure to webservice return codes
        dcl-ds ds_RtnCode likeds(rtnCode_Template) inz(*likeds);
        dcl-ds psds PSDS qualified;
        program char(10) pos(1);
        procedureName *PROC;
        statusCode *STATUS;
        prvStatus zoned(5) pos(16);
        srcListLineNum char(8) pos(21);
        routineName *ROUTINE;
        nbrPassedParms *PARMS;
        exceptionType char(3) pos(40);
        exceptionNumber char(4) pos(43);
        pgmLib char(10) pos(81);
        ExceptionData char(80) pos(91);
        messageText char(80) pos(91);
        ExceptionId char(4) pos(171);
        date char(8) pos(191);
        year zoned(2) pos(199);
        lastFileUsed char(8) pos(201);
        fileErrorInfo char(35) pos(209);
        rpgFileRoutine char(6) pos(214);
        rpgStatement char(6) pos(214);
        fileRecordFormat char(8) pos(236);
        jobUserNumber char(26) pos(244);
        job char(10) pos(244);
        jobName char(10) pos(244);
        User char(10) pos(254);
        jobUser char(10) pos(254);
        number zoned(6) pos(264);
        jobnumber zoned(6) pos(264);
        jobnumberalpha char(6) pos(264);
        jobDate zoned(6) pos(270);
        runDate zoned(6) pos(276);
        runTime zoned(6) pos(282);
        runtimeHour char(2) pos(282);
        runtimeMinute char(2) pos(284);
        runtimeSecond char(2) pos(286);
        createDate char(6) pos(288);
        createTime char(6) pos(294);
        compilerLevel char(4) pos(300);
        srcFile char(10) pos(304);
        srcLib char(10) pos(314);
        srcMbr char(10) pos(324);
        currentPgmProc char(10) pos(334);
        currentModule char(10) pos(344);
        currentUser char(10) pos(358);
        systemName char(8) pos(396);
        end-ds;

        //-- GETDSTRCMP - Process the incoming webservice JSON payload
        //-- If a POST was requested (write) then read JSON payload and save to the IFS location
        //-- returns *ON if successful, *OFF otherwise
        dcl-proc GETDSTRCMP;
        dcl-pi GETDSTRCMP end-pi;

        monitor;

        // Set SQL option, mainly to force cursor to close at endmodule
        exec sql
        set option naming=*sys,
        commit=*none,
        usrprf=*user,
        dynusrprf=*user,
        datfmt=*iso,
        closqlcsr=*endmod;

        // Initialize program variables
        init();

        // Now process the incoming method - only Post
        select;
        when method='POST';
        methodPOST(ds_RtnCode);
        other;
        ds_RtnCode.errorMsg='* Fail - method('+method+') is not supported';
        endsl;

        if debug <> '';
        line.printstring='Final:'+ds_RtnCode.errorMsg;
        write QPRINT line;
        close qprint;
        endif;

        //Send the JSON response document
        sendBasicResponse(ds_RtnCode);

        return;

        on-error ;
            dsply ('** ENDED ABNORMALLY ** ');
        endmon ;

        end-proc;

        //-- methodPOST() : If a POST was requested (write) then read JSON
        //-- payload and save to the IFS location
        dcl-proc methodPOST;
        dcl-pi *n ind;
        ds_methodPOST likeds(rtnCode_Template);
        end-pi;

        dcl-s docNode like(yajl_val);
        dcl-s errMsg varchar(500);
        dcl-s load_ifs varchar(500);

        load_ifs='/BG/DistroComplete/DC'+
                      %char(%timestamp:*iso0)+'-POST.json';


        // get the JSON document loaded into memory
        // **AND** save the payload direct to the IFS location
        docNode=yajl_stdin_load_tree (*on: errMsg : load_ifs);

        if (docNode=*NULL) or ((errMsg) <> '');
        ds_methodPOST.success=*off;
        ds_methodPOST.errorMsg='* Webservice('+%trim(psds.program)+
        ') failed with error: '+errMsg;
        else;
        ds_methodPOST.success=*on;
        ds_methodPOST.errorMsg='Webservice('+%trim(psds.program)+
        ') Success. Json successfully stored at:'+
        payload_ifs;

	//Process the json to application files	
      
        endif;

        yajl_tree_free(docNode);

        return ds_methodPOST.success;

        end-proc;

        //-- init() : program Initilisation
        dcl-proc init;
        dcl-pi *n ind end-pi;

        // This procedure will return the webservice METHOD
        dcl-pr getenv pointer extproc('getenv');
         *n pointer value options(*string:*trim);
        end-pr;

        clear ds_RtnCode;

        // if the DEBUG=Y parm has come in from the webserver start the debug
        // print and store a more detailed version of variable 'webserver'
        if debug <> '';
        open qprint;
        webservice= %char(psds.jobNumber);
        write QPRINT line;
        endif;

        // Retrieve the HTTP method - Default to GET if not provided
        env = getenv('REQUEST_METHOD');
        if env <> *null;
           method=%str(env);
        else;
           method='GET';
        endif;

        return *On;

        end-proc;

        //-- sendBasicResponse() : Send the Basic JSON response to the calling
        dcl-proc sendBasicResponse;
        dcl-pi *n ind;
        ds_sendBasicResponse likeds(rtnCode_Template) const;
        end-pi;

        yajl_genOpen(*on);
        yajl_beginObj();
        yajl_addBool('success': ds_sendBasicResponse.success);
        yajl_addChar('errorMsg': ds_sendBasicResponse.errorMsg);

        if debug <> '';
        yajl_addChar('DEBUG': webservice);
        else;
        yajl_addChar('DEBUG': '');
        endif;

        yajl_endObj();

        errMsg=ds_sendBasicResponse.errorMsg;
        if ds_sendBasicResponse.success;
        yajl_writeStdout(200: errMsg);
        else;
        yajl_writeStdout(500: errMsg);
        endif;

        yajl_genClose();

        return ds_sendBasicResponse.success;

        end-proc;


        //-- sendDataResponse() : Send the Basic JSON response to the calling
        dcl-proc sendDataResponse;
        dcl-pi *n ind;
        sendDataResponse likeds(rtnCode_Template) const;
        end-pi;

        yajl_genOpen(*on);
        yajl_beginObj();

        yajl_beginArray(%trim(psds.program));
        yajl_beginObj();
        yajl_addBool('success': sendDataResponse.success);
        yajl_addChar('errorMsg': sendDataResponse.errorMsg);
        yajl_endObj();
        yajl_endArray();

        yajl_endObj();

        errMsg=sendDataResponse.errorMsg;
        if sendDataResponse.success;
        yajl_writeStdout(200: errMsg);
        else;
        yajl_writeStdout(500: errMsg);
        endif;

        yajl_genClose();

        return sendDataResponse.success;

        end-proc;

Re: Query on OAuth and Authentication Token for Webservice API as a provider

Posted: Tue May 13, 2025 3:46 pm
by Scott Klement
Your question doesn't appear to have anything whatsoever to do with HTTPAPI.

You are using YAJL to work with the JSON messages... there is a YAJL forum here.

But, 99% of the work for creating your own OAUTH authentication server isn't formatting/interpreting JSON. That's like 1% of the work. You need to write code to create cryptographic tokens, including both access tokens and refresh tokens, that securely carry authorization claims. You need to be able to integrate your authorization server with your API as well as with the clients calling it. This is where the work is.

Personally, I would use JWT for the tokens. So you might google JWT and learn about them.

Then you need to decide which features of OAUTH you want to support. I would Google OAuth 2.0 (which is what everyone uses) and I would start learning about that.

Or -- more realistically -- you can just buy a software package that contains an OAUTH framework and gives you examples. I helped write one for Midrange Dynamics for their MDRest4i product. Purchasing this would be much easier and safer than writing your own unless you have good reason to invest the time and money in building your own from the ground up.