Получение ng-token-auth для работы с devise_token_auth

У меня есть проект Rails и Ionic. Серверная часть использует Gem devise_token_auth, а внешняя часть — ng-token-auth; они должны работать «бесшовно».

У меня все работает, вплоть до регистрации и входа в систему, который возвращает действительный объект ответа. Тем не менее, любые дальнейшие запросы после использования $state.go('app.somepage') приводят к 401 неавторизованному ответу.

У меня такое ощущение, что я на самом деле нигде не храню токен. Может кто-нибудь помочь?

Вот несколько фрагментов:

    .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('app', {
  url: "/app",
  abstract: true,
  templateUrl: "templates/menu.html",
  controller: 'AppCtrl',
  resolve: {
    auth: function($auth) {
      return $auth.validateUser();
    }
  }

})

Ресурсы:

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

И в 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');
    });
  };

Объект ответа на вход:

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

Верхняя часть 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

И некоторые модели по умолчанию для пользователя на стороне рельсов.

Журнал рельсов:

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)

Если кто-то может дать некоторое представление, это было бы замечательно. Я рад опубликовать больше фрагментов по мере необходимости.


person gamut    schedule 25.02.2015    source источник
comment
Я еще не прочитал весь ваш вопрос, но я написал целый пост об аутентификации Rails/Angular с библиотеками Линн Дилан Херли. airpair.com/ruby- на рельсах/сообщениях/   -  person Jason Swett    schedule 10.03.2015
comment
Кроме того, я не знаю ответа на ваш вопрос навскидку, но что бы я сделал в вашей ситуации, это просто запустить новый пустой проект и следовать моему руководству и посмотреть, работает ли он. Затем, если учебник работает, посмотрите, чем ваш проект отличается от учебника.   -  person Jason Swett    schedule 10.03.2015
comment
Как у вас дела с этим проектом? Я делаю что-то очень похожее и у меня проблемы, у вас есть открытый исходный код?   -  person Phil Brockwell    schedule 21.05.2015
comment
Нет, но если вы прочитаете мой выбранный ответ ниже, это должно дать вам подсказку. Мне не разрешено открывать исходный код. :(   -  person gamut    schedule 22.05.2015


Ответы (7)


Как оказалось, решение было довольно простым. Кажется, что в большинстве примеров, которые все предоставляют, они не разрешают access-token вместе со всеми другими заголовками CORS.

Для этого мы использовали стойки-коры, внизу 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

А потом в 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
person gamut    schedule 18.04.2015
comment
Не уверен, почему это не получило голосов или что это не описано в базовой документации. Это было необходимо для меня, и я не думаю, что мы делаем что-то необычное. - person commandantk; 11.05.2015
comment
Согласованный. Я не знаю, как это может работать без этого. Хотя мне нужен был только блок allow, ни один из элементов ApplicationController. Кроме того, блок разрешения может находиться в вашем application.rb вместо config.ru, если вы используете Rails. - person smoyth; 21.05.2015
comment
Спасибо. Я вижу, что это проблема многих людей. Я выбрал свой собственный ответ, так как это действительно было решением, и я думаю, что ваш последний комментарий уместен. Мы как бы использовали подход отбойного молотка. Спасибо за подсказку по коду в config.ru. - person gamut; 22.05.2015
comment
Привет, ребята, у меня точно такая же проблема (ionic, нет заголовка токена доступа), и эти решения не помогли. Любые идеи будут высоко оценены! - person Uri Klar; 30.05.2015

Эта информация может быть актуальна для вас.

jwako/ionic_rails_sample

person jwako    schedule 18.03.2015

Что касается моего случая, я использую файлы cookie для хранения токена. И всякий раз, когда мы используем $auth методы в нашем приложении Angular, некоторые из методов будут пытаться перейти к маршруту разработки, который вы определили в своем маршрутизаторе Rails, и сопоставлять/проверять токен, который хранится в любом из запросов заголовка. (каждый раз, когда вы пытаетесь выполнить http-запрос! Проверяйте заголовки запросов с помощью инспектора браузера, если они содержат uid или auth_token, если вы собираетесь проверять через GET /validate_token (https://github.com/lynndylanhurley/devise_token_auth#usage-tldr))

Поскольку вы не упомянули свой маршрут, мы можем предположить, что /auth.

И эти $http запросы, предоставляемые $auth, должны содержать токен для аутентификации в Rails Devise, а также перехватывать и сохранять его в файлах cookie браузера всякий раз, когда мы делаем $auth.submitLogin().

Вот пример того, как это работает в моем предыдущем проекте.

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);
    }
  };
}])

И установите формат токена, чтобы он выглядел так (или настраиваемый, как вам нужно)

$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 }}"
  }
});

Не забудьте ввести ipCookie (поиск angular-cookie вместо angular-cookies) в Interceptor, так как это библиотека файлов cookie, которую ng-token-auth использует для управления файлами cookie.

Пожалуйста, прокомментируйте ниже вопросы, если я недостаточно ясно выразился. :D

person Reydi Sutandang    schedule 10.03.2015
comment
Спасибо, так получилось. Кроме того, мы используем локальное хранилище для хранения токена, что не менее эффективно. - person gamut; 11.03.2015
comment
Я могу убедиться, что это решение работает. Я также использую локальное хранилище. - person Jason Swett; 06.09.2016

Может быть, слишком поздно,

Но проблема в том, что вы не можете авторизоваться на куки (только Android). Итак, вы можете попробовать использовать localStorage для сохранения информации о сеансе (на iOS и Android).

e.g

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

Вы можете прочитать больше в конкретном выпуске документации: https://github.com/lynndylanhurley/ng-token-auth/issues/93

person joseglego    schedule 20.05.2015
comment
Мы сделали это изначально, но это не решило проблему. Это просто позволило нам сохранить некоторую информацию о сеансе и притвориться, что аутентификация имела место. Так или иначе, вся идея токенов как раз и заключается в использовании данных сеанса. Это другое решение другой проблемы. - person gamut; 22.05.2015

немного поздно, но для тех, кто может попробовать использовать ng-token-auth в приложении Ionic, я сделал, чтобы заставить его работать (в моем случае), чтобы установить следующую конфигурацию для моего модуля:

app.config(['$httpProvider', function($httpProvider) {  
    $httpProvider.defaults.withCredentials = true;

  }]);

(Я не отправлял файлы cookie в своем http-запросе)

person Ezequiel Ramiro    schedule 17.04.2017

Вы пытались добавить конфигурацию для $authProvider. Этот пример находится в файле readme на https://github.com/lynndylanhurley/devise_token_auth.

angular.module('myApp', ['ng-token-auth'])
  .config(function($authProvider) {
    $authProvider.configure({
      apiUrl: 'http://api.example.com'
      authProviderPaths: {
        github: '/auth/github' // <-- note that this is different than what was set with github
      }
    });
  });
person Daniel Garcia    schedule 26.02.2015
comment
Да, спасибо, аутентификация сначала работает, но потом не переводится в последующие запросы страниц. - person gamut; 04.03.2015

Я подозреваю, что пользователь, под которым вы пытаетесь войти, недействителен. Когда это происходит, заголовок авторизации пуст, и в ответе не отправляется токен доступа.

Это происходит здесь: https://github.com/lynndylanhurley/devise_token_auth/blob/0d4de71/app/controllers/devise_token_auth/concerns/set_user_by_token.rb#L53

person Daniel Cadenas    schedule 19.03.2015