devise-jwt-cookie2 0.6.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b102a530ecfb53046e5a429698e2db4c373e8f3a9569dc51d1153c12bbf10cae
4
+ data.tar.gz: ac17f1d3252c92b4e087472555017d856c815f8a456808af1dfc7acae77a8099
5
+ SHA512:
6
+ metadata.gz: 8df2266a978c16560d9e307adbc83dc510ca5a1b1751ffb0ecc27d0d4daef394fce4a1d6dfe18a337cb6379ce72657efce51e0008a163e8b90df3e7443b311c4
7
+ data.tar.gz: 4d91703d5c8b425b52dfe777491135b1ae9633c9251331786345b722516f3c4a15ca349dbc92b43bfaf5761c56bbbb4a41122af405c9ba180c7fbd2348f38996
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2025 Major League Hacking PBC Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # Devise::JWT::Cookie
2
+
3
+ `devise-jwt-cookie2` is a fork of [devise-jwt-cookie](https://github.com/scarhand/devise-jwt-cookie), a [devise](https://github.com/plataformatec/devise) extension based on [devise-jwt](https://github.com/waiting-for-dev/devise-jwt). It should be used alongside `devise-jwt`.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'devise-jwt-cookie2', '~> 0.6.0'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ ## Usage
18
+
19
+ First you need to setup up and configure devise and devise-jwt. This gem hooks into devise-jwt to add an httpOnly cookie with the JWT.
20
+
21
+ ### Model configuration
22
+
23
+ You have to update the user model to be able to use the cookie method. For example:
24
+
25
+ ```ruby
26
+ class User < ApplicationRecord
27
+ devise :database_authenticatable,
28
+ :jwt_cookie_authenticatable,
29
+ :jwt_authenticatable, jwt_revocation_strategy: Blacklist
30
+ end
31
+ ```
32
+
33
+ ### Configuration reference
34
+
35
+ This library can be configured by calling `jwt_cookie` on the devise config object:
36
+
37
+ ```ruby
38
+ Devise.setup do |config|
39
+ config.jwt do |jwt|
40
+ # config for devise-jwt goes here
41
+ end
42
+ config.jwt_cookie do |jwt_cookie|
43
+ # ...
44
+ jwt_cookie.secure = false if Rails.env.development?
45
+ end
46
+ end
47
+ ```
48
+
49
+ #### name ([MDN](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Cookies#name))
50
+
51
+ The name of the cookie. Defaults to `access_token`.
52
+
53
+ #### secure ([MDN](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Cookies#secure))
54
+
55
+ If a secure cookie should be set, this means the cookie must be sent over a secure connection. Defaults to `true`.
56
+
57
+ #### httponly ([MDN](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Cookies#httponly))
58
+
59
+ HttpOnly option on the cookie, if set to true JavaScript running in the browser cannot access the cookie.
60
+ Defaults to `true`
61
+
62
+ #### domain ([MDN](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Cookies#domain))
63
+
64
+ The domain the cookie should be issued to. Will be omitted if not set.
65
+
66
+ #### same_site ([MDN](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Cookies#samesite))
67
+
68
+ Set's the SameSite attribute on the cookie. Defaults to `none`.
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devise
4
+ module JWT
5
+ module Cookie
6
+ # Helper class to build and remove cookies
7
+ class CookieHelper
8
+ include Cookie::Import['name', 'domain', 'secure', 'httponly', 'same_site']
9
+
10
+ def build(token)
11
+ if token.nil?
12
+ remove_cookie
13
+ else
14
+ create_cookie(token)
15
+ end
16
+ end
17
+
18
+ def read_from(cookies)
19
+ cookies[name]
20
+ end
21
+
22
+ private
23
+
24
+ def create_cookie(token)
25
+ jwt = Warden::JWTAuth::TokenDecoder.new.call(token)
26
+ res = {
27
+ value: token,
28
+ **global_options,
29
+ expires: Time.at(jwt['exp'].to_i)
30
+ }
31
+ [name, res]
32
+ end
33
+
34
+ def remove_cookie
35
+ res = {
36
+ value: nil,
37
+ **global_options,
38
+ max_age: '0',
39
+ expires: Time.at(0)
40
+ }
41
+ [name, res]
42
+ end
43
+
44
+ def global_options
45
+ res = {
46
+ path: '/',
47
+ httponly: httponly,
48
+ secure: secure,
49
+ same_site: same_site
50
+ }
51
+ res[:domain] = domain if domain.present?
52
+
53
+ res
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devise
4
+ module JWT
5
+ module Cookie
6
+ # Rack middleware to handle JWT token in cookies
7
+ class Middleware
8
+ ENV_KEY = 'warden-jwt_auth.token'
9
+
10
+ attr_reader :app, :config
11
+
12
+ def initialize(app)
13
+ @app = app
14
+ @config = Warden::JWTAuth.config
15
+ end
16
+
17
+ def call(env)
18
+ token_should_be_revoked = token_should_be_revoked?(env)
19
+ if token_should_be_revoked
20
+ # add the Authorization header, devise-jwt needs this to revoke tokens
21
+ # we need to make sure this is done before the other middleware is run
22
+ request = ActionDispatch::Request.new(env)
23
+ env['HTTP_AUTHORIZATION'] = "Bearer #{CookieHelper.new.read_from(request.cookies)}"
24
+ end
25
+
26
+ status, headers, response = app.call(env)
27
+ if headers['Authorization'] && env[ENV_KEY]
28
+ name, cookie = CookieHelper.new.build(env[ENV_KEY])
29
+ Rack::Utils.set_cookie_header!(headers, name, cookie)
30
+ elsif token_should_be_revoked
31
+ name, cookie = CookieHelper.new.build(nil)
32
+ Rack::Utils.set_cookie_header!(headers, name, cookie)
33
+ end
34
+ [status, headers, response]
35
+ end
36
+
37
+ def token_should_be_revoked?(env)
38
+ path_info = env['PATH_INFO'] || ''
39
+ method = env['REQUEST_METHOD']
40
+ revocation_requests = config.revocation_requests
41
+ revocation_requests.each do |tuple|
42
+ revocation_method, revocation_path = tuple
43
+ return true if path_info.match(revocation_path) &&
44
+ method == revocation_method
45
+ end
46
+ false
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module Devise
6
+ module Models
7
+ module JwtCookieAuthenticatable
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ def self.find_for_jwt_authentication(sub)
12
+ find_by(primary_key => sub)
13
+ end
14
+ end
15
+
16
+ def jwt_subject
17
+ id
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'devise/jwt/cookie/models/jwt_cookie_authenticatable'
4
+
5
+ module Devise
6
+ module Models
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/railtie'
4
+
5
+ module Devise
6
+ module JWT
7
+ module Cookie
8
+ class Railtie < Rails::Railtie
9
+ initializer 'devise-jwt-cookie-middleware' do |app|
10
+ app.middleware.insert_before Warden::JWTAuth::Middleware, Devise::JWT::Cookie::Middleware
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'warden'
4
+
5
+ module Devise
6
+ module JWT
7
+ module Cookie
8
+ # Warden strategy to authenticate an user through a JWT token in
9
+ # an http-only cookie
10
+ class Strategy < Warden::Strategies::Base
11
+ def valid?
12
+ !token.nil?
13
+ end
14
+
15
+ def store?
16
+ false
17
+ end
18
+
19
+ def authenticate!
20
+ aud = Warden::JWTAuth::EnvHelper.aud_header(env)
21
+ user = Warden::JWTAuth::UserDecoder.new.call(token, scope, aud)
22
+ success!(user)
23
+ rescue ::JWT::DecodeError => e
24
+ fail!(e.message)
25
+ end
26
+
27
+ private
28
+
29
+ def token
30
+ @token ||= CookieHelper.new.read_from(cookies)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ Warden::Strategies.add(:jwt_cookie, Devise::JWT::Cookie::Strategy)
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Devise
4
+ module JWT
5
+ module Cookie
6
+ VERSION = '0.6.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/configurable'
4
+ require 'dry/auto_inject'
5
+ require 'devise'
6
+ require 'devise/jwt'
7
+ require 'devise/jwt/cookie/strategy'
8
+
9
+ # Authentication library
10
+ module Devise
11
+ def self.jwt_cookie
12
+ yield(Devise::JWT::Cookie.config)
13
+ end
14
+
15
+ add_module(:jwt_cookie_authenticatable, strategy: :jwt_cookie)
16
+
17
+ module JWT
18
+ # Devise extension for adding cookie support on top of devise-jwt
19
+ module Cookie
20
+ extend Dry::Configurable
21
+
22
+ setting :name, default: 'access_token'
23
+ setting :secure, default: true
24
+ setting :httponly, default: true
25
+ setting :same_site, default: :none
26
+ setting :domain
27
+
28
+ Import = Dry::AutoInject(config)
29
+ end
30
+ end
31
+ end
32
+
33
+ require 'devise/jwt/cookie/version'
34
+ require 'devise/jwt/cookie/railtie'
35
+ require 'devise/jwt/cookie/cookie_helper'
36
+ require 'devise/jwt/cookie/middleware'
37
+ require 'devise/jwt/cookie/models'
metadata ADDED
@@ -0,0 +1,222 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: devise-jwt-cookie2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0
5
+ platform: ruby
6
+ authors:
7
+ - Major League Hacking
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-02-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: devise-jwt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.10'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-auto_inject
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dry-configurable
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">"
60
+ - !ruby/object:Gem::Version
61
+ version: '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'
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
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 7.1.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 7.1.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec-rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 7.1.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 7.1.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: sqlite3
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: mlh-rubocop-config
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '1.0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '1.0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: simplecov
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '='
172
+ - !ruby/object:Gem::Version
173
+ version: '0.17'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '='
179
+ - !ruby/object:Gem::Version
180
+ version: '0.17'
181
+ description: Cookie-based JWT authentication for devise with configurable token revocation
182
+ strategies, forked from devise-jwt-cookie
183
+ email:
184
+ - hi@mlh.io
185
+ executables: []
186
+ extensions: []
187
+ extra_rdoc_files: []
188
+ files:
189
+ - LICENSE
190
+ - README.md
191
+ - lib/devise/jwt/cookie/cookie_helper.rb
192
+ - lib/devise/jwt/cookie/middleware.rb
193
+ - lib/devise/jwt/cookie/models.rb
194
+ - lib/devise/jwt/cookie/models/jwt_cookie_authenticatable.rb
195
+ - lib/devise/jwt/cookie/railtie.rb
196
+ - lib/devise/jwt/cookie/strategy.rb
197
+ - lib/devise/jwt/cookie/version.rb
198
+ - lib/devise/jwt/cookie2.rb
199
+ homepage: https://github.com/mlh/devise-jwt-cookie2
200
+ licenses:
201
+ - MIT
202
+ metadata: {}
203
+ post_install_message:
204
+ rdoc_options: []
205
+ require_paths:
206
+ - lib
207
+ required_ruby_version: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: 2.7.0
212
+ required_rubygems_version: !ruby/object:Gem::Requirement
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: '0'
217
+ requirements: []
218
+ rubygems_version: 3.1.6
219
+ signing_key:
220
+ specification_version: 4
221
+ summary: Cookie-based JWT authentication for devise
222
+ test_files: []