github.com/anycable/anycable-go@v1.5.1/docs/jwt_identification.md (about) 1 # JWT authentication 2 3 AnyCable provides support for [JWT][jwt]-based authentication and identification. 4 5 We use the term "identification", because you can also pass a properly structured information as a part of the token to not only authentication the connection but also set up _identifiers_ (in terms of Action Cable). This approach brings the following benefits: 6 7 - **Performance**. No RPC call is required during the connection initiation, since we already have identification information. Thus, less load on the RPC server, much faster connection time (at least, 2x faster). 8 - **Usability**. Universal way of dealing with credentials (no need to deal with cookies for web and whatever else for mobile apps). 9 - **Security**. CSRF-safe by design. Configurable life time for tokens makes it easier to keep access under control. 10 11 ## Usage 12 13 > See the [demo](https://github.com/anycable/anycable_rails_demo/pull/23) of using JWT identification in a Rails app with [AnyCable JS client library][anycable-client]. 14 15 **NOTE**: Currently, we only support the HMAC signing algorithms. 16 17 By default, the `--secret` configuration parameter is used as a JWT secret key. If you want to use a custom key for JWT, you can specify it via the `--jwt_secret` (`ANYCABLE_JWT_SECRET`) parameter. 18 19 Other configuration options are: 20 21 - (_Optional_) **--jwt_param** (`ANYCABLE_ID_PARAM`, default: "jid"): the name of a query string param or an HTTP header, which carries a token. The header name is prefixed with `X-`. 22 - (_Optional_) **--enforce_jwt** (`ANYCABLE_ENFORCE_JWT`, default: false): whether to require all connection requests to contain a token. Connections without a token would be rejected right away. If not set, the servers fallbacks to the RPC call (if RPC is configured) or would be accepted if authentication is disabled (`--noauth`). 23 24 A client must provide an identification token either via a query param or via an HTTP header (if possible). For example: 25 26 ```js 27 import { createCable } from '@anycable/web' 28 29 let cable = createCable('ws://cable.example.com/cable?jid=[JWT_TOKEN]') 30 ``` 31 32 The token MUST include the `ext` claim with the JSON-encoded connection identifiers. 33 34 ## Generating tokens 35 36 ### Rails/Ruby 37 38 When using AnyCable Ruby/Rails SDK, you can generate tokens as follows: 39 40 ```ruby 41 token = AnyCable::JWT.encode({user: current_user}) 42 43 # Setting TTL is also possible 44 token = AnyCable::JWT.encode({user: current_user}, expires_at: 10.minutes.from_now) 45 ``` 46 47 If you don't want to use our SDKs (why?), here is how you can generate tokens yourself: 48 49 ```ruby 50 require "jwt" 51 require "json" 52 53 ENCRYPTION_KEY = "some-sercret-key" 54 55 # !!! Expiration is the responsibility of the token issuer 56 exp = Time.now.to_i + 30 57 58 # Provides the serialized values for identifiers (`identified_by` in Action Cable) 59 identifiers = {user_id: 42} 60 61 # JWT payload 62 payload = {ext: identifiers.to_json, exp: exp} 63 64 puts JWT.encode payload, ENCRYPTION_KEY, "HS256" 65 ``` 66 67 ## JavaScript/TypeScript 68 69 You can use [AnyCable server-side JS SDK](https://github.com/anycable/anycable-serverless-js) to generate tokens as follows: 70 71 ```js 72 import { identificator } from "@anycable/serverless-js"; 73 74 const jwtSecret = "very-secret"; 75 const jwtTTL = "1h"; 76 77 export const identifier = identificator(jwtSecret, jwtTTL); 78 79 // Then, somewhere in your code, generate a token and provide it to the client 80 const userId = authenticatedUser.id; 81 const token = await identifier.generateToken({ userId }); 82 ``` 83 84 ## PHP 85 86 You can use the following snippet to generate tokens in PHP: 87 88 ```php 89 use Firebase\JWT\JWT; 90 91 $identifiers = ['user_id' => 42]; 92 $payload = ['ext' => json_encode($identifiers), 'exp' => time() + 300]; 93 $jwt = JWT::encode($payload, $ENCRYPTION_KEY, 'HS256'); 94 ``` 95 96 ## Python 97 98 Here is an example Python code to generate AnyCable tokens: 99 100 ```python 101 import json 102 import jwt 103 import time 104 105 identifiers = {'user_id': 42} 106 payload = {'ext': json.dumps(identifiers), 'exp': int(time.time()) + 300} 107 jwt.encode(payload, ENCRYPTION_KEY, algorithm='HS256') 108 ``` 109 110 ### Handling expired tokens 111 112 > 🎥 Check out this [AnyCasts episode](https://anycable.io/blog/anycasts-using-anycable-client/) to learn more about the expiration problem and how to solve it using [anycable-client](https://github.com/anycable/anycable-client). 113 114 Whenever a server encounters a token that has expired, it rejects the connection and send the `disconnect` message with `reason: "token_expired"`. It's a client responsibility to handle this situation and refresh the token. 115 116 See, for example, how [anycable-client handles this](https://github.com/anycable/anycable-client#refreshing-authentication-tokens). 117 118 [jwt]: https://jwt.io 119 [anycable-client]: https://github.com/anycable/anycable-client