Skip to content

Action Cable and devise token auth #986

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

Closed
jgoodall628 opened this issue Oct 20, 2017 · 12 comments
Closed

Action Cable and devise token auth #986

jgoodall628 opened this issue Oct 20, 2017 · 12 comments

Comments

@jgoodall628
Copy link

Does anyone know how to authenticate action cable connections while using devise token auth? Currently have to authorize connections two different types of users as well customers and employees. I can't really find any info.

@zachfeldman
Copy link
Contributor

How about just checking the validity of the users' token based on their UID?

Perhaps I'm misunderstanding how action cable is different from a controller action?

@jgoodall628
Copy link
Author

As far as I know websockets/action cable do not let you specify headers. So we can't get the info like we can in controllers

@lynndylanhurley
Copy link
Owner

lynndylanhurley commented Oct 20, 2017

@jgoodall628 you will need to include the latest valid token info (uid, token, and client values) as params to the ActionCable initialization request and then manually check that the token is valid.

For example:

# app/channels/application_cable/connection.rb
module ApplicationCable  
  class Connection < ActionCable::Connection::Base    
    identified_by :current_user

    def connect      
      self.current_user = find_verified_user    
    end     

    private      
      def find_verified_user
        uid = params[:uid]
        token = params[:token]
        client_id = params[:client]

        user = User.find_by_uid(uid)

        if user && user.valid_token?(token, client_id)          
          user        
        else          
          reject_unauthorized_connection        
        end      
      end  
  end
end

@juanmanuelramallo
Copy link

juanmanuelramallo commented Jan 7, 2018

Instead of params you should use request.query_parameters and add them into the URL as (i.e) ws://localhost:3000/cable?uid=my_uid&token=my_access_token&client=my_client

@lynndylanhurley
Copy link
Owner

@juanmanuelramallo - thanks, that's exactly what I meant. does params not check the query parameters in websocket requests?

@juanmanuelramallo
Copy link

juanmanuelramallo commented Jan 8, 2018

No, it doesn't 😕

@wmlutz
Copy link

wmlutz commented Jan 12, 2018

Worth noting that you need to figure out how to update the token on your client (which for me is React Native) as the cable doesn't have headers to send you the next token/expiry.

@juanmanuelramallo
Copy link

@wmlutz - You have to expose the headers by doing the following in application.rb(if you use Rack::Cors)

config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource '*',
      headers: :any,
      methods: [:get, :options, :post, :delete],
      expose: ['access-token', 'expiry', 'token-type', 'uid', 'client'] # <-- this is the important part
  end
end

Then, after you make any kind of request you should store the new access-token.

Anyways, for ActionCable, this information is only needed at the connection, I mean when you call the createConsumer function:

ActionCable.createConsumer(`${env.REACT_APP_WS_URL}/?access-token=${accessToken}&client=${client}&uid=${uid}`);

@wmlutz
Copy link

wmlutz commented Jan 12, 2018

@juanmanuelramallo Hmmmm. I'm not using Rack::Cors, but I may have to now. Thanks for the help. For reasons I haven't yet figured out, my tokens seem to loose validity randomly during the app's usage. AND I can't recreate the problem reliably so I'm having problems hunting this one down. My solution is probably going to be to set the next valid token every time a message is sent over actioncable.

@MaicolBen
Copy link
Collaborator

You shouldn't update manually the tokens during the socket connection, and in the case of react native, you have to send the id of the user through the headers

@jgoodall628 Can we close the issue?

@lorenzsell
Copy link

@wmlutz I am having the same issue with my react native app, with my tokens randomly loosing validity. I also can't seem to reliably replicate and track down the issue. Did you ever figure it out?

@Herz3h
Copy link

Herz3h commented Nov 6, 2019

Here is how we use devise_token_auth with the application:

module ApplicationCable
    class Connection < ActionCable::Connection::Base
        include ActionController::HttpAuthentication::Basic::ControllerMethods
        include ActionController::HttpAuthentication::Token::ControllerMethods
        identified_by :current_user

        def connect
            client = request.headers["client"]
            uid = request.headers["uid"]
            access_token = request.headers["access-token"]

            self.current_user = find_verified_user access_token, uid, client
        end

        private
        def find_verified_user token, uid, client_id # this checks whether a user is authenticated with devise
            user = User.find_by email: uid
            # http://www.rubydoc.info/gems/devise_token_auth/0.1.38/DeviseTokenAuth%2FConcerns%2FUser:valid_token%3F
            if user && user.valid_token?(token, client_id)
                user
            else
                reject_unauthorized_connection
            end
        end   
    end
end

We simply send the client, uid, access-token from the client app and check it for actioncable on the server side without updating the token. But we experience same issue as @wmlutz and @lorenzsell which is tokens randomly loosing validity.

Any idea ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants