[Solved] Authorization header isn't being sent on POST

I have a custom app that sends a POST to get some data. I noticed however in my local development (without xpack installed) the Authorization header isn't sent.

Here's a bit of code that works (from another plugin but follows the same flow):

return this.http.get(chrome.addBasePath('/api/watcher/watchers')).then(....

Will send the Authorization header:
Authorization:Basic Z...

However this plugin does a POST:

return this._http.post(chrome.addBasePath('/api/app/apps'), {indices: indices}).then(...

This one does not send a Authorization header and I get an error:

[security_exception] missing authentication token for REST request [...

Both are being sent before the page loads via the resolve action in the angular route. Is there something that I need to specifically set for POSTs to send the Auth header? Are there any known issues where the Auth header will not be sent or is ripped out by accident?

Does anyone know where this Auth header is stored between requests?

have you add request header whitelist ?

I didn't have to in the past, not sure how to even do that. Both of the projects simply have the standard routing backend for any plugin.

Where would this whitelist live?

Further frustration and trials. I've discovered that this one specific app refuses to send the authorization header. What's funny is that if I hijack a url of another plugin, it works!

So for example, if my "apps" plugin suddenly changes it's endpoints from:

/api/app_plugin/apps

to

/api/watcher/apps

The "watcher" URL portion is from another plugin installed. The "apps" plugin responds to it since I set up the route and it gets the authorization header. But it only gets the authorization header IF I visit the "watcher" plugin first which hits /api/watcher/watches.

If I uninstall the "watcher" plugin, the authorization header isn't set to any URL related to the "apps" plugin.

This whole situation is baffling. Why is this custom plugin I've built called "watchers" work every single time with no issues. I have about 4 other plugins as well, none of them have issues.

P.S. This issue doesn't exist at all in my actual environments because I'm using x-pack security and it seems the cookie is fine and happy with the whole situation.

would you mind show me your plugin source?

It's troublesome with my company, they fear the release of code (regardless of how generic it is). For now I'm going to create a couple sample projects and hopefully recreate the issue in a stripped down version.

It may either make the issue obvious and this help me fix it, or will be shareable. Thanks for your help @whoami

@Kikketer the browser decides when to send the Authorization header, and only does so when it gets a 401 response from the backend with the same authentication realm name. See http://stackoverflow.com/a/12701105/296172

In order for your plugin to get the same Authorization header that other plugins are getting, make sure to properly forward 401 responses and check out the way that Kibana does it here: https://github.com/elastic/kibana/blob/master/src/core_plugins/elasticsearch/lib/cluster.js#L91-L101

Thanks @spalger I'll look at that. Thing is my "watcher" plugin that's working correctly didn't do anything special to get the authorization working. Same goes for the "apps" plugin where authentication doesn't work.

I'll be building out generics whenever I find the time to see if I can narrow down the differences. But both are relatively simple plugins.

For now I really hacked the "apps" plugin to just work locally for me:

  if (process.env.WTF && !request.headers.authorization) {
    request.headers.authorization = 'Basic ZW...'
  }

Like I said, in production it works fine because the cookies are golden and we are using x-pack.

@spalger This issue is starting to spread to another plugin of mine. Not sure why it wouldn't be sending the authentication headers by default since the endpoints are just inside the server side of Kibana.

A POST call to: https://localhost:5601/yzb/es_admin/_mget has the authentication attached, but immedately afterward with my own endpoints: https://localhost:5601/yzb/api/rawlog/search does not.

The server side is pretty simple:

module.exports = function(server) {
  //Search
  server.route({
    method: ['POST'],
    path: '/api/rawlog/search',
    handler: function(request, reply) {
      let searchIndex = request.payload.index

      search(server, request, searchIndex)
        .then(results => {
          reply(jsend.success(results))
        })
        .catch(err => {
          reply(jsend.error(err))
        })
    }
  })

Other plugins seem to work just fine. I'm not sure actually how to send a 401 response via the server side plugin code.

I've been unable to narrow this down as well. I have a similar project that i've branched. One branch it'll send the auth headers, the other branch it will not. The EXACT same code is placed in both branches but the auth goes in one but not the other:

Client (on initialize of the constructor):

this.http.post(chrome.addBasePath('/api/rawlog/wtf'))

Server:

  server.route({
    path: '/api/rawlog/wtf',
    method: ['POST'],
    handler(request, reply) {
      reply({
        ok: true,
        resp: []
      })
    }
  })

Similar to my original post, I assume the auth is working in the first branch because something else is initiating this authentication requirement. I'm not sure what it is however...

Is there something in the server that I need to set? In the 8 plugins I've written so far I've never had to do anything special to get authentication to work, it just worked. Now 2 of them are starting to fail and only because I moved some code around (instead of being one big file).

I've created a generic plugin that gets the authorization header sent along fine. There's literally no extra configuration needed.
I'm currently working through the "not-working" branch to see if I can get it to stop working now. I assume by doing this I'll discover the issue.

I discovered the issue, narrowed down to one line. The catch error has to be sent in the raw instead of wrapped up in a jsend (or any other object)

If you compare working to non-working that's the only difference. In non-working the authorization headers are not sent.

The "i need authorization headers" command is sent (invisible to chrome tools and such) as an error and the error object needs to be in tact to work properly. I witnessed this by adding a console log in the catch section and saw that it was thrown even when everything else was working.

SOLVED! WOOT

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.