AngularJS: How to send authentication token with $ resource requests?

I want to send an authentication token when requesting a resource from my API.

I implemented the service using $ resource:

factory('Todo', ['$resource', function($resource) { return $resource('http://localhost:port/todos.json', {port:":3001"} , { query: {method: 'GET', isArray: true} }); }]) 

And I have a service that stores the auth token:

 factory('TokenHandler', function() { var tokenHandler = {}; var token = "none"; tokenHandler.set = function( newToken ) { token = newToken; }; tokenHandler.get = function() { return token; }; return tokenHandler; }); 

I want to send a token from tokenHandler.get every time I send a request through the Todo service. I was able to send it, putting it in a call to a specific action. For example, this works:

 Todo.query( {access_token : tokenHandler.get()} ); 

But I would prefer to define access_token as a parameter in the Todo service, since it must be sent with every call. And improve DRY. But everything in the factory is executed only once, so the access_token must be available before the factory definition, and it cannot change it afterwards.

Is there a way to add a dynamically updated request parameter to a service?

+62
javascript angularjs authentication
Jun 24 2018-12-12T00:
source share
8 answers

Thanks to Andy Jocelyn. I chose his idea to wrap the action of resources. Now the service for the resource is as follows:

 .factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) { var resource = $resource('http://localhost:port/todos/:id', { port:":3001", id:'@id' }, { update: {method: 'PUT'} }); resource = tokenHandler.wrapActions( resource, ["query", "update"] ); return resource; }]) 

As you can see, a resource is determined, first of all, in the usual way. In my example, this includes a custom action called update . Subsequently, the resource is overwritten by returning the tokenHandler.wrapAction() method, which takes the resource and an array of actions as parameters.

As expected, the latter method actually includes actions to enable the authentication token in each request and returns the changed resource. So let's look at the code for this:

 .factory('TokenHandler', function() { var tokenHandler = {}; var token = "none"; tokenHandler.set = function( newToken ) { token = newToken; }; tokenHandler.get = function() { return token; }; // wrap given actions of a resource to send auth token with every // request tokenHandler.wrapActions = function( resource, actions ) { // copy original resource var wrappedResource = resource; for (var i=0; i < actions.length; i++) { tokenWrapper( wrappedResource, actions[i] ); }; // return modified copy of resource return wrappedResource; }; // wraps resource action to send request with auth token var tokenWrapper = function( resource, action ) { // copy original action resource['_' + action] = resource[action]; // create new action wrapping the original and sending token resource[action] = function( data, success, error){ return resource['_' + action]( angular.extend({}, data || {}, {access_token: tokenHandler.get()}), success, error ); }; }; return tokenHandler; }); 

As you can see, the wrapActions() method creates resource parameters from it and loops through the actions array to call another tokenWrapper() function for each action. At the end, it returns a modified copy of the resource.

The tokenWrapper method first creates a copy of a previously existing resource action. This copy has a trailing underscore. So query() becomes _query() . Subsequently, the new method overwrites the original query() method. This new method wraps _query() , as Andy Hoslin suggested, to provide an authentication token with every request sent through this action.

It’s good that with this approach, we can still use the predefined actions that come with each angularjs resource (get, query, save, etc.), without the need to redefine them. And in the rest of the code (for example, in controllers) we can use the default action name.

+60
Jun 24 '12 at 21:20
source share

Another way is to use an HTTP interceptor, which replaces the "magic" authorization header with the current OAuth token. The following is OAuth code, but a fix that is a simple exercise for the reader.

 // Injects an HTTP interceptor that replaces a "Bearer" authorization header // with the current Bearer token. module.factory('oauthHttpInterceptor', function (OAuth) { return { request: function (config) { // This is just example logic, you could check the URL (for example) if (config.headers.Authorization === 'Bearer') { config.headers.Authorization = 'Bearer ' + btoa(OAuth.accessToken); } return config; } }; }); module.config(function ($httpProvider) { $httpProvider.interceptors.push('oauthHttpInterceptor'); }); 
+34
Jul 17 '13 at 8:56
source share

I really like this approach:

http://blog.brunoscopelliti.com/authentication-to-a-restful-web-service-in-an-angularjs-web-app

where the token is always automatically sent inside the request header without the need for a wrapper.

 // Define a new http header $http.defaults.headers.common['auth-token'] = 'C3PO R2D2'; 
+21
Nov 07 '13 at 14:16
source share

You can create a wrapper function for it.

 app.factory('Todo', function($resource, TokenHandler) { var res= $resource('http://localhost:port/todos.json', { port: ':3001', }, { _query: {method: 'GET', isArray: true} }); res.query = function(data, success, error) { //We put a {} on the first parameter of extend so it won't edit data return res._query( angular.extend({}, data || {}, {access_token: TokenHandler.get()}), success, error ); }; return res; }) 
+9
Jun 24 '12 at 18:35
source share

I also had to deal with this problem. I do not think that if this is an elegant solution, but it works, and there are 2 lines of code:

I assume that you received your token from your server after authentication in SessionService, for example. Then call this method:

  angular.module('xxx.sessionService', ['ngResource']). factory('SessionService', function( $http, $rootScope) { //... function setHttpProviderCommonHeaderToken(token){ $http.defaults.headers.common['X-AUTH-TOKEN'] = token; } }); 

After that, all your requests from $ resource and $ http will have a token in the header.

+5
May 23 '13 at 13:38
source share

Another solution would be to use resource.bind (optional ParamDefaults), which return a new instance of the resource associated with the optional parameters

 var myResource = $resource(url, {id: '@_id'}); var myResourceProtectedByToken = myResource.bind({ access_token : function(){ return tokenHandler.get(); }}); return myResourceProtectedByToken; 

The access_token function will be called every time any action on the resource is called.

+3
Mar 26 '14 at 21:11
source share

I might not understand your whole question (feel free to correct me :)), but specifically to add an access_token to each request, did you try to insert the TokenHandler module into the Todo module?

 // app var app = angular.module('app', ['ngResource']); // token handler app.factory('TokenHandler', function() { /* ... */ }); // inject the TokenHandler app.factory('Todo', function($resource, TokenHandler) { // get the token var token = TokenHandler.get(); // and add it as a default param return $resource('http://localhost:port/todos.json', { port: ':3001', access_token : token }); }) 

You can call Todo.query() and it will add ?token=none to your url. Or, if you prefer to add a placeholder token, you can do this too:

 http://localhost:port/todos.json/:token 

Hope this helps :)

+1
Jun 24 2018-12-12T00:
source share

Following the accepted answer, I would suggest expanding the resource to set the token with the Todo object:

 .factory('Todo', ['$resource', 'TokenHandler', function($resource, tokenHandler) { var resource = $resource('http://localhost:port/todos/:id', { port:":3001", id:'@id' }, { update: {method: 'PUT'} }); resource = tokenHandler.wrapActions( resource, ["query", "update"] ); resource.prototype.setToken = function setTodoToken(newToken) { tokenHandler.set(newToken); }; return resource; }]); 

Thus, there is no need to import TokenHandler every time you want to use the Todo object, and you can use:

todo.setToken(theNewToken);

Another change I would like to make is to allow default actions if they are empty in wrapActions :

 if (!actions || actions.length === 0) { actions = []; for (i in resource) { if (i !== 'bind') { actions.push(i); } } } 
+1
Oct. 06 '14 at 12:52
source share



All Articles