Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Q: What is the simple_token_authentication configuration for an api? #67

Open
Rotimi opened this issue Jun 10, 2014 · 15 comments
Open

Q: What is the simple_token_authentication configuration for an api? #67

Rotimi opened this issue Jun 10, 2014 · 15 comments
Labels
duplicate support request This issue is a request for support using Simple Token Authentication.

Comments

@Rotimi
Copy link

Rotimi commented Jun 10, 2014

When using this gem for a json api exclusively what is the best practice configuration?
How are controllers protected since there is no "authenticate_user" method.

My config is below, please correct me.

for this gem:

  1. config.sign_in_token = false

  2. acts_as_token_authentication_handler_for User, fallback_to_devise: false

In application_controller.rb, I assume this is diabling json csrf protection?
3) protect_from_forgery with: :null_session -> csrf protection
4) skip_before_action :verify_authenticity_token, if: :json_request?

In devise gem configs to disable redirects:
5) config.to_prepare do
DeviseController.respond_to :json
end
6) config.navigational_formats = ['/', :html]

My issues:

With the above configuration in an api should I be disabling csrf and devise fall_back? #49 seems to imply that the devise fallback should be disabled for api is this correct?

Also, since the fallback is disabled will I have to handle cases in which current_user is missing?

@gonzalo-bulnes
Copy link
Owner

Hi @Rotimi,

The answer is yes to both questions:

@donbobka
Copy link
Contributor

@Rotimi, IMO the best way of gem usage for api following:

  1. protect_from_forgery with: :null_session - It isn't a disabling of CSRF protection, it's another method of protection:
    :null_session - Provides an empty session during every request
  2. config/initializers/simple_token_authentication.rb without modification
  3. Example:
# /app/controller/api/base_controller.rb

class API::BaseController < ActionController::Base
  protect_from_forgery with: :null_session # CSRF protection for API
end

# /app/controller/api/v1/post_controller.rb

class API::V1::PostController < API::BaseController
  acts_as_token_authentication_handler_for User, only: [:new]

  def index
    # ... Accessible for unauthenticated users ...
  end

  def show
    # ... Accessible for unauthenticated users ...
  end

  def new
    # ... Only for authenticated users...
    # If current_user is missing, devise throw error with status code 401
  end
end

In this way you have a CSRF protection and devise will handle cases when current_user is missing

@gonzalo-bulnes
Copy link
Owner

Note @Rotimi: @donbobka is right, protect_from_forgery with: :null_session doesn't disable CSRF protection, that's why the skip_before_action :verify_authenticity_token, if: :json_request instruction is necessary (Rails request forgery protection documentation).

@gonzalo-bulnes
Copy link
Owner

For reference: #45

@donbobka
Copy link
Contributor

IMO this documentation(Rails request forgery protection documentation) is obsolete. Because without verify_authenticity_token call protect_from_forgery with: :null_session do nothing.

@gonzalo-bulnes
Copy link
Owner

I may be wrong, but I think that's the point. AFAIK the CSRF protection does not make sense for API (I have previously references this post about that), that's why we disable it.

@donbobka
Copy link
Contributor

I made PR to rails(rails/rails#15608) about this obsolete documentation. Let's look what will say rails maintainers.

My quote from this PR, why skip_before_action :verify_authenticity_token should be deleted:

:null_session was discussed in #5326. Quote from there:

null_session is another change we'd discussed, instead of resetting the session it should just assign the cookie and session objects to values which return null for everything and then send no Set-Cookie headers in the response.

Look at method handle_unverified_request

In case you call skip_before_action :verify_authenticity_token, handle_unverified_request method of NullSession class wouldn't be called. It's mean that session variable will be real session and all data populated to session will be stored for subsequent requests. That's means that api will be vulnerable for CSRF attacks.

But if you delete skip_before_action :verify_authenticity_token line. session will be NullSessionHash and all data populated to session will be lost for subsequent requests

@Rotimi
Copy link
Author

Rotimi commented Jun 11, 2014

All the ideas being made @gonzalo-bulnes @donbobka are useful but it seems they highly depend on the use case. For a non-browser client it seems disabling csrf is standard but it turns out that it is not advised when a browser is consuming the json api. See: this and this

So I believe the options for a client agnostic api are:

  1. As @gonzalo-bulnes has mentioned the simplest way would be to allow csrf and the devise fallback. Then send the csrf token with every json request from the browser and mobile client. Extracting the csrf token from rails/devise on something like android/ios requires some work.

  2. Disable csrf protection and disable the devise fallback. Implement access restriction when token auth fails or current_user is missing. And finally disable cookie session storage and devise sending cookies to the clients with config.skip_session_storage. Might be code leading to vulnerabilities.

  3. Follow @donbobka steps that keep csrf and devise fallback while using protect_from_forgery with: :null_session but not using skip_before_action :verify_authenticity_token, if: :json_request. This way rails will destroy a session without a csrf token without raising an exception. See: this

I will probably go with option 3 since it preserves the simple_token_authentication and devise gems especially with their community vetting.

Thanks for the help and guidance!

PS: With this method is there a problem with devise sending session cookies on registration and login or when devise actually comes into play when current_user is missing @donbobka ?

@donbobka
Copy link
Contributor

This way rails will destroy a session without a csrf token without raising an exception.

In this way rails works if you use protect_from_forgery with: :reset_session. As i mentioned before :null_session keeps your session and cookies untouched, at the same time for your api requests (all actions under protect_from_forgery with: :null_session) it provides an empty session (here) and empty cookies (here) and skip session update step (here).

PS: With this method is there a problem with devise sending session cookies on registration and login or when devise actually comes into play when current_user is missing @donbobka ?

Due to :null_session provides to your request empty session, devise have to authorize user with email and token every request. If pair email/token is invalid or absent, current_user would be missing, therefore authenticate_user! method of devise throws a error.

@gonzalo-bulnes
Copy link
Owner

@gertfindel

@BSorbus
Copy link

BSorbus commented Feb 1, 2016

Hi @gonzalo-bulnes

This configuration is correct for for Grape gem?

My application run for http and api mode.
Rails 4.2.4 Devise 3.5.6, simple_token_authentication 1.12.0, grape 0.14.0

config/initializers/simple_token_authentication.rb without modification

My User model:

class User < ActiveRecord::Base
  acts_as_token_authenticatable

  devise  :database_authenticatable, 
          :recoverable, 
          :timeoutable, 
          :registerable, 
          :confirmable, 
            :trackable, 
          :validatable,
          :lockable,
            :password_expirable,
            :secure_validatable, 
            :password_archivable, 
            :expirable,
          :authentication_keys => [:email]

  #gem 'devise_security_extension'
  #devise :password_expirable, 
  #        :secure_validatable, 
  #        :password_archivable, 
  #        :session_limitable, 
  #        :expirable

....
end

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  protect_from_forgery with: :null_session, :if => Proc.new { |c| c.request.format == 'application/json' }

  acts_as_token_authentication_handler_for User, fallback: :none
....
end

and routes.rb

  mount API::Base, at: "/"
end

inside app/controllers/api/v1/certificates.rb

module API  
  module V1
    class Certificates < Grape::API
      include API::V1::Defaults
      #include Devise::Controllers::Helpers

      acts_as_token_authentication_handler_for User

      resource :certificates do
        desc "Return all certificates"
          get "", root: :certificates do
            authenticate_user!
            Certificate.all
          end
        end


      end


    end
  end
end 

..and WEBrick (when run) show error:
/home/xxxxxxx/my_app/app/controllers/api/v1/certificates.rb:8:in `<class:Certificates>': undefined method `acts_as_token_authentication_handler_for' for API::V1::Certificates:Class (NoMethodError)

if uncomment
include Devise::Controllers::Helpers

...this error show to :(

What is wrong?

@gonzalo-bulnes
Copy link
Owner

Hello @BSorbus,

The Grape support is work in progress (and I need help with it from someone familiar with Grape!) The intent is described in the wiki, progress is coordinated from #138 (there are intructions there to use the experimental code), and the current bottleneck is described in #194.

I suggest you start from #194, as it contains links to the most relevant comments in #138. Please let me know in #138 if you experiment difficulties using the experimental branch, and if you are able to suggest how the Simple Token Authentication methods / callbacks should be distributed between Grape::API and Grape::Endpoint so the usage feels convenient to Grape users.

@BSorbus
Copy link

BSorbus commented Feb 1, 2016

Thanks for the information.
I wish ( myself too :) ) the expeditious completion of this adapter :)

@ratneshnavlakhe
Copy link

ratneshnavlakhe commented Jan 5, 2017

The auth_token gets created in the database when we try to create the user, how should we stop this to generate auth_token only at login POST call.

@gonzalo-bulnes
Copy link
Owner

Hello @ratneshnavlakhe,

Can you open a new issue with your question please?
You can just copy your comment in a new issue, give it a title and I'll reply to you there!

Keeping topics separate makes easier for anyone having the same question to find yours.
Thank you!

@gonzalo-bulnes gonzalo-bulnes added the support request This issue is a request for support using Simple Token Authentication. label Jan 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate support request This issue is a request for support using Simple Token Authentication.
Projects
None yet
Development

No branches or pull requests

5 participants