I am new to Rails and webdev. Trying to implement a simple API for a mobile application with Rails + Devise + Doorkeeper (e.g. https://github.com/doorkeeper-gem/doorkeeper-provider-app ).
Faced a problem that the user cannot fulfill the authorization request (POST / oauth / token) if he has already received the token. I.e:.
curl -F grant_type=password -F username=1@tothetrip.com -F password=12345678 -X POST http:
First time:
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjp7ImlkIjoyLCJlbWFpbCI6IjFAdG90aGV0cmlwLmNvbSJ9fQ.dYai6nH_KYb9YbDltqwFuzCO3i0igR_gw2T7u_TeVcI","token_type":"bearer","expires_in":7200,"created_at":1435864812}
The token goes into the oauth_access_tokens table (which is not necessary for JWT, but not a problem).
If I repeat this request, I get a 422 page of errors and rails with something like
ActiveRecord::RecordInvalid in Doorkeeper::TokensController#create Validation failed: Token has already been taken activerecord (4.2.3) lib/active_record/validations.rb:79:in `raise_record_invalid' activerecord (4.2.3) lib/active_record/validations.rb:43:in `save!' activerecord (4.2.3) lib/active_record/attribute_methods/dirty.rb:29:in `save!' activerecord (4.2.3) lib/active_record/transactions.rb:291:in `block in save!' activerecord (4.2.3) lib/active_record/transactions.rb:351:in `block in with_transaction_returning_status' activerecord (4.2.3) lib/active_record/connection_adapters/abstract/database_statements.rb:213:in `block in transaction' activerecord (4.2.3) lib/active_record/connection_adapters/abstract/transaction.rb:184:in `within_new_transaction' activerecord (4.2.3) lib/active_record/connection_adapters/abstract/database_statements.rb:213:in `transaction' activerecord (4.2.3) lib/active_record/transactions.rb:220:in `transaction' activerecord (4.2.3) lib/active_record/transactions.rb:348:in `with_transaction_returning_status' activerecord (4.2.3) lib/active_record/transactions.rb:291:in `save!' activerecord (4.2.3) lib/active_record/persistence.rb:51:in `create!' doorkeeper (2.2.1) lib/doorkeeper/models/access_token_mixin.rb:76:in `find_or_create_for' doorkeeper (2.2.1) lib/doorkeeper/oauth/request_concern.rb:33:in `find_or_create_access_token' doorkeeper (2.2.1) lib/doorkeeper/oauth/password_access_token_request.rb:30:in `before_successful_response' doorkeeper (2.2.1) lib/doorkeeper/oauth/request_concern.rb:7:in `authorize' doorkeeper (2.2.1) lib/doorkeeper/request/password.rb:19:in `authorize' doorkeeper (2.2.1) app/controllers/doorkeeper/tokens_controller.rb:42:in `authorize_response' doorkeeper (2.2.1) app/controllers/doorkeeper/tokens_controller.rb:4:in `create'
Even if I cancel the token with POST / oauth / revoke, everything will be the same, except for canceling the timestamp in oauth_access_tokens. And this is very strange.
I will investigate it a bit and find the code fragment in the gatekeeper jewel (access_token_mixin.rb):
def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token) if Doorkeeper.configuration.reuse_access_token access_token = matching_token_for(application, resource_owner_id, scopes) if access_token && !access_token.expired? return access_token end end create!( application_id: application.try(:id), resource_owner_id: resource_owner_id, scopes: scopes.to_s, expires_in: expires_in, use_refresh_token: use_refresh_token ) end
So, the error in the creation! a method that says that we tried to add a duplicate (in stacktrace). And if I set reuse_access_token in Doorkeeper.configure, then this is normal. But I get the same token after each permission, which is very unsafe, as I understand it. And yes, if I manually remove the token from oauth_access_tokens, then I can auth.
So what's wrong?
My Doorkeeper Configuration:
Doorkeeper.configure do
Routes
require 'api_constraints' Rails.application.routes.draw do use_doorkeeper devise_for :users namespace :api, defaults: {format: :json}, constraints: { subdomain: 'api' }, path: '/' do scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do resources :users, :only => [:show, :create, :update] get '/me' => "credentials#me" end end end