neroniaky / angular-token

:key: Token based authentication service for Angular with interceptor and multi-user support. Works best with devise token auth for Rails. Example:
https://stackblitz.com/github/neroniaky/angular-token
MIT License
370 stars 190 forks source link

Reloading the app causes 401 Unauthorised on first api call after token is validated #481

Open rmcsharry opened 5 years ago

rmcsharry commented 5 years ago

I'm submitting a...

Current behavior

If an existing and logged in user:

  1. the user reloads the app using browser refresh,
  2. then the token is validated correctly,
  3. but the next call to the API fails as 401 Unauthorized.
  4. all following calls succeed

Expected behavior

First request after reloading the app (ie. browser refresh) should not result in 401.

Environment

Angular-Token version: 6.0.3 & 6.0.4 Angular version: 6.1.7 Rails 5.1.6 devise_token_auth 0.2.0 Bundler

Browser:

Other After the validate_token is called and a new token is sent back by the API, that token is written to localstorage. But the next api call (to get data) does not use it, it is still using the previous token.

The Rails api throws the error "Filter chain halted as authenticate_user! redirected".

The next api request succeeds.

What is also strange is that if I add this test method to see what happens in the Rails application_controller, I get the output shown by "->":

  before_action :test
  respond_to :json
  include Devise::Controllers::Helpers
  include DeviseTokenAuth::Concerns::SetUserByToken

  def test
    puts '****'
    puts request.env["HTTP_ACCESS_TOKEN"] -> shows the token (ie the old one)
    puts request.env["access-token"] -> shows nothing
    authenticate_user!
  end
rmcsharry commented 5 years ago

One (awful) workaround I tried is that on the component that is reloaded I added a timer to delay calling the service that gets the data for that component.

I tested this and it works, but obviously it is impractical to add this to 50+ components.

It seems the data request to the API is being intercepted to add the headers, but this is happening before the token is updated in localstorage.

Since previous versions did not have interceptors (the api calls were wrapped directly by angular2-token), so you were always 100% certain of injecting the latest token. Not anymore it seems :(

rmcsharry commented 5 years ago

Possible solution:

I think that before line 20 of the interceptor we need to check if validate_token call has finished and only get token from storage after it has completed.

I am not entirely sure how to achieve that - somehow the interceptor needs to only allow a request if there are no current validate_token calls in-flight.

rmcsharry commented 5 years ago

It seems this is a common problem, some suggested solutions are here.

I'd like to update the library to fix this bug...but not sure of time/skill required, I might have a go next week.

In the meantime, the quickest/easiest workaround without updating the library is to add retry(1) - or more than 1 retry, up to you - to every http call eg:

    return this.http.get(this.proposalsUrl, {params: params}).pipe(
      map((response: HttpResponse<TYPE>) => response), retry(2),
      catchError((response: HttpErrorResponse) => this.apiError.handleError(response))
    );
rmcsharry commented 5 years ago

The retry only works sometimes πŸ‘Ž

It causes some requests to get cancelled, which then causes token invalidation problem which again results in a 401 unauthorized. So this issue is basically related to this one.

So now I'm not sure if the fix should be done in this library or the devise_token_auth gem :(

SimonBrazell commented 3 years ago

@rmcsharry did you ever find a solution to this? I believe I'm experiencing it now myself, might have to find another solution...

crazybob9 commented 3 years ago

I have the same issue, has anyone found a way to fix it?

rmcsharry commented 3 years ago

@SimonBrazell No, I never found a solution, my app still does this πŸ‘Ž

SimonBrazell commented 3 years ago

All good @rmcsharry I found out I was actually encountering a different issue of my own creation.

I was setting apiBase programatically based on a value stored in local storage which wasn't available before the first API call, so the API was correctly responding with a 401 as the token wasn't present in the default tenant (multi-tenant app) forcing sign out and the user to login again.

Once I fixed this all seems to be working fine, although perhaps I'm just yet to encounter your issue...

ldsk-trifork commented 10 months ago

Bump.