2

I'm working on an Angular app with a Rails API. I'm using Devise Token Auth and the 6.x branch of angular-token. I can CRUD users with no problem, and I can sign in, but when I try to access a restricted resource (one restricted through the before_action :authenticate_user! helper), I get an error regardless of whether I'm signed in or not.

Relevant controller:

class QuestionsController < ApplicationController
    before_action :authenticate_user!

    # GET /questions.json
    def index
      @questions = Question.all

      # This code is never reached due to the :authenticate_user! helper, 
      # but when I comment it out, I get the following:
      puts user_signed_in?  # => false
      puts current_user     # => nil

      render json: @questions
    end

A few things:

  • I'm definitely signed in according to angular-token -- TokenService.UserSignedIn() returns true at the time of the 401 Unauthorized response.
  • I've enabled CORS.
  • I've exposed the following headers in the rack-cors config: ['access-token', 'expiry', 'token-type', 'uid', 'client'].

Any help would be greatly appreciated.

Example response from signin:

{
    "data": {
        "id": 1,
        "email": "me@example.com",
        "provider": "email",
        "uid": "me@example.com",
        "allow_password_change": false,
        "name": null,
        "nickname": null,
        "image": null
    }
}

Angular code:

this.authService.loginUser({
  login: '',
  password: ''
})
.subscribe(resp => {
    if (resp.status === 200) {
      this.router.navigate(['/']);
    }
  },
  err => {
    // cut for brevity
});

Edit:

I've figured out the problem is with angular-token not setting the auth headers due to what I believe to be a bug in the following code:

// Add the headers if the request is going to the configured server
if (this.tokenService.currentAuthData && req.url.match(this.tokenService.apiPath)) {
  (<any>Object).assign(baseHeaders, {
    'access-token': this.tokenService.currentAuthData.accessToken,
    'client':       this.tokenService.currentAuthData.client,
    'expiry':       this.tokenService.currentAuthData.expiry,
    'token-type':   this.tokenService.currentAuthData.tokenType,
    'uid':          this.tokenService.currentAuthData.uid
  });
}

Since I don't have apiPath defined, req.url.match(this.tokenService.apiPath)) is evaluated to false and thus, the headers are never added.

Niek
  • 1,464
  • 1
  • 17
  • 27
  • can you show us an example using postman ? an example of sign in to see what rails server will return. And your angular code when you call your API. – El Fadel Anas Jul 02 '18 at 11:22
  • I've added examples. Note that the sign in itself works fine, it's the requests to the resources where the issue lies. – Niek Jul 02 '18 at 11:39
  • i need to see the header of your response. The header of your response should contain the next parameters : access-token - uid - client - expiry. This params should be stored and after that added in every http request you make. have you already did that ? – El Fadel Anas Jul 02 '18 at 11:45
  • Yes, the response headers include all those headers. The storing of the token is what `angular-token` is for. – Niek Jul 02 '18 at 15:14
  • No even with angular-token you need to set header of your request, if you didn't set it i think that is the mistake. – El Fadel Anas Jul 02 '18 at 15:21

1 Answers1

3

I don't know about angular-token, but I had the same issue with my React/Redux based app - turns out that it was indeed simply a problem with the headers missing or not being properly set.

For some reason it seems like DeviseTokenAuth:: based controllers can only work by setting client and access-token, but for all other resources protected by DeviseTokenAuth::Concerns::SetUserByToken, you have to set all headers exactly as expected and documented by devise_token_auth.

  • Check headers values and formats.
  • Check that headers are exactly equal to the one returned by the last request (the authentication request in your case).
  • Also, check your mapping

In config/initializers/devise_token_auth.rb :

config.headers_names = {:'access-token' => 'access-token',
                     :'client' => 'client',
                     :'expiry' => 'expiry',
                     :'uid' => 'id',
                     :'token-type' => 'token-type' }

(it defines the name of the expected http headers (not the models/activerecords attributes)).

nakwa
  • 1,157
  • 1
  • 13
  • 25