lynndylanhurley / lynndylanhurley/devise_token_auth
BUG: config.enable_standard_devise_support = true returns wrong current_user
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 3.6k
- Forks
- 1.1k
- PR merge metrics
- No merged PRs in 30d
Description
- devise_token_auth **[0.1.42]**
- Ruby **[2.4.2]**
- Rails **[5.1.4]**
- relevant Gemfile.lock
```
devise (4.3.0)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.2)
responders
warden (~> 1.2.3)
devise-i18n (1.5.0)
devise (>= 3.4)
devise_token_auth (0.1.42)
devise (> 3.5.2, <= 4.3)
rails (< 6)
```
* **Request and response headers**:
Described below.
* **Rails Stacktrace**: this can be found in the `log/development.log` of your API.
* **Environmental Info**: My app uses both `devise` and `devise_token_auth`, which I achieve by subclassing ApplicationController into `ApiController` and `ApplicationController`. One inheriting from `ApplicationController::Base` the other `ApplicationController::API`
* **Routes**: I'm using both `devise` and `devise_token_auth`
```
namespace :api do |api|
mount_devise_token_auth_for 'User', at: 'auth'
end
as :user do
# post 'users/password/change', to: 'users/passwords#change', as: 'change_user_password'
patch 'users/password/change', to: 'users/passwords#change', as: 'change_user_password'
get 'users/password/check-email', to: 'users/passwords#check_email', as: 'check_email_user_password'
get 'users/password/reset-success', to: 'users/passwords#reset_success', as: 'reset_success_user_password'
end
devise_for :users, skip: :registrations, controllers: {
confirmations: 'users/confirmations',
passwords: 'users/passwords',
# omniauth_callbacks: 'web/users/omniauth_callbacks',
# registrations: 'users/registrations',
sessions: 'users/sessions',
}
```
* **Gems**: Using devise web. Also using `paperTrail` gem, which writes a copy of the Users table over to UserVersion.
* **Custom Overrides**: I'm (trying) to namespace both devise and devise_token_auth into separate controllers. But I have run into problems completely doing this (explained below).
* **Custom Frontend**: iPhone & Android native clients, Postman for testing
## Bug setup
In my app, my API consumers can switch between child and parent users, with the Bearer token `uid / client / access-token` being the mechanism for how my API consumers can switch between users.
However they were complaining about getting back incorrect data, with data from the wrong user getting sent back to them. After investigating I found their concerns to be true, with `devise_token_auth` returning back incorrect users based on the headers it is sent.
To replicate. I created two users with the same password:
user 1: `dojouser17+marge@gmail.com`
user 2: `dojouser17+bart@gmail.com`
First, log in with user 1:
```
GET api/auth/sign_in
{
"email": "dojouser17+bart@gmail.com", "password": "That1234"
}
```
receiving correctly back headers corresponding to uid = `dojouser17+bart@gmail.com` / user 1:
```
access-token →z9fS0CuMUZk9_F1FpRY5Tw
cache-control →max-age=0, private, must-revalidate
client →AfYoRq09GaCmATnzAqjAgQ
content-type →application/json; charset=utf-8
etag →W/"1edac4bb16fda94888d87a447cbc0834"
expiry →1516659606
token-type →Bearer
transfer-encoding →chunked
uid →dojouser17+bart@gmail.com
x-content-type-options →nosniff
x-frame-options →SAMEORIGIN
x-request-id →3b1f7b69-03aa-4d54-9911-0aca229d195a
x-runtime →0.217444
x-xss-protection →1; mode=block
```
Next, log in with user 2:
```
GET api/auth/sign_in
{
"email": "dojouser17+marge@gmail.com", "password": "That1234"
}
```
receiving correctly back headers corresponding to uid = `dojouser17+marge@gmail.com` / user 2:
```
access-token →4jktXiC-jDG-yMRUboUL2A
cache-control →max-age=0, private, must-revalidate
client →GV1hagOF6CRWIZkvzBHtrA
content-type →application/json; charset=utf-8
etag →W/"aae490494e2e72440f4f0c8273f3dfaf"
expiry →1516660022
token-type →Bearer
transfer-encoding →chunked
uid →dojouser17+marge@gmail.com
x-content-type-options →nosniff
x-frame-options →SAMEORIGIN
x-request-id →ee93f722-3a71-4f9b-bfdf-2408e1d9cec1
x-runtime →0.223127
x-xss-protection →1; mode=block
```
## Bug
Now here's where the bug happens. When I copy the headers for these two separate users into two separate requests and hit an app endpoint requiring authentication. When `config.enable_standard_devise_support` is `true` the api returns back the incorrect current_user (confirmed with byebug), always defaulting to the first user that hit the endpoint (user 1), instead of user 2.
As proof, attached is a screenshot where you can see `dojouser17+marge@gmail.com` (user 2) sending a token request and getting a response back with authentication headers meant for `dojouser17+bart@gmail.com` (user 1):

Restarting the server had no effect on the Postman requests.
## Fix for now
When I changed `config.enable_standard_devise_support = false` the problem went away. Consequently I strongly suspect there is an issue with `DeviseTokenAuth.enable_standard_devise_support`, such as in the way it gets invoked within `set_user_by_token`:
```ruby
def set_user_by_token(mapping=nil)
# determine target authentication class
rc = resource_class(mapping)
# no default user defined
return unless rc
# gets the headers names, which was set in the initialize file
uid_name = DeviseTokenAuth.headers_names[:'uid']
access_token_name = DeviseTokenAuth.headers_names[:'access-token']
client_name = DeviseTokenAuth.headers_names[:'client']
# parse header for values necessary for authentication
uid = request.headers[uid_name] || params[uid_name]
@token ||= request.headers[access_token_name] || params[access_token_name]
@client_id ||= request.headers[client_name] || params[client_name]
# client_id isn't required, set to 'default' if absent
@client_id ||= 'default'
# check for an existing user, authenticated via warden/devise, if enabled
if DeviseTokenAuth.enable_standard_devise_support
devise_warden_user = warden.user(rc.to_s.underscore.to_sym)
if devise_warden_user && devise_warden_user.tokens[@client_id].nil?
@used_auth_by_token = false
@resource = devise_warden_user
@resource.create_new_auth_token
end
end
...
```
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at set_user_by_token, especially the enable_standard_devise_support branch, and reproduce the issue with the two users and separate uid, client, and access-token headers described above. Done means requests authenticated with each token consistently return the corresponding current_user while standard Devise support remains enabled.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rails, ruby
- Domain
- api, authentication
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100