anycable-rails-jwt 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +82 -0
- data/lib/anycable/rails/jwt/config.rb +13 -0
- data/lib/anycable/rails/jwt/helper.rb +34 -0
- data/lib/anycable/rails/jwt/railtie.rb +17 -0
- data/lib/anycable/rails/jwt/version.rb +9 -0
- data/lib/anycable-rails-jwt.rb +45 -0
- metadata +140 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7e38b60305bc5b61376f47a37f89c09e1abb328e9c8df059cce318db9442f8e2
|
4
|
+
data.tar.gz: 077eadd6f914cfa06076dbf8b58ae7884ca24d760a4184b8433301ba7e453cc4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b12e6a72e8fa9564922875b30df8842a4d46d5609c19ebbe74d85ce95dac9d05f127c29ebdea32fd818c20b26b31b80de32b5c18384f4265dcd50a87ee6d893f
|
7
|
+
data.tar.gz: 2f2475a62ffd70990edd0e9936c39a883285a77582804f7a15a02fcb38add534320e4245ed894b62994b272f5db0fd77a3706ec1dcc502f31532632a96b25fe2
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2021 Vladimir Dementyev
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
[](https://rubygems.org/gems/anycable-rails-jwt)
|
2
|
+
[](https://github.com/anycable/anycable-rails-jwt/actions)
|
3
|
+
|
4
|
+
# Anycable Rails JWT
|
5
|
+
|
6
|
+
AnyCable Rails helpers for [JWT-based identification](https://docs.anycable.io/anycable-go/jwt_identification) from [AnyCable PRO][pro].
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add gem to your project:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
# Gemfile
|
14
|
+
gem "anycable-rails-jwt"
|
15
|
+
```
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
### Configuration
|
20
|
+
|
21
|
+
This gem extends AnyCable configuration and adds the following new parameters:
|
22
|
+
|
23
|
+
- `jwt_id_key`: JWT encryption key used to sign tokens (required).
|
24
|
+
- `jwt_id_param`: the name of the query string parameter to carry tokens (defaults to `jid`).
|
25
|
+
- `jwt_id_ttl`: the number of seconds for a token to live (defaults to 3600, one hour).
|
26
|
+
|
27
|
+
You can specify these parameters in `config/anycable.yml`, credentials, ENV or whatever source of configuration you use for AnyCable.
|
28
|
+
|
29
|
+
### `action_cable_with_jwt_meta_tag`
|
30
|
+
|
31
|
+
In order to generate a token and pass it to a client, you can use an HTML meta tag similar to the built-in `#action_cable_meta_tag`:
|
32
|
+
|
33
|
+
```erb
|
34
|
+
<%= action_cable_with_jwt_meta_tag(current_user: current_user) %> would render:
|
35
|
+
# => <meta name="action-cable-url" content="ws://demo.anycable.io/cable?token=eyJhbGciOiJIUzI1NiJ9....EWCEzziOx3sKyMoNzBt20a3QvhEdxJXCXaZsA-f-UzU" />
|
36
|
+
```
|
37
|
+
|
38
|
+
You must pass all the required identifiers you have in your `ApplicationCable::Connection` class.
|
39
|
+
|
40
|
+
### `AnyCable::Rails::JWT.encode`
|
41
|
+
|
42
|
+
Alternatively, you can generate a token and deliver it to a client the way you prefer. For that, you can use `AnyCable::Rails::JWT.encode` method:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
AnyCable::Rails::JWT.encode(current_user: current_user) #=> <token>
|
46
|
+
|
47
|
+
# you can also override the global TTL setting via expires_at option
|
48
|
+
AnyCable::Rails::JWT.encode(current_user: current_user, expires_at: 10.minutes.from_now)
|
49
|
+
```
|
50
|
+
|
51
|
+
### Decoding tokens and using without AnyCable
|
52
|
+
|
53
|
+
Although the main purpose of this library is to add AnyCable PRO identification support, it's possible to use JWT-based authentication without AnyCable at all. For that, you can do something like this in your `ApplicationCable::Connection` class:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
module ApplicationCable
|
57
|
+
class Connection < ActionCable::Connection::Base
|
58
|
+
identified_by :current_user
|
59
|
+
|
60
|
+
def connect
|
61
|
+
token = request.params[:token]
|
62
|
+
|
63
|
+
identifiers = AnyCable::Rails::JWT.decode(token)
|
64
|
+
identifiers.each do |k, v|
|
65
|
+
public_send("#{k}=", v)
|
66
|
+
end
|
67
|
+
rescue JWT::DecodeError
|
68
|
+
reject_unauthorized_connection
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
## Contributing
|
75
|
+
|
76
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/anycable/anycable-rails-jwt](https://github.com/anycable/anycable-rails-jwt).
|
77
|
+
|
78
|
+
## License
|
79
|
+
|
80
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
81
|
+
|
82
|
+
[pro]: https://anycable.io/#pro
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "anycable/config"
|
4
|
+
# Make sure Rails extensions for Anyway Config are loaded
|
5
|
+
# See https://github.com/anycable/anycable-rails/issues/63
|
6
|
+
require "anyway/rails"
|
7
|
+
|
8
|
+
# Extend AnyCable configuration with jwt_id_key, jwt_id_param, jwt_id_ttl.
|
9
|
+
AnyCable::Config.attr_config(
|
10
|
+
:jwt_id_key,
|
11
|
+
jwt_id_param: "jid",
|
12
|
+
jwt_id_ttl: 3600
|
13
|
+
)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module AnyCable
|
6
|
+
module Rails
|
7
|
+
module JWT # :nodoc:
|
8
|
+
module Helper
|
9
|
+
def action_cable_with_jwt_meta_tag(**identifiers)
|
10
|
+
# From: https://github.com/rails/rails/blob/main/actioncable/lib/action_cable/helpers/action_cable_helper.rb
|
11
|
+
base_url = ActionCable.server.config.url ||
|
12
|
+
ActionCable.server.config.mount_path ||
|
13
|
+
raise("No Action Cable URL configured -- please configure this at config.action_cable.url")
|
14
|
+
|
15
|
+
token = JWT.encode(**identifiers)
|
16
|
+
|
17
|
+
parts = [base_url, "#{AnyCable.config.jwt_id_param}=#{token}"]
|
18
|
+
|
19
|
+
uri = URI.parse(base_url)
|
20
|
+
|
21
|
+
url = parts.join(uri.query ? "&" : "?")
|
22
|
+
|
23
|
+
tag "meta", name: "action-cable-url", content: url
|
24
|
+
end
|
25
|
+
|
26
|
+
def any_cable_jwt_meta_tag(**identifiers)
|
27
|
+
token = JWT.encode(**identifiers)
|
28
|
+
|
29
|
+
tag "meta", name: "any-cable-jwt", content: token
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "anycable/rails/jwt/helper"
|
4
|
+
|
5
|
+
module AnyCable
|
6
|
+
module Rails
|
7
|
+
module JWT # :nodoc:
|
8
|
+
class Railtie < ::Rails::Railtie # :nodoc:
|
9
|
+
initializer "anycable_rails_jwt.helpers" do
|
10
|
+
ActiveSupport.on_load(:action_view) do
|
11
|
+
include AnyCable::Rails::JWT::Helper
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jwt"
|
4
|
+
require "json"
|
5
|
+
require "anycable-rails"
|
6
|
+
|
7
|
+
require "anycable/rails/jwt/version"
|
8
|
+
require "anycable/rails/jwt/config"
|
9
|
+
|
10
|
+
module AnyCable
|
11
|
+
module Rails
|
12
|
+
module JWT
|
13
|
+
ALGORITHM = "HS256"
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def encode(expires_at: nil, **identifiers)
|
17
|
+
key = AnyCable.config.jwt_id_key
|
18
|
+
raise ArgumentError, "JWT encryption key is not specified. Add it via `jwt_id_key` option" if key.blank?
|
19
|
+
|
20
|
+
expires_at ||= AnyCable.config.jwt_id_ttl.seconds.from_now
|
21
|
+
|
22
|
+
serialized_ids = identifiers.transform_values { |v| AnyCable::Rails.serialize(v) }
|
23
|
+
|
24
|
+
payload = {ext: serialized_ids.to_json, exp: expires_at.to_i}
|
25
|
+
|
26
|
+
::JWT.encode(payload, key, ALGORITHM)
|
27
|
+
end
|
28
|
+
|
29
|
+
def decode(token)
|
30
|
+
key = AnyCable.config.jwt_id_key
|
31
|
+
raise ArgumentError, "JWT encryption key is not specified. Add it via `jwt_id_key` option" if key.blank?
|
32
|
+
|
33
|
+
::JWT.decode(token, key, true, {algorithm: ALGORITHM}).then do |decoded|
|
34
|
+
JSON.parse(decoded.first.fetch("ext"))
|
35
|
+
end.then do |serialized_ids|
|
36
|
+
serialized_ids.transform_values! { |v| AnyCable::Rails.deserialize(v) }
|
37
|
+
serialized_ids
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
require "anycable/rails/jwt/railtie" if defined?(Rails::Railtie)
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: anycable-rails-jwt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vladimir Dementyev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-09-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: anycable-rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.15'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.15'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: combustion
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '13.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '13.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '4.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '4.0'
|
97
|
+
description: AnyCable Rails helpers for JWT-based authentication
|
98
|
+
email:
|
99
|
+
- dementiev.vm@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- CHANGELOG.md
|
105
|
+
- LICENSE.txt
|
106
|
+
- README.md
|
107
|
+
- lib/anycable-rails-jwt.rb
|
108
|
+
- lib/anycable/rails/jwt/config.rb
|
109
|
+
- lib/anycable/rails/jwt/helper.rb
|
110
|
+
- lib/anycable/rails/jwt/railtie.rb
|
111
|
+
- lib/anycable/rails/jwt/version.rb
|
112
|
+
homepage: http://github.com/anycable/anycable-rails-jwt
|
113
|
+
licenses:
|
114
|
+
- MIT
|
115
|
+
metadata:
|
116
|
+
bug_tracker_uri: http://github.com/anycable/anycable-rails-jwt/issues
|
117
|
+
changelog_uri: https://github.com/anycable/anycable-rails-jwt/blob/master/CHANGELOG.md
|
118
|
+
documentation_uri: http://github.com/anycable/anycable-rails-jwt
|
119
|
+
homepage_uri: http://github.com/anycable/anycable-rails-jwt
|
120
|
+
source_code_uri: http://github.com/anycable/anycable-rails-jwt
|
121
|
+
post_install_message:
|
122
|
+
rdoc_options: []
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '2.6'
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
requirements: []
|
136
|
+
rubygems_version: 3.2.15
|
137
|
+
signing_key:
|
138
|
+
specification_version: 4
|
139
|
+
summary: AnyCable Rails helpers for JWT-based authentication
|
140
|
+
test_files: []
|