define([ 'jquery', 'underscore', 'js/programs/utils/api_config' ], function( $, _, apiConfig ) { 'use strict'; var auth = { autoSync: { /** * Override Backbone.sync to seamlessly attempt (re-)authentication when necessary. * * If a 401 error response is encountered while making a request to the Programs, * API, this wrapper will attempt to request an id token from a custom endpoint * via AJAX. Then the original request will be retried once more. * * Any other response than 401 on the original API request, or any error occurring * on the retried API request (including 401), will be handled by the base sync * implementation. * */ sync: function( method, model, options ) { var oldError = options.error; this._setHeaders( options ); options.notifyOnError = false; // suppress Studio error pop-up that will happen if we get a 401 options.error = function(xhr, textStatus, errorThrown) { if (xhr && xhr.status === 401) { // attempt auth and retry this._updateToken(function() { // restore the original error handler options.error = oldError; options.notifyOnError = true; // if it fails again, let Studio notify. delete options.xhr; // remove the failed (401) xhr from the last try. // update authorization header this._setHeaders( options ); Backbone.sync.call(this, method, model, options); }.bind(this)); } else if (oldError) { // fall back to the original error handler oldError.call(this, xhr, textStatus, errorThrown); } }.bind(this); return Backbone.sync.call(this, method, model, options); }, /** * Fix up headers on an imminent AJAX sync, ensuring that the JWT token is enclosed * and that credentials are included when the request is being made cross-domain. */ _setHeaders: function( ajaxOptions ) { ajaxOptions.headers = _.extend ( ajaxOptions.headers || {}, { Authorization: 'JWT ' + apiConfig.get( 'idToken' ) }); ajaxOptions.xhrFields = _.extend( ajaxOptions.xhrFields || {}, { withCredentials: true }); }, /** * Fetch a new id token from the configured endpoint, update the api config, * and invoke the specified callback. */ _updateToken: function( success ) { $.ajax({ url: apiConfig.get('authUrl'), xhrFields: { // See: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials withCredentials: true }, crossDomain: true }).done(function ( data ) { // save the newly-retrieved id token apiConfig.set( 'idToken', data.id_token ); }).done( success ); } } }; return auth; } );