2 Replies Latest reply: Dec 8, 2016 3:33 PM by Anthony Haxby RSS

    NPrinting 17.2.1 REST API - Create OnDemand Request

    Anthony Haxby

      I'm having issues invoking the API request to en-queue a new on-demand request as per the API (https://help.qlik.com/en-US/nprinting/17.2/APIs/NP%20API/#ondemandRequestsPost).

       

      Authentication is successful and I am able to query Apps, Filters, Reports and On-Demand Requests.  However, when I invoke the API to create an On-Demand request I receive the following error from the server:

       

      {
          "title": "Forbidden",
          "description": "REVEL_CSRF: tokens mismatch."
      }
      
      

       

      This error occurs regardless of the payload being supplied.  Even supplying no payload generates the same result.  Reviewing the error description it appears to relate to a Cross-Site Request Forgery (CSRF) exception where the authentication token associated with the current login session is different.

       

      I'm using .NET Framework v4.6.1 to query the service.  The relevant code is as follows (edited for brevity):

       

      public class NPrintingService
      {
        private HttpClient Client { get; set; }
      
        public NPrintingService(string serviceUrl)
        {
        var httpClientHandler = new HttpClientHandler { UseDefaultCredentials = true };
      
      
        var client = new HttpClient(httpClientHandler)
        {
        BaseAddress = new Uri(serviceUrl)
        };
      
      
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      
        this.Client = client;
        }
      
      
        public async Task<bool> Authenticate()
        {
        var response = await this.Client.GetAsync("api/v1/login/ntlm");
        if (response.IsSuccessStatusCode)
        {
        return await Task.FromResult(true);
        }
      
      
        return await Task.FromResult(false);
        }
      
      
        public async Task<string> CreateOnDemandRequest()
        {
        HttpResponseMessage response = await this.Client.PostAsJsonAsync("api/v1/ondemand/requests", null);
      
      
        if (response.IsSuccessStatusCode)
        {
        var content = await response.Content.ReadAsStringAsync();
        return content;
        }
      
      
        //var error = await response.Content.ReadAsStringAsync(); // NOTE: This code is used to retrieve the underlying 'REVEL_CSRF: tokens mismatch' error
        throw new ApplicationException($"({response.StatusCode}): {response.ReasonPhrase}");
        }
      }
      
      

       

      This code is being used in a Console application running in the context of my domain account.  This domain account has been setup in NPrinting as a user with the 'Administrator' security role.  I've also ensured that the user has been linked with my domain account.

       

      var service = new NPrintingService(ConfigurationManager.AppSettings["ServiceUrl"]);
      
      var authenticate = await service.Authenticate();
      
      var result = await service.CreateOnDemandRequest(); // Exception occurs here
      
      

       

      I can confirm that the request is successfully authenticated.  I've also run this code with Fiddler active and confirmed that the output is the same -- Authentication is successful, but creating the On-Demand request fails.

       

      Has anyone been able to successfully use this API?  If so, how?

        • Re: NPrinting 17.2.1 REST API - Create OnDemand Request
          Anthony Haxby

          One discrepancy I noticed in Fiddler is that after performing the initial Authentication call that the Cookie: NPWEBCONSOLE_SESSION was not the same for the POST request, though it was the same as the GET requests.

           

          GET Request:

          2b959abceaaf4b269651180bae7834ffda463955-_TS:sessionuserid:e66d500966df4d96b9eb52989c13d931NPWEBCONSOLE_XSRF-TOKEN:I/z1TsYFYSOO2P3T/DrgI7IpJt0DTuiWadLOx09YoFQ=

           

          POST Request:

          5faf8b88b0e99baa0681b3ad51658564cc6cfa27-NPWEBCONSOLE_XSRF-TOKEN:I/z1TsYFYSOO2P3T/DrgI7IpJt0DTuiWadLOx09YoFQ=_TS:sessionuserid:e66d500966df4d96b9eb52989c13d931

           

          I've revised my code so that after the Authentication request the NPWEBCONSOLE_XSRF-TOKEN and NPWEBCONSOLE_SESSION cookie values are pulled from the response and then used on each subsequent request, and have confirmed through Fiddler that the cookie values are now consistent amongst all request types, but I still get the same error as before (REVEL_CSRF: tokens mismatch).

          • Re: NPrinting 17.2.1 REST API - Create OnDemand Request
            Anthony Haxby

            Short Answer: Your requests need to include the custom HTTP header X-XSRF-TOKEN.  The value of this header will be identical to the value of the NPWEBCONSOLE_XSRF-TOKEN cookie.  For example (taken from Fiddler, which will successfully submit an OnDemand request)

             

             

            POST https://REDACTED/api/v1/ondemand/requests HTTP/1.1

            Accept: application/json

            X-XSRF-TOKEN: 9z80xdFG/9T+xhKUmBFlFxVOCbkjqimdW+iuUZtXtl4=

            Content-Type: application/json; charset=utf-8

            Host: REDACTED

            Cookie: NPWEBCONSOLE_XSRF-TOKEN=9z80xdFG/9T+xhKUmBFlFxVOCbkjqimdW+iuUZtXtl4=; NPWEBCONSOLE_SESSION=71512e89749096debeeaa8eb3583f508570e1df6-%00NPWEBCONSOLE_XSRF-TOKEN%3A9z80xdFG%2F9T%2BxhKUmBFlFxVOCbkjqimdW%2BiuUZtXtl4%3D%00%00_TS%3Asession%00%00userid%3Ae66d500966df4d96b9eb52989c13d931%00

            Content-Length: 100

            Expect: 100-continue

             

             

            {"Config":{"OutputFormat":"HTML","ReportId":"595d3ca8-ff7a-4199-98ad-dc55e6004b6c"},"Type":"Report"}

             

             

             

             

            Long Answer:

             

             

            The only other instance of this error I could find was for the source for a GO library at (https://github.com/cbonello/revel-csrf/blob/master/csrf.go).

             

             

            I ended up decompiling the NPrinting Server WebEngine (which is built using the .NET Framework) and found in the Startup class the following snippet:

             

             

            directoryName.EnvironmentVariables["GOPATH"] = fileInfo.DirectoryName;

             

             

            Which gave me a clue that the application was actually using something built in GO.  This turned out to be a proxy that sits in front of the WebEngine to validate requests.  The application executable is located at:

             

             

            C:\Program Files\NPrintingServer\proxy\webconsoleproxy\

             

             

            To help diagnose the problem I enabled additional loggin in the app.config, located at:

             

             

            C:\Program Files\NPrintingServer\proxy\webconsoleproxy\src\qlik.com\webconsoleproxy\conf

             

             

            To enable logging, the following changes were made to the end of the conf:

             

             

            log.trace.output = %(app.name)s.log

            log.info.output  = %(app.name)s.log

            log.warn.output  = %(app.name)s.log

            log.error.output = %(app.name)s.log

             

             

            I could then see that the library was missing a token from the client:

             

             

            TRACE 2016/12/08 11:26:44 csrf.go:56: CSRF Filter: cookie found, value: jTmzEhFFnxRGlm11lv3w2zoIuGIxMCbCHb5TjD6B5iE=

            TRACE 2016/12/08 11:26:44 csrf.go:100: REVEL-CSRF: Processing unsafe 'POST' method...

            TRACE 2016/12/08 11:26:44 csrf.go:128: REVEL-CSRF: Token received from client: ''

             

             

            Reviewing the source of the CSRF.go file I found that it was expecting a header named X-XSRF-TOKEN.  Taking an intuitive step I used the same value returned from the NPrinting authentication cookie (NPWEBCONSOLE_XSRF-TOKEN) and was successful in getting the POST requests submitted.