devise-api 0.2.0 → 0.3.1
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 +4 -4
- data/.claude/commands/commit.md +38 -0
- data/.claude/commands/create-branch.md +57 -0
- data/.claude/commands/create-pr.md +41 -0
- data/CHANGELOG.md +60 -1
- data/CLAUDE.md +49 -0
- data/Gemfile +9 -3
- data/Gemfile.lock +257 -185
- data/README.md +320 -162
- data/Rakefile +6 -0
- data/app/controllers/devise/api/tokens_controller.rb +48 -108
- data/app/services/devise/api/resource_owner_service/authenticate.rb +9 -2
- data/app/services/devise/api/tokens_service/create.rb +16 -8
- data/app/services/devise/api/tokens_service/refresh.rb +17 -2
- data/app/services/devise/api/tokens_service/revoke.rb +1 -1
- data/config/locales/en.yml +1 -0
- data/docs/README.md +32 -0
- data/docs/analysis/known-issues.md +56 -0
- data/docs/analysis/security-review.md +53 -0
- data/docs/api-reference.md +95 -0
- data/docs/architecture.md +145 -0
- data/docs/configuration.md +84 -0
- data/docs/data-model.md +80 -0
- data/docs/development.md +52 -0
- data/docs/extending.md +75 -0
- data/docs/services.md +72 -0
- data/docs/testing.md +65 -0
- data/lib/devise/api/configuration.rb +7 -0
- data/lib/devise/api/controllers/helpers.rb +5 -3
- data/lib/devise/api/generators/templates/migration.rb.erb +2 -2
- data/lib/devise/api/rails/engine.rb +6 -0
- data/lib/devise/api/responses/error_response.rb +40 -29
- data/lib/devise/api/responses/token_response.rb +5 -1
- data/lib/devise/api/token.rb +34 -2
- data/lib/devise/api/version.rb +1 -1
- metadata +18 -7
- data/sig/devise/api.rbs +0 -6
|
@@ -15,6 +15,7 @@ module Devise
|
|
|
15
15
|
sign_up_disabled
|
|
16
16
|
invalid_refresh_token
|
|
17
17
|
invalid_email
|
|
18
|
+
invalid_login
|
|
18
19
|
invalid_resource_owner
|
|
19
20
|
resource_owner_create_error
|
|
20
21
|
devise_api_token_create_error
|
|
@@ -22,6 +23,15 @@ module Devise
|
|
|
22
23
|
invalid_authentication
|
|
23
24
|
].freeze
|
|
24
25
|
|
|
26
|
+
UNAUTHORIZED_ERRORS = %i[
|
|
27
|
+
invalid_token expired_token expired_refresh_token revoked_token invalid_authentication
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
BAD_REQUEST_ERRORS = %i[
|
|
31
|
+
invalid_email invalid_login invalid_refresh_token refresh_token_disabled sign_up_disabled
|
|
32
|
+
invalid_resource_owner
|
|
33
|
+
].freeze
|
|
34
|
+
|
|
25
35
|
ERROR_TYPES.each do |error_type|
|
|
26
36
|
method_name = error_type.end_with?('_error') ? error_type : "#{error_type}_error"
|
|
27
37
|
|
|
@@ -55,33 +65,34 @@ module Devise
|
|
|
55
65
|
|
|
56
66
|
private
|
|
57
67
|
|
|
58
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
59
68
|
def error_description
|
|
60
69
|
return [I18n.t("devise.api.error_response.#{error}")] if record.blank?
|
|
61
|
-
if invalid_authentication_error?
|
|
62
|
-
return [I18n.t('devise.api.error_response.lockable.locked')]
|
|
63
|
-
end
|
|
64
|
-
if invalid_authentication_error? && devise_confirmable_info.present? && !record.confirmed?
|
|
65
|
-
return [I18n.t('devise.api.error_response.confirmable.unconfirmed')]
|
|
66
|
-
end
|
|
67
|
-
return [I18n.t('devise.api.error_response.invalid_authentication')] if invalid_authentication_error?
|
|
70
|
+
return invalid_authentication_description if invalid_authentication_error?
|
|
68
71
|
|
|
69
72
|
record.errors.full_messages
|
|
70
73
|
end
|
|
71
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
72
74
|
|
|
73
|
-
def
|
|
74
|
-
unless
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
def invalid_authentication_description
|
|
76
|
+
unless Devise.api.config.paranoid
|
|
77
|
+
return [I18n.t('devise.api.error_response.lockable.locked')] if lockable_record? && record.access_locked?
|
|
78
|
+
if confirmable_record? && !record.confirmed?
|
|
79
|
+
return [I18n.t('devise.api.error_response.confirmable.unconfirmed')]
|
|
80
|
+
end
|
|
78
81
|
end
|
|
79
82
|
|
|
83
|
+
[I18n.t('devise.api.error_response.invalid_authentication')]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def devise_lockable_info
|
|
87
|
+
return nil unless verbose_account_state? && lockable_record? && invalid_authentication_error?
|
|
88
|
+
|
|
80
89
|
unlock_at = record.access_locked? ? record.locked_at + ::Devise.unlock_in : nil
|
|
81
90
|
|
|
82
91
|
{
|
|
83
92
|
locked: record.access_locked?,
|
|
84
93
|
max_attempts: ::Devise.maximum_attempts,
|
|
94
|
+
failed_attempts: record.failed_attempts,
|
|
95
|
+
# Deprecated misspelling kept for backward compatibility; will be removed in the next major release
|
|
85
96
|
failed_attemps: record.failed_attempts,
|
|
86
97
|
locked_at: record.locked_at,
|
|
87
98
|
unlock_at: unlock_at
|
|
@@ -89,11 +100,7 @@ module Devise
|
|
|
89
100
|
end
|
|
90
101
|
|
|
91
102
|
def devise_confirmable_info
|
|
92
|
-
unless
|
|
93
|
-
resource_class.supported_devise_modules.confirmable? &&
|
|
94
|
-
invalid_authentication_error?
|
|
95
|
-
return nil
|
|
96
|
-
end
|
|
103
|
+
return nil unless verbose_account_state? && confirmable_record? && invalid_authentication_error?
|
|
97
104
|
|
|
98
105
|
{
|
|
99
106
|
confirmed: record.confirmed?,
|
|
@@ -101,20 +108,24 @@ module Devise
|
|
|
101
108
|
}.compact
|
|
102
109
|
end
|
|
103
110
|
|
|
111
|
+
def lockable_record?
|
|
112
|
+
record.present? && resource_class.present? && resource_class.supported_devise_modules.lockable?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def confirmable_record?
|
|
116
|
+
record.present? && resource_class.present? && resource_class.supported_devise_modules.confirmable?
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def verbose_account_state?
|
|
120
|
+
!Devise.api.config.paranoid && Devise.api.config.error_response.verbose_account_state
|
|
121
|
+
end
|
|
122
|
+
|
|
104
123
|
def unauthorized_status?
|
|
105
|
-
|
|
106
|
-
expired_token_error? ||
|
|
107
|
-
expired_refresh_token_error? ||
|
|
108
|
-
revoked_token_error? ||
|
|
109
|
-
invalid_authentication_error?
|
|
124
|
+
UNAUTHORIZED_ERRORS.include?(error)
|
|
110
125
|
end
|
|
111
126
|
|
|
112
127
|
def bad_request_status?
|
|
113
|
-
|
|
114
|
-
invalid_refresh_token_error? ||
|
|
115
|
-
refresh_token_disabled_error? ||
|
|
116
|
-
sign_up_disabled_error? ||
|
|
117
|
-
invalid_resource_owner_error?
|
|
128
|
+
BAD_REQUEST_ERRORS.include?(error)
|
|
118
129
|
end
|
|
119
130
|
end
|
|
120
131
|
end
|
|
@@ -66,7 +66,11 @@ module Devise
|
|
|
66
66
|
def signed_up_body
|
|
67
67
|
return default_body unless resource_owner.class.supported_devise_modules.confirmable?
|
|
68
68
|
|
|
69
|
-
message = resource_owner.confirmed?
|
|
69
|
+
message = if resource_owner.confirmed?
|
|
70
|
+
nil
|
|
71
|
+
else
|
|
72
|
+
I18n.t('devise.api.error_response.registerable.signed_up_but_unconfirmed')
|
|
73
|
+
end
|
|
70
74
|
|
|
71
75
|
default_body.merge(confirmable: { confirmed: resource_owner.confirmed?, message: message }.compact)
|
|
72
76
|
end
|
data/lib/devise/api/token.rb
CHANGED
|
@@ -7,6 +7,10 @@ module Devise
|
|
|
7
7
|
class Token < ::ActiveRecord::Base
|
|
8
8
|
self.table_name = 'devise_api_tokens'
|
|
9
9
|
|
|
10
|
+
# Redact raw token secrets from #inspect / pretty_print output (see also the
|
|
11
|
+
# devise.api.filter_parameters engine initializer for request-log filtering)
|
|
12
|
+
self.filter_attributes += %i[access_token refresh_token previous_refresh_token]
|
|
13
|
+
|
|
10
14
|
# associations
|
|
11
15
|
belongs_to :resource_owner,
|
|
12
16
|
polymorphic: true,
|
|
@@ -36,6 +40,21 @@ module Devise
|
|
|
36
40
|
revoked_at.present?
|
|
37
41
|
end
|
|
38
42
|
|
|
43
|
+
def revoke!
|
|
44
|
+
return self if revoked?
|
|
45
|
+
|
|
46
|
+
update!(revoked_at: Time.current)
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Revokes every token in this token's refresh chain (ancestors and descendants). Used for
|
|
51
|
+
# refresh-token reuse detection when refresh_token.rotation_enabled is on.
|
|
52
|
+
def revoke_family!
|
|
53
|
+
transaction do
|
|
54
|
+
family_tokens.each(&:revoke!)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
39
58
|
def active?
|
|
40
59
|
!inactive?
|
|
41
60
|
end
|
|
@@ -47,13 +66,13 @@ module Devise
|
|
|
47
66
|
def expired?
|
|
48
67
|
return false if Devise.api.config.access_token.expires_in_infinite.call(resource_owner)
|
|
49
68
|
|
|
50
|
-
!!(expires_in && Time.
|
|
69
|
+
!!(expires_in && Time.current > expires_at)
|
|
51
70
|
end
|
|
52
71
|
|
|
53
72
|
def refresh_token_expired?
|
|
54
73
|
return false if Devise.api.config.refresh_token.expires_in_infinite.call(resource_owner)
|
|
55
74
|
|
|
56
|
-
Time.
|
|
75
|
+
Time.current > refresh_token_expires_at
|
|
57
76
|
end
|
|
58
77
|
|
|
59
78
|
def self.generate_uniq_access_token(resource_owner)
|
|
@@ -83,6 +102,19 @@ module Devise
|
|
|
83
102
|
def refresh_token_expires_at
|
|
84
103
|
created_at + Devise.api.config.refresh_token.expires_in.seconds
|
|
85
104
|
end
|
|
105
|
+
|
|
106
|
+
def family_tokens
|
|
107
|
+
root = self
|
|
108
|
+
root = root.previous_refresh while root.previous_refresh.present?
|
|
109
|
+
|
|
110
|
+
tokens = []
|
|
111
|
+
queue = [root]
|
|
112
|
+
while (token = queue.shift)
|
|
113
|
+
tokens << token
|
|
114
|
+
queue.concat(token.refreshes.to_a)
|
|
115
|
+
end
|
|
116
|
+
tokens
|
|
117
|
+
end
|
|
86
118
|
end
|
|
87
119
|
end
|
|
88
120
|
end
|
data/lib/devise/api/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: devise-api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- nejdetkadir
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: devise
|
|
@@ -113,9 +112,13 @@ executables: []
|
|
|
113
112
|
extensions: []
|
|
114
113
|
extra_rdoc_files: []
|
|
115
114
|
files:
|
|
115
|
+
- ".claude/commands/commit.md"
|
|
116
|
+
- ".claude/commands/create-branch.md"
|
|
117
|
+
- ".claude/commands/create-pr.md"
|
|
116
118
|
- ".rspec"
|
|
117
119
|
- ".rubocop.yml"
|
|
118
120
|
- CHANGELOG.md
|
|
121
|
+
- CLAUDE.md
|
|
119
122
|
- CODE_OF_CONDUCT.md
|
|
120
123
|
- Gemfile
|
|
121
124
|
- Gemfile.lock
|
|
@@ -132,6 +135,17 @@ files:
|
|
|
132
135
|
- app/services/devise/api/tokens_service/revoke.rb
|
|
133
136
|
- config/locales/en.yml
|
|
134
137
|
- devise-api.gemspec
|
|
138
|
+
- docs/README.md
|
|
139
|
+
- docs/analysis/known-issues.md
|
|
140
|
+
- docs/analysis/security-review.md
|
|
141
|
+
- docs/api-reference.md
|
|
142
|
+
- docs/architecture.md
|
|
143
|
+
- docs/configuration.md
|
|
144
|
+
- docs/data-model.md
|
|
145
|
+
- docs/development.md
|
|
146
|
+
- docs/extending.md
|
|
147
|
+
- docs/services.md
|
|
148
|
+
- docs/testing.md
|
|
135
149
|
- lib/devise/api.rb
|
|
136
150
|
- lib/devise/api/configuration.rb
|
|
137
151
|
- lib/devise/api/controllers/helpers.rb
|
|
@@ -143,7 +157,6 @@ files:
|
|
|
143
157
|
- lib/devise/api/responses/token_response.rb
|
|
144
158
|
- lib/devise/api/token.rb
|
|
145
159
|
- lib/devise/api/version.rb
|
|
146
|
-
- sig/devise/api.rbs
|
|
147
160
|
homepage: https://github.com/nejdetkadir/devise-api
|
|
148
161
|
licenses:
|
|
149
162
|
- MIT
|
|
@@ -151,7 +164,6 @@ metadata:
|
|
|
151
164
|
homepage_uri: https://github.com/nejdetkadir/devise-api
|
|
152
165
|
source_code_uri: https://github.com/nejdetkadir/devise-api
|
|
153
166
|
changelog_uri: https://github.com/nejdetkadir/devise-api/blob/main/CHANGELOG.md
|
|
154
|
-
post_install_message:
|
|
155
167
|
rdoc_options: []
|
|
156
168
|
require_paths:
|
|
157
169
|
- lib
|
|
@@ -166,8 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
166
178
|
- !ruby/object:Gem::Version
|
|
167
179
|
version: '0'
|
|
168
180
|
requirements: []
|
|
169
|
-
rubygems_version:
|
|
170
|
-
signing_key:
|
|
181
|
+
rubygems_version: 4.0.6
|
|
171
182
|
specification_version: 4
|
|
172
183
|
summary: The devise-api gem is a convenient way to add authentication to your Ruby
|
|
173
184
|
on Rails application using the devise gem. It provides support for access tokens
|