Oauth 2.0 token based authentication

I have gone through multiple documents , Including ng-cordova and oauth-ng but I still can’t find any resource which deals with a basic token based authentication in angularjs/Ionic

I am having trouble about how to make this curl call in angularjs

curl -X POST -vu sampleapp:appkey http://sampleurl/oauth/token -H "Accept: application/json" -d "password=pwd&username=sampleuname&grant_type=password&scope=read%20write&client_secret=appkey&client_id=sampleapp"

I am doing this and it’s giving me a 401 error. However a curl call works just fine.

$scope.login = function() {

            $http({
              method: "post", 
              url: "http://sampleurl/oauth/token", 
              data:  "client_id=" + clientId + "&client_secret=" + clientSecret + "password=pwd&username=sampleuser&grant_type=password" + "&scope=read%20write",
              withCredentials: true,
              headers: {
                'Content-Type': 'application/json; charset=utf-8'
                }
            })                
               .success(function(data) {
                    accessToken = data.access_token;
                    $location.path("/secure");
                })
                .error(function(data, status) {
                    alert("ERROR: " + data);
                });

}

I realise that once I get the token , I have to do something similar to

$http.get('http://apiurl/api/v1/users', 
    {headers: { Authorization: ' Token api_key=xxxxxxxxxxxxxxxxxxxxxxxxxxxx'}})
    .then(function(response) {
            service.currentUser = response.data.user;
            console.log(service.currentUser);
    });

But so far I’ve been unable to figure out a way to make a call to the server and save the access token in my localstorage. All resources on the internet are primarily catered towards 3rd party logins (google,facebook,twitter etc ) or JWT tokens.

I am fairly new at this but I’ve found out that I need to worry about password grant flow where the user gives his/her credentials to the consumer and the consumer exchanges these for an access and refresh token. Still I don’t believe I am making the right call.

oauth-ng seemed like a good solution but their documentation from what I’ve seen confuses me as I want to send the username and password to the url too and the sample is not implementing it or has a provision for it from what I can tell?

This is what they have in their documentation :

<oauth
  site="http://oauth-ng-server.herokuapp.com"
  client-id="d6d2b510d18471d2e22aa202216e86c42beac80f9a6ac2da505dcb79c7b2fd99"
  redirect-uri="http://localhost:9000"
  profile-uri="http://oauth-ng-server.herokuapp.com/api/v1/me"
  scope="public">
</oauth>

Again , this is a first time I’m trying integration of any kind and it makes sense for me to think that the call will have credentials sent with it? How do I send it then ?

On the suggestion of building interceptors to handle authentication , I went ahead and built one and for now , I believe everything works good apart from the fact I don’t know how to send my client ID or secret to my post request .

I’m getting a 401 for Full authentication is required to access this resource

here’s my app.js

var app = angular.module('AngularAuthApp', ['ngRoute', 'LocalStorageModule', 'angular-loading-bar']);

app.config(function ($routeProvider) {

    $routeProvider.when("/home", {
        controller: "homeController",
        templateUrl: "/views/home.html"
    });

    $routeProvider.when("/login", {
        controller: "loginController",
        templateUrl: "/views/login.html"
    });


    $routeProvider.otherwise({ redirectTo: "/home" });
});

app.run(['authService', function (authService) {
    authService.fillAuthData();
}]);


app.config(function ($httpProvider) {
    $httpProvider.interceptors.push('authInterceptorService');
});ig(function ($httpProvider) {
        $httpProvider.interceptors.push('authInterceptorService');
    });

Here’s my authService.js

app.factory('authService', ['$http', '$q', 'localStorageService', function ($http, $q, localStorageService) {

    var serviceBase = 'http://url/oauth/';
    var authServiceFactory = {};

    var _authentication = {
        isAuth: false,
        userName : ""
    };

    var _login = function (loginData) {

        var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password ;
        var deferred = $q.defer();

        $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {

            localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });

            _authentication.isAuth = true;
            _authentication.userName = loginData.userName;

            deferred.resolve(response);

        }).error(function (err, status) {
            _logOut();
            deferred.reject(err);
        });

        return deferred.promise;

    };

  authServiceFactory.login = _login;
  return authServiceFactory;

}]);

Here’s my authInterceptorService.js

app.factory('authInterceptorService', ['$q', '$location', 'localStorageService', function ($q, $location, localStorageService) {

    var authInterceptorServiceFactory = {};

    var _request = function (config) {

        config.headers = config.headers || {};

        var authData = localStorageService.get('authorizationData');
        if (authData) {
            config.headers.Authorization = 'Bearer ' + authData.token;
        }

        return config;
    }

    var _responseError = function (rejection) {
        if (rejection.status === 401) {
            $location.path('/login');
        }
        return $q.reject(rejection);
    }

    authInterceptorServiceFactory.request = _request;
    authInterceptorServiceFactory.responseError = _responseError;

    return authInterceptorServiceFactory;
}]);

I am not sure as to where to send my clientID and secret . Sending it part of the data variable doesn’t work and I’m not sure if the scope should be sent too (as per the curl call) .This is where I am stuck at the moment.

1 Like

Hi guy for oauth we have 4 different flow if i remember.

I had the same problem i and didnt found any component for angularjs for password grant flow.
So my advice is to implement it by yourself, if you need a help ask here.

Basically you have to make a get request to oauth api with clientID + client_secret + grant_type=password + username + password.
Example:

$http.get(OAuthBaseUrl + 'client_id=' + OAuthCliendId + '&client_secret=' + OAuthClientSecret + '&grant_type=password&username=' + username.trim() + '&password=' + password.trim());

After that you can handle a success or error callback promise.
In success you can save access token and refresh token to local storage.
On each router change so on each $stateChangeStart event is triggered you should check if current user token exists and is not expired if one of these 2 conditions fails you have to check if you can retrieve new access token based on your refresh token saved before.If refresh token is expired too you must log out user from your app.

I hope i was helpful.

Thanks for the response . There are logic written to handle what to do after one gets the token. There are examples out there too. But shouldn’t the method be a post ? $http.post ? I am wondering, not really experience in that matter. I am guessing because the curl command that I am using is a post one. I will go ahead and try your code too.

oauth will never use get requests to authenticate… because you then are forced to send your password and so on on the url… it is a post requests because on backend side the auth-tokens are generated for you.

in most cases the an accesstoken and a refreshtoken.

your accesstoken is valid for some time. after that you need to refresh your authorization with the refreshtoken. --> you get a new refresh- and accesstoken.

after you have an authorization token you have to send it with your api-calls (which needs authorization).
In general you can use the Authorization http-header.
There are different types of tokens one exaple is the bearer token.
So you set your header like that:

Authorization: TOKENTYPE ACCESSTOKEN

hope that helps.

And the to fullfil the oauth2 requirements your api should be run under https… so your server should have a ssl certificate.

I was under the impression that ng-resource was needed to implement this but apparently not .

so running

$http.post(OAuthBaseUrl + 'client_id=' + OAuthCliendId + '&client_secret=' + OAuthClientSecret + '&grant_type=password&username=' + username.trim() + '&password=' + password.trim());

will work to get the access_token ?

in my opinion if it is a real oauth api you have to send the query-parameters within the request body

$http.post(OAuthBaseUrl, {
  'client_id': OAuthCliendId,
  'client_secret': OAuthClientSecret,
  'grant_type': password,
  'username': username.trim(),
  'password': password.trim()
});

the only problem with .post is the cors issue. I think I can use $http.jsonp for it right ?

nope jsonp can only “fake” get requests

start your chrome with the --disabled-web-security flag

or use chrome plugins like

Hello bengtler ,

this is what I have for now in the Login Controller . I’m still getting unauthorized 401 error. Am I doing everything correct ?

.controller('LoginController', function($scope, $http, $location) {
 
    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
 
    $scope.login = function() {
     
                $http.post(OAuthUrl, {
                  'client_id': clientId,
                  'client_secret': clientSecret,
                  'grant_type': password,
                  'username': username.trim(),
                  'password': password.trim(),
                  
                  withCredentials: true,
                  headers: {
                    'Content-Type': 'application/json; charset=utf-8'
                    }
                })              
                   .success(function(data) {
                         accessToken = data.access_token;
                        $location.path("/secure");
                    })
                    .error(function(data, status) {
                        alert("ERROR: " + data);
                    });
    })
})

maybe your credentials are wrong?

no, I’m hardcoding the credentials for a test user. I crosschecked those things, it’s not wrong.

okay but if it is your api … you should check how you have to set headers and so on.

sry.

Where should I be using the Authorization : Access Token signature ?

you can set this as a header

headers: {
  'Content-Type': 'application/json; charset=utf-8',
  'Authorization': 'XXXXXXX XXXXX'
}
1 Like

Trust me and try with get method.
Set this in your app.js in config function:

$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];

Delete withCredentials option and delete your content type header too.

Some resources:

1 Like

i suggest you to implement it using interceptors and ui router events rather than simply use controllers because interceptors and ui router events let you catch an unauthorized http status or bad request in whole app in case of refresh token is expired.

1 Like

@ebreo I have updated my code and added an interceptor. It is working but let me know if you have answer to my question of how to send my clientID and secret to the server.

@bengtler thanks for the follow up . I have updated my code and added an interceptor. It is working but let me know how to send my clientID and secret to the server.

yeah how i said… it depends how the API-works you are using… if it needs this parameters as query parameter or in the request body… or if the API needs them in the request header.