Send a token with every baseline synchronization request

My PHP api requires a user token to be sent with every request from my external Backbone application to make sure the user ...

  • Actively
  • Has permission to access the resource

What is the easiest way to install this in Backbone? I guess the only way is to overwrite Backbone.sync, but what will the code look like? CoffeeScript is preferred.

EDIT

Two more things
1. I would like to redirect the user to /login if I get 403: Access Forbidden Error
2. I am pulling a user model that includes a token from localStorage when the application loads 3. I have baseModel and baseCollection, all models / collections are taken from

+10
javascript api coffeescript
Jan 7 '13 at 20:59
source share
4 answers

You can do it:

 var _sync = Backbone.sync; Backbone.sync = function(method, model, options) { if( model && (method === 'create' || method === 'update' || method === 'patch') ) { options.contentType = 'application/json'; options.data = JSON.stringify(options.attrs || model.toJSON()); } _.extend( options.data, { "access_token": "some-token" }); return _sync.call( this, method, model, options ); } 

And just listen for the fail event from the fetch / save method to redirect the user to /login

 model.fetch().fail( /* redirect */ ) 
+11
Jan 07 '13 at 22:24
source share

Backbone uses jQuery $. ajax , so you can use $ .ajaxSetup to "set default values ​​for future Ajax requests":

 $.ajaxSetup({ headers: { "accept": "application/json", "token": YOUR_TOKEN } }); 

Update: An improvement on this idea (thanks to @Glen) is to use $. ajaxSend to check for a token every time before setting it in the request headers:

 $(document).ajaxSend(function(event, request) { var token = App.getAuthToken(); if (token) { request.setRequestHeader("token", token); } }); 

Where App.getAuthToken () is a function in your Backbone application.

+18
03 Oct '13 at 0:42
source share

Authentication is the responsibility of the application.

For a Backbone application, the auth logic should be in the Backbone code, and changing the global behavior of jQuery ajax should be avoided at all costs.

Cons of ajaxSetup or ajaxSend

From the jQuery ajaxSetup document:

Note. The settings specified here will affect all calls to $.ajax or Ajax-based derivatives such as $.get() . This can cause unwanted behavior, as other subscribers (for example, plugins) can expect the usual default settings. For this reason, we strongly recommend against using this API. Instead, set the parameters explicitly in the call or define a simple plugin for this.

ajaxSend has the same problem as mentioned above. The only advantage he has over ajaxSetup is the function call every time, which gives you more flexibility than the object-based parameters passed to ajaxSetup .

The safest way: AuthModel and AuthCollection

Put the authentication logic in the base model and collection. This is the most common solution.

Here you can use an existing BaseModel , but I would prefer to separate BaseModel from AuthModel , as you can create a custom model that uses your base model, but also uses a different external API, for example.

Since the new sync function for the model and collection is similar, but both can have a different parent implementation, I created a simple function generator.

 /** * Generates a new sync function which adds the token to the request header * and handles a redirect on error. * @param {Function} syncFn the parent `sync` function to call. * @return {Function} a new version of sync which implements the auth logic. */ var authSyncFunction = function(syncFn) { return function(method, model, options) { options = options || {}; var beforeSend = options.beforeSend, error = options.error; _.extend(options, { // Add auth headers beforeSend: function(xhr) { xhr.setRequestHeader('Authorization', "Bearer " + yourTokenHere); if (beforeSend) return beforeSend.apply(this, arguments); }, // handle unauthorized error (401) error: function(xhr, textStatus, errorThrown) { if (error) error.call(options.context, xhr, textStatus, errorThrown); if (xhr.status === 401) { Backbone.history.navigate('login'); } } }); return syncFn.call(this, method, model, options); }; }; 

Use a generator for both the model and the collection.

 var AuthModel = BaseModel.extend({ sync: authSyncFunction(BaseModel.prototype.sync) }); var AuthCollection = BaseCollection.extend({ sync: authSyncFunction(BaseCollection.prototype.sync) }); 

Then you will be ready to use them on models and collections, for which you will definitely need authentication. Since you already used the base model and the collection, it would be easy to change BaseModel.extend to AuthModel.extend .

Although I know that you asked for forwarding to the 403 Forbidden answer, I think it should be at 401 Unauthorized . See 403 Forbidden vs 401 Unauthorized HTTP Responses

Override sync trunk

If you do not want to change all models and collections at the moment, but still want to follow the recommendations and avoid changing the global ajax setting, overriding the Backbone.sync function is a simple alternative.

Using our previously defined sync generator:

 Backbone.sync = authSyncFunction(Backbone.sync); 

Local storage and authentication management

To manage data in local storage, check Backbone-session .

This is a good implementation of the Backbone model, which synchronizes with local storage instead of the REST API. It also provides a nice interface for managing authentication.

 // Extend from Session to implement your API behaviour var Account = Session.extend({ signIn: function () {}, signOut: function () {}, getAuthStatus: function () {} }); // Using the custom Account implementation var session = new Account(); session.fetch() .then(session.getAuthStatus) .then(function () { console.log('Logged in as %s', session.get('name')); }) .fail(function () { console.log('Not yet logged in!'); }); 
+1
Feb 01 '17 at 23:13
source share
 Backbone.$.ajaxSetup({ headers: { 'sid': 'blabla' } }); 
-3
Jun 04 '15 at 10:18
source share



All Articles