module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
#puts params[:auth_token]
self
Unfortunately for websocket connections, additional headers and custom ones are not supported1 by most2 websocket clients and servers. So the possible options are:
Attach as an URL parameter and parse it on the server
path.to.api/cable?token=1234
# and parse it like
request.params[:token]
Cons: It could be vulnerable as it may end up in logs and system process information available to others that have access to the server, more here
Solution: Encrypt the token and attach it, so even if it can be seen in the logs, it would serve no purpose until its decrypted.
Client side:
# Append jwt to protocols
new WebSocket(url, existing_protocols.concat(jwt))
I created a JS library action-cable-react-jwt for React
and React-Native
that just does this. Feel free to use it.
Server side:
# get the user by
# self.current_user = find_verified_user
def find_verified_user
begin
header_array = self.request.headers[:HTTP_SEC_WEBSOCKET_PROTOCOL].split(',')
token = header_array[header_array.length-1]
decoded_token = JWT.decode token, Rails.application.secrets.secret_key_base, true, { :algorithm => 'HS256' }
if (current_user = User.find((decoded_token[0])['sub']))
current_user
else
reject_unauthorized_connection
end
rescue
reject_unauthorized_connection
end
end
1 Most Websocket APIs (including Mozilla's) are just like the one below:
The WebSocket constructor accepts one required and one optional parameter:
WebSocket WebSocket( in DOMString url, in optional DOMString protocols ); WebSocket WebSocket( in DOMString url, in optional DOMString[] protocols );
url
The URL to which to connect; this should be the URL to which the WebSocket server will respond.
protocols
OptionalEither a single protocol string or an array of protocol strings. These strings are used to indicate sub-protocols, so that a single server can implement multiple WebSocket sub-protocols (for example, you might want one server to be able to handle different types of interactions depending on the specified protocol). If you don't specify a protocol string, an empty string is assumed.
2 There are always excpetions, for instance, this node.js lib ws allows building custom headers, so you can use the usual Authorization: Bearer token
header, and parse it on the server but both client and server should use ws
.
As I already stated in a comment the accepted answer is not a good idea, simply because the convention is that the URL should not contain such sensitive data. You can find more information here: https://tools.ietf.org/html/rfc6750#section-5.3 (though this is specifically about OAuth).
There is however another approach: Use HTTP basic auth via the ws url. I found that most websocket clients allow you to implicitly set the headers by prepending the url with http basic auth like this: wss://user:pass@yourdomain.com/cable
.
This will add the Authorization
header with a value of Basic ...
. In my case I was using devise with devise-jwt and simply implemented a strategy which inherited from the one provided in the gem which pulls the jwt out of the Authorization
header. So I set the url like this: wss://TOKEN@host.com/cable
which sets the header to this (pseudo): Basic base64("token:")
and parse that in the strategy.
In case any of you would like to use ActionCable.createCustomer. But have renewable token as I do:
const consumer = ActionCable.createConsumer("/cable")
const consumer_url = consumer.url
Object.defineProperty(
consumer,
'url',
{
get: function() {
const token = localStorage.getItem('auth-token')
const email = localStorage.getItem('auth-email')
return consumer_url+"?email="+email+"&token="+token
}
});
return consumer;
Then in case that the connection is lost it will be opened with a fresh new token.
to add to previous answers, if you used your JWT as a param, you're going to have to at least btoa(your_token)
@js and Base64.decode64(request.params[:token])
@rails as rails considers dot '.' a separator so your token will be cut off @rails params side