CsrfValidationException on Custom Plugin REST Endpoint Despite GET Request

Hi everyone,

I am building a custom IdentityIQ plugin that uses a JavaScript snippet on the home page to query a custom plugin REST endpoint (ChatResource).

Even after changing my endpoint method from POST to GET to avoid state-changing CSRF filters, I am still encountering the following security exception in the system logs when the frontend script queries the endpoint:

The Error:

<SyslogEvent classname="sailpoint.server.CsrfService" eventLevel="ERROR">
  <EventMessage>CSRF validation failed for /identityiq/plugin/rest/iiqbot/ask</EventMessage>
</SyslogEvent>

ChatResource.java:

    @GET
    @Path("ask")
    public Response ask(@QueryParam("message") String message) {
        Map<String, Object> result = new HashMap<>();
        try {
            SailPointContext context = getContext();
            Identity user = getLoggedInUser();
            String msg = (message != null) ? message.toLowerCase().trim() : "";

            String reply = buildReply(context, user, msg);
            result.put("reply", reply);
            
            return Response.ok(result).build();
        } catch (Exception e) {
            result.put("reply", "An error occurred: " + e.getMessage());
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(result).build();
        }
    }

snippet.js:

jQuery.ajax({
            url: restUrl,
            type: 'GET',
            dataType: 'text', 
            success: function(res) {
                var replyText = (res && res.reply) ? res.reply : "Received empty response from server.";
                msgContainer.append('<div class="iiq-bubble iiq">' + replyText + '</div>');
                msgContainer.scrollTop(msgContainer[0].scrollHeight);
            },
            error: function(xhr, textStatus, errorThrown) {
                var debugMsg = '<b>Error: ' + textStatus + '</b><br/>Response: ' + xhr.responseText;
                msgContainer.append('<div class="iiq-bubble iiq">' + debugMsg + '</div>');
                msgContainer.scrollTop(msgContainer[0].scrollHeight);
            }
        });

My Setup:

  1. Java Resource Class: Extends BasePluginResource with @Path("iiqbot") and @AllowAll.

  2. Method Annotation: Annotated with @GET and @Path("ask").

  3. Frontend Call: Using PluginHelper.getPluginRestUrl('iiqbot/ask?message=...') via jQuery.ajax.

Why is RestCsrfValidationFilter intercepting and throwing a CsrfValidationException on a @GET request under /plugin/rest/*, and how can I correctly authorize or exempt a custom plugin REST endpoint from this validation?

If i access: http://[ip]:8080/identityiq/plugin/rest/iiqbot/ask, then get “CSRF validation failed” error. In my plugin snippet ui, i get the output mentioned below:

Any help or best practices would be greatly appreciated!

the problem is the request is missing authentication in the ajax call, so u need to pass the CSRF token from there to make sure api call works. you can use the PluginHelper.getCsrfToken(); which comes out of box and it should helo with the error.

Switching from POST to GET won’t get fix the issue. The CSRF filter on /plugin/rest/* checks for the session’s CSRF token in a request header, regardless of HTTP method, and @AllowAll only controls authorization, not CSRF. Opening the URL directly in the browser will always fail for the same reason, since an address-bar request can’t send that header.

can you send the token with your ajax call:

jQuery.ajax({
    url: restUrl,
    type: 'GET',
    dataType: 'json',
    headers: {'X-XSRF-TOKEN': PluginHelper.getCsrfToken()},
    ...
});

I wouldn’t disable CSRF globally. cab you please check the plugin docs if PluginHelper.getCsrfToken() is available.

can you fix below things also

  1. With dataType: 'text', res is a plain string, so res.reply is always undefined and you’ll get the “empty response” message. Use dataType: 'json' (or JSON.parse(res)).
  2. Wrap the message in encodeURIComponent() when you build the query string.
  3. You’re appending replyText and xhr.responseText as raw HTML. Use .text() on a created element instead to avoid XSS.
  4. Add @Produces(MediaType.APPLICATION_JSON) to your resource method or class.
  5. Don’t return e.getMessage() to the browser. Log it server-side and return a generic message.
  6. Consider @RequiredRight instead of @AllowAll if the replies expose identity or access data.

try it and let me know if it fix your issue