Can I pass a bearer token to an Angular JS app in the response headers?

2k views Asked by At

For various reasons I need to use a server technology, in this case ASP.Net to render my Angular JS application. So when the user requests the index page, the request will go through the ASP.Net pipeline which will serve the scripts, views etc for the application.

I need to get a bearer token into the application so that it can call various Web API services.

Is it acceptable from a security perspective to pass the bearer token to the users browser in the response headers? Then when the Angular JS application starts up it will read the value from the header and put it into local storage for subsequent API requests.

User Flow...

  1. User goes to members.domain.com where he/she is not authenticated.
  2. User redirected to OAuth provider.
  3. User authenticates with provider.
  4. User is then redirected back to members.domain.com. User is now authenticated and they have a cookie.
  5. Initial application page is rendered through the MVC pipeline. Claims examined and UI is rendered with menu options (etc) as per the users claims (e.g. role="recruiter" etc).

That gets the initial application rendered on the users browser and the menu options are created based on the users claims.

The missing piece now is to get a bearer token into the browser which can be used to call various APIs.

2

There are 2 answers

6
rdegges On BEST ANSWER

UPDATE: I'm completely changing my answer based on your updated question.

What you're trying to do actually sounds fine to me. It sounds like you're going to be inspecting a JWT as your Bearer token on the client-side.

To do this, you'll need to do one of two things:

  1. If you'd like to keep your application more 'secure' by using cookies, you can create a route on your backend (/me or something similar), that when requested, returns all of the currently logged in user's personal data (their claims, and whatever is needed to render your UI). When the Angular application starts up, it can make a request to this page, will be authenticated by the already set cookies, and will just return whatever data is needed to the frontend to do the rendering.

  2. If you'd like to do everything in pure JS at the risk of being more susceptible to XSS attacks, then instead of storing the user's Bearer token in a cookie, you could store it in HTML5 local storage. This way, your Angular app can access the token via Javascript directly in the browser to render the page. This will likely provider faster performance, but this means that anyone who can run malicious JS on your domain will also be able to read the user's Bearer token (from LocalStorage), and potentially cause problems.

Finally: If you go the LocalStorage route, and store the Bearer token in LocalStorage, than what you can do to authenticate your Angular API requests to your backend is:

  1. Grab the bearer token out of LocalStorage.
  2. Make your API request to your backend, putting the Bearer token into the HTTP Authorization header.
  3. On your backend, parse out the HTTP Authorization header, and grab the token.
  4. Validate the token to make sure the JWT is valid / not expired / etc.
  5. That's it!

The standard HTTP header used for authenticating API requests is HTTP Authorization, so if you want to do the flow you're describing (with LocalStorage), just put your token into HTTP Authorization and you'll be good to go. Most web frameworks handle the parsing of this header automatically, and will look there for credentials like the token you'll be sending.

0
pejman On

you can use this code...

var loginApp = angular.module("LoginApp", ["LocalStorageModule"]);

loginApp.service("loginService", function ($http, $q) {
this.loginService = function (url, method, data, headers) {
    var deferred = $q.defer();
    $http({
        method: method,
        headers: headers,
        data: data,
        url: url
    })
        .success(function (response) {
            deferred.resolve(response);
        })
        .error(function (error) {
            deferred.reject(error);
        });
    return deferred.promise;
};});

loginApp.controller("LoginController", [
"$http", "loginService", "localStorageService",
function (http, service, localStorageService) {
    var self = this;
    localStorageService.remove("authorizationData");
    self.login = function () {

        var url = "http://localhost:port/token";
        var data = "grant_type=password&username=" + self.username + "&password=" + self.password;

        service.loginService(url, "POST", data, { 'Content-Type': "application/x-www-form-urlencoded" })
            .then(function (response) {

                var obj = {
                    token: response.access_token,
                    userName: response.userName
                };
                localStorageService.set("authorizationData", obj);
            }, function (error) {
                alert("username or password incorrect");
            });
    };
}]);