module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
#puts params[:auth_token]
self.current_user = find_verified_user
logger.add_tags 'ActionCable', current_user.name
end
end
end
Je n'utilise pas Web comme point de terminaison pour le câble d'action. Je souhaite donc utiliser auth_token pour l'authentification. Par défaut, le câble d'action utilise l'ID utilisateur de la session pour l'authentification. Comment passer les paramètres à la méthode de connexion?
J'ai réussi à envoyer mon jeton d'authentification en tant que paramètre de requête.
Lors de la création de mon consommateur dans mon application javascript, je passe le jeton à l'URL du serveur de câble comme suit:
wss://myapp.com/cable?token=1234
Dans ma connexion par câble, je peux obtenir cette token
en accédant au request.params
:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
logger.add_tags 'ActionCable', current_user.name
end
protected:
def find_verified_user
if current_user = User.find_by(token: request.params[:token])
current_user
else
reject_unauthorized_connection
end
end
end
end
Ce n'est clairement pas idéal, mais je ne pense pas que vous puissiez envoyer des en-têtes personnalisés lors de la création de la prise Web.
La réponse de Pierre fonctionne. Cependant, il est judicieux d’être explicite quant à l’attente de ces paramètres dans votre application.
Par exemple, dans l’un de vos fichiers de configuration (par exemple, application.rb
, development.rb
, etc.), vous pouvez procéder comme suit:
config.action_cable.mount_path = '/cable/:token'
Et puis simplement y accéder depuis votre classe Connection
avec:
request.params[:token]
Malheureusement pour les connexions websocket, les en-têtes supplémentaires et personnalisés ne sont pas pris en charge.1 par la plupart2 clients et serveurs websocket. Les options possibles sont donc:
Attacher en tant que paramètre d'URL et l'analyser sur le serveur
path.to.api/cable?token=1234
# and parse it like
request.params[:token]
Cons : Il peut être vulnérable car il peut se retrouver dans les journaux et les informations de processus système disponibles pour les autres utilisateurs ayant accès au serveur, plus ici
Solution : Cryptez le jeton et attachez-le. Ainsi, même s'il est visible dans les journaux, il ne servirait à rien jusqu'à ce qu'il soit déchiffré.
Côté client:
# Append jwt to protocols
new WebSocket(url, existing_protocols.concat(jwt))
J'ai créé une bibliothèque JS action-cable-react-jwt pour React
et React-Native
that ne fait que cela. Sentez-vous libre de l'utiliser.
Du côté serveur:
# 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 La plupart des API Websocket (y compris de Mozilla ) ressemblent à celle ci-dessous:
Le constructeur WebSocket accepte un paramètre obligatoire et un paramètre facultatif :
WebSocket WebSocket( in DOMString url, in optional DOMString protocols ); WebSocket WebSocket( in DOMString url, in optional DOMString[] protocols );
url
L'URL à laquelle se connecter; il devrait s'agir de l'URL à laquelle le serveur WebSocket répondra.
protocols
FacultatifSoit une chaîne de protocole unique, soit un tableau de chaînes de protocole. Ces chaînes Sont utilisées pour indiquer les sous-protocoles, de sorte qu'un serveur Puisse implémenter plusieurs sous-protocoles WebSocket (par exemple, vous pouvez Vouloir qu'un serveur puisse: gérer différents types d’interactions en fonction du protocole spécifié). Si vous ne spécifiez pas de chaîne de protocole , Une chaîne vide est utilisée.
2 Il y a toujours des exceptions, par exemple, ce noeud.js lib ws permet de créer des en-têtes personnalisés. Vous pouvez donc utiliser l'en-tête Authorization: Bearer token
habituel et l'analyser sur le serveur, mais le client et le serveur doivent utiliser ws
.
pour ajouter aux réponses précédentes, si vous utilisiez votre JWT en tant que paramètre, vous devrez au moins btoa(your_token)
@js et Base64.decode64(request.params[:token])
@ @Rails comme Rails considère le point '.' un séparateur pour que votre jeton soit coupé @Rails params side
Comme je l'ai déjà indiqué dans un commentaire, la réponse acceptée est ce n'est pas une bonne idée , tout simplement parce que la convention veut que l'URL ne doit pas contient de telles données sensibles. Vous pouvez trouver plus d'informations ici: https://tools.ietf.org/html/rfc6750#section-5.3 (bien que cela concerne spécifiquement OAuth).
Il existe cependant une autre approche: Utiliser l’authentification de base HTTP via l’URL ws . J'ai constaté que la plupart des clients Websocket vous permettent de définir implicitement les en-têtes en ajoutant l'URL avec l'authentification de base http comme ceci: wss://user:[email protected]/cable
.
Cela ajoutera l'en-tête Authorization
avec une valeur de Basic ...
. Dans mon cas, j’utilisais imagine avec devise-jwt et j’ai simplement mis en œuvre une stratégie héritée de celle fournie dans la gemme, qui tire le jwt de l’en-tête Authorization
. J'ai donc défini l'URL comme ceci: wss://[email protected]/cable
qui définit l'en-tête sur ceci (pseudo): Basic base64("token:")
et l'analyse dans la stratégie.
Au cas où l’un d’entre vous voudrait utiliser ActionCable.createCustomer. Mais avoir un jeton renouvelable comme je le fais:
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;
Ensuite, en cas de perte de connexion, il sera ouvert avec un nouveau jeton.
Concernant la sécurité de Réponse de Pierre : Si vous utilisez le protocole WSS, qui utilise SSL pour le cryptage, les principes d'envoi de données sécurisées doivent être identiques à ceux du protocole HTTPS. Lorsque vous utilisez SSL, les paramètres de chaîne de requête sont cryptés, ainsi que le corps de la demande. Ainsi, si dans les API HTTP, vous envoyez tout type de jeton via HTTPS et le considérez comme sécurisé, il devrait en être de même pour WSS. Rappelez-vous que, comme pour HTTPS, n'envoyez pas d'informations d'identification telles que mot de passe via des paramètres de requête, car l'URL de la demande pourrait être enregistrée sur un serveur et donc stockée avec votre mot de passe. Utilisez plutôt des objets tels que des jetons émis par le serveur.
Vous pouvez également vérifier ceci (cela décrit essentiellement quelque chose comme l’authentification JWT + la vérification de l’adresse IP): https://devcenter.heroku.com/articles/websocket-security#authentication-authorization .
Une autre façon (comme je l’ai fait à la fin au lieu de mon autre réponse) serait d’avoir une action authenticate
sur votre canal. Je l'ai utilisé pour déterminer l'utilisateur actuel et le définir dans la connexion/le canal. Tous les éléments sont envoyés via des Websockets, donc les informations d'identification ne sont pas un problème ici lorsque nous les avons cryptées (c'est-à-dire wss
).