5

Is there a way in Ember.js (and Ember-data) to send credentials to an api that requires Basic HTTP Authentication? I can see how it's done in JQuery here, but don't see any straightforward way to do it in Ember. I thought maybe adding something to the header would work (see below in coffeescript), but no success:

App.AuthAdapter =  DS.RESTAdapter.extend(
    host: 'https://my-api.example.com'
    namespace: 'v1'
    headers:
        "Authorization Basic fooUsername:barPassword"
    ...

3 Answers 3

3

You can extend the default Rest adapter and add a headers hash which will be included in the ajax that's sent.

App.ApplicationAdapter = DS.RESTAdapter.extend(
   headers:
     withCredentials: true
     Authorization: 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
)

Or you could take it a step farther and override the ajax method

App.ApplicationAdapter = DS.RESTAdapter.extend(
  ajax: (url, type, hash) ->
    adapter = this
    new Ember.RSVP.Promise((resolve, reject) ->
      hash = hash or {}
      hash.url = url
      hash.type = type
      hash.dataType = "json"
      hash.context = adapter

      if hash.data and type isnt "GET"
        hash.contentType = "application/json; charset=utf-8"
        hash.data = JSON.stringify(hash.data)

      if adapter.headers isnt `undefined`
        headers = adapter.headers
        hash.beforeSend = (xhr) ->
          forEach.call Ember.keys(headers), (key) ->
            xhr.setRequestHeader key, headers[key]

    hash.success = (json) ->
      Ember.run null, resolve, json

    hash.error = (jqXHR, textStatus, errorThrown) ->
      Ember.run null, reject, adapter.ajaxError(jqXHR)

    Ember.$.ajax hash
  )
)
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your reply! I think this will get me moving in the right direction. After this I realized that because my dev location is different than our api location, Ember sends an HTTP OPTIONS request first, so after our backend person adjusts their stuff, I'll try this again.
Also, I found if I wanted to change the header after the Adapter was initiated, I could do it this way: adapter = this.get('container').lookup('adapter:auth'); adapter.set('headers', { 'Authorization': 'Basic ' + token });
The ajax method is marked private in the source code. I believe it is not future safe do rely on it. The alternative would be to overwrite in your RESTAdapter the methods that use it (find, etc.)
2

Can you use $.ajaxPrefilter? e.g.

Ember.$.ajaxPrefilter (options) ->
  options.xhrFields = { withCredentials: true }
  options.username = 'fooUsername'
  options.password = 'barPassword'
  true # need to return non-falsy here

Comments

2

As @gerry3 stated $.ajaxPrefilter is a valid solution.

But if you want to solve a problem of dynamically changing your Headers AFTER an event, for instance, a successful LOGIN attempt, then you need to put more wires. In my case I need to send back a 'Token' Header that is provided by the server after a successful AJAX-login. But, of course, when the user initiates the App he's not logged-in already.

The problem is that once you reopen or extend the RESTAdapter, or define an ajaxPrefilter, even if you're binding it to a value (or localStorage as in my case) the class won't be following the current variable value. It's like a snapshot taken at some moment. So it's useless in my scenario.

I'm following Embercast Client Authentication which is a good start (code available), but instead of jQuery data-fetching I'm using Ember-Data.

So the trick is to observe the token and re-define the ajaxPrefilter as many times as you need it.

tokenChanged: function() {
    this.get('token')=='' ? 
        localStorage.removeItem('token') : 
            localStorage.token = this.get('token');

    $.ajaxPrefilter(function(options, originalOptions, xhr) {
        return xhr.setRequestHeader('Token', localStorage.token);
    });

}.observes('token')

Therefore, when the user logs-in he'll have a valid token and send it in every request to the server via the RESTAdapter.

Hope this helps someone.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.