Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting ng-token-auth to work with devise_token_auth

I have a Rails and Ionic project. The back-end uses the devise_token_auth Gem, and the front end ng-token-auth; these are supposed to work "seamlessly".

I've got everything working as far as registration and sign in, which returns a valid response object. Hoever, any further requests after I use $state.go('app.somepage') result in 401 Unauthorized responses.

I get the sense that I'm not actually storing the token anywhere. Can someone please help?

Here are some snippets:

    .controller('LoginCtrl',['$scope', '$auth', '$state', function($scope, $auth, $state) {
    $scope.loginForm = {}
    $scope.handleLoginBtnClick = function() {
      console.log($scope.loginForm);
      $auth.submitLogin($scope.loginForm)
          .then(function(resp) {
            $state.go('app.feed');
          })
          .catch(function(resp) {
            console.log(resp.errors);
          });
    };

State definition:

    .state('app', {
  url: "/app",
  abstract: true,
  templateUrl: "templates/menu.html",
  controller: 'AppCtrl',
  resolve: {
    auth: function($auth) {
      return $auth.validateUser();
    }
  }

})

Resources:

factory('Post', ['railsResourceFactory', 'apiUrl', function (railsResourceFactory, apiUrl) {
    return railsResourceFactory({
        url: apiUrl + '/posts',
        name: 'post'
    });
}]).

And in PostsCtrl:

  $scope.loadFeed = function() {
    Post.query().then(function (posts) {
      $scope.posts = posts;
    }, function (error) {
      console.log( 'Did not get posts!'); ### THIS FIRES
    }).finally(function() {
      // Stop the ion-refresher from spinning
      $scope.$broadcast('scroll.refreshComplete');
    });
  };

Login response object:

{"data":{"id":1,"provider":"email","uid":"1234","phone":null,"name":"Admin","image":null,"username":"admin"}}

Top of ApplicationController:

class ApplicationController < ActionController::Base
  include DeviseTokenAuth::Concerns::SetUserByToken

  before_filter :add_allow_credentials_headers
  before_filter :cors_preflight_check
  after_filter :cors_set_access_control_headers
  before_action :configure_permitted_parameters, if: :devise_controller?

  ..yadayada...

  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) << :phone
    devise_parameter_sanitizer.for(:sign_up) << :username
    devise_parameter_sanitizer.for(:sign_up) << :session

    devise_parameter_sanitizer.for(:sign_in) << :phone
    devise_parameter_sanitizer.for(:sign_in) << :username
    devise_parameter_sanitizer.for(:sign_in) << :session
  end

And some default models for User on the rails side.

Rails log:

Started GET "/posts" for 192.168.83.26 at 2015-02-24 23:29:02 -0500
Processing by PostsController#index as JSON
  Parameters: {"post"=>{}}
Filter chain halted as :authenticate_user! rendered or redirected
Completed 401 Unauthorized in 1ms (Views: 0.2ms | ActiveRecord: 0.0ms)

If anyone can provide some insight that would be wonderful. I'm happy to post more snippets as needed.

like image 304
mostlydev Avatar asked Feb 25 '15 04:02

mostlydev


4 Answers

As it turns out, the solution was rather simple. It seems that in most of the examples everyone provides, they neglect to permit access-token, along with all the other CORS headers.

We used rack-cors for this, at the bottom of config.ru:

require 'rack/cors'
use Rack::Cors do

  # allow all origins in development
  allow do
    origins '*'
    resource '*',
             :headers => :any,
             :expose  => ['access-token', 'expiry', 'token-type', 'uid', 'client'],
             :methods => [:get, :post, :delete, :put, :options]
  end
end

And then in ApplicationController.rb:

  before_filter :add_allow_credentials_headers
  skip_before_filter :verify_authenticity_token
  before_filter :cors_preflight_check
  after_filter :cors_set_access_control_headers


  def cors_set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
    headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization, Token'
    headers['Access-Control-Max-Age'] = '1728000'
  end

  def cors_preflight_check
    if request.method == 'OPTIONS'
      headers['Access-Control-Allow-Origin'] = '*'
      headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
      headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version, Token'
      headers['Access-Control-Max-Age'] = '1728000'

      render :text => '', :content_type => 'text/plain'
    end
  end

  def add_allow_credentials_headers
    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#section_5
    #
    # Because we want our front-end to send cookies to allow the API to be authenticated
    # (using 'withCredentials' in the XMLHttpRequest), we need to add some headers so
    # the browser will not reject the response
    response.headers['Access-Control-Allow-Origin'] = request.headers['Origin'] || '*'
    response.headers['Access-Control-Allow-Credentials'] = 'true'
  end
like image 97
mostlydev Avatar answered Nov 19 '22 20:11

mostlydev


This information may be relevant to you.

jwako/ionic_rails_sample

like image 30
jwako Avatar answered Nov 19 '22 21:11

jwako


As for my case, i use cookies to store the token. And whenever we do $auth methods in our Angular application, some of the methods will try to go to the devise route that you have defined in your Rails router and match/validate the token that is stored in any of the header's requests. (everytime you try to do http request! checkout your request headers using your browser inspector's if they're containing uid or auth_token if you're going to validate through the GET /validate_token (https://github.com/lynndylanhurley/devise_token_auth#usage-tldr))

Since you didn't mention your route, we can assume /auth.

And those $http request provided by the $auth should be containing token to be authenticated the the Rails's Devise, and catch and store it to the browser's cookies whenever we do $auth.submitLogin().

Here's the example on how it works in my previous project.

app.factory('authInterceptor', ['$q', 'ipCookie', '$location',  function($q, ipCookie, $location) {
  return {
    request: function(config) {
      config.headers = config.headers || {};
      if (ipCookie('access-token')) {
        config.headers['Access-Token'] = ipCookie('access-token');
        config.headers['Client'] = ipCookie('client');
        config.headers['Expiry'] = ipCookie('expiry');
        config.headers['Uid'] = ipCookie('uid');
      }
      return config;
    },
    responseError: function(response) {
      if (response.status === 401) {
        $location.path('/login');
        ipCookie.remove('access-token');
      }
      return $q.reject(response);
    }
  };
}])

And set the token format to be looked like this (or custom as you need it)

$authProvider.configure({
  tokenValidationPath: '/auth/validate_token',
  signOutUrl: '/auth/sign_out',
  confirmationSuccessUrl: window.location.href,
  emailSignInPath: '/auth/sign_in',
  storage: 'cookies',
  tokenFormat: {
    "access-token": "{{ token }}",
    "token-type": "Bearer",
    "client": "{{ clientId }}",
    "expiry": "{{ expiry }}",
    "uid": "{{ uid }}"
  }
});

Don't forget to inject ipCookie (lookup for angular-cookie instead of angular-cookies) to the Interceptor since this is the cookie library that ng-token-auth use for cookies management.

Please comment below with questions if i didn't make myself clear enough. :D

like image 2
Reydi Sutandang Avatar answered Nov 19 '22 22:11

Reydi Sutandang


Maybe is too late,

But the problem is because you can't have the auth on cookies (only Android). So, you can try to use the localStorage to save your session info (on iOS & Android)

e.g

.config(function($authProvider) {
  $authProvider.configure({
    apiUrl: 'http://myprivateapidomain/api',
    storage: 'localStorage'
  });
})

You can read more in the specific issue of the documentation: https://github.com/lynndylanhurley/ng-token-auth/issues/93

like image 1
joseglego Avatar answered Nov 19 '22 21:11

joseglego