auth_rails 1.0.0 → 1.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 +4 -4
- data/README.md +85 -0
- data/app/controllers/auth_rails/api/auth_controller.rb +8 -3
- data/app/controllers/concerns/auth_rails/authentication.rb +1 -1
- data/auth_rails.gemspec +2 -1
- data/lib/auth_rails/class_methods.rb +44 -0
- data/lib/auth_rails/config.rb +6 -2
- data/lib/auth_rails/strategies/allowed_token_strategy.rb +1 -1
- data/lib/auth_rails/strategies/base_strategy.rb +1 -1
- data/lib/auth_rails/version.rb +1 -1
- data/lib/auth_rails.rb +2 -0
- data/lib/generators/auth_rails/migration_generator.rb +53 -0
- data/lib/generators/auth_rails/templates/allowed_tokens.tt +17 -0
- data/lib/generators/auth_rails_generator.rb +38 -0
- data/lib/generators/templates/auth_rails.tt +32 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c69df81c88cacdf202e2c49cf329423ded23e509fc2dc67fd60d07fda1fbe12e
|
4
|
+
data.tar.gz: abbad690e4211d181950af7bf542e2df367b79c0c79c049f32c5192188106f4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b97b6c42ed2386b526ce58bbc9e8fd2982e7b60357adebfb0c547f3b3cbf6d07f6cbca8e4cf13871a42f69935ae09daf2dc7528563dd024dcc95dc95e8611f5a
|
7
|
+
data.tar.gz: 758425cb02330e2efd68fedc8730e4b8879136098f0ecfb1d9543812e8220fdbc51f8ec1eaa15c19a09d17e29788c4968969121e08c9a6cf922a2ea4b60e005e
|
data/README.md
CHANGED
@@ -6,8 +6,43 @@ Simple authentication for rails.
|
|
6
6
|
gem 'auth_rails'
|
7
7
|
```
|
8
8
|
|
9
|
+
# CLI
|
10
|
+
|
11
|
+
- init `auth_rails`
|
12
|
+
|
13
|
+
```sh
|
14
|
+
rails g auth_rails
|
15
|
+
```
|
16
|
+
|
17
|
+
- init `auth_rails` with strategy
|
18
|
+
|
19
|
+
```sh
|
20
|
+
rails g auth_rails --strategy allowed_token
|
21
|
+
```
|
22
|
+
|
23
|
+
- create migration for `allowed_token` strategy
|
24
|
+
|
25
|
+
```sh
|
26
|
+
rails g auth_rails:migration --strategy allowed_token
|
27
|
+
```
|
28
|
+
|
29
|
+
- if your model is not User
|
30
|
+
|
31
|
+
```sh
|
32
|
+
rails g auth_rails:migration --strategy allowed_token --model CustomUser
|
33
|
+
```
|
34
|
+
|
9
35
|
# Configuration
|
10
36
|
|
37
|
+
- User model must have `has_secure_password`
|
38
|
+
|
39
|
+
```rb
|
40
|
+
# app/models/user.rb
|
41
|
+
class User < ApplicationRecord
|
42
|
+
has_secure_password
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
11
46
|
```rb
|
12
47
|
# config/initializers/auth_rails.rb
|
13
48
|
|
@@ -84,6 +119,8 @@ end
|
|
84
119
|
|
85
120
|
class User < ApplicationRecord
|
86
121
|
include AuthRails::Concerns::AllowedTokenStrategy
|
122
|
+
|
123
|
+
has_secure_password
|
87
124
|
end
|
88
125
|
```
|
89
126
|
|
@@ -128,6 +165,54 @@ module Api
|
|
128
165
|
end
|
129
166
|
```
|
130
167
|
|
168
|
+
- In case your identifier is not email
|
169
|
+
|
170
|
+
```rb
|
171
|
+
Rails.application.config.to_prepare do
|
172
|
+
AuthRails.configure do |config|
|
173
|
+
config.resource_class = User # required
|
174
|
+
config.identifier_name = :username # must be string or symbol, default is email
|
175
|
+
end
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
- If you have a custom method to validate password
|
180
|
+
|
181
|
+
```rb
|
182
|
+
Rails.application.config.to_prepare do
|
183
|
+
AuthRails.configure do |config|
|
184
|
+
config.resource_class = User # required
|
185
|
+
config.identifier_name = :username # must be string or symbol, default is email
|
186
|
+
config.authenticate = ->(resource, password) { resource.password == password } # must be a proc, validate password
|
187
|
+
end
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
- Sometimes, you have a complex logic to get the user
|
192
|
+
|
193
|
+
```rb
|
194
|
+
Rails.application.config.to_prepare do
|
195
|
+
AuthRails.configure do |config|
|
196
|
+
config.resource_class = User # required
|
197
|
+
config.identifier_name = :username # this one is sub in jwt
|
198
|
+
config.dig_params = ->(params) { params[:identifier] } # must be a proc, how to get identifier from params
|
199
|
+
|
200
|
+
# how to get user from identifier
|
201
|
+
# identifier default is params[<identifier_name>]
|
202
|
+
# or extract from dig_params
|
203
|
+
config.retrieve_resource = lambda { |identifier|
|
204
|
+
User.where(email: identifier)
|
205
|
+
.or(User.where(username: identifier))
|
206
|
+
.first
|
207
|
+
}
|
208
|
+
end
|
209
|
+
end
|
210
|
+
```
|
211
|
+
|
212
|
+
# Strategy list
|
213
|
+
|
214
|
+
- allowed_token
|
215
|
+
|
131
216
|
# License
|
132
217
|
|
133
218
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -4,9 +4,9 @@ module AuthRails
|
|
4
4
|
module Api
|
5
5
|
class AuthController < ApiController
|
6
6
|
def create
|
7
|
-
resource = AuthRails.
|
7
|
+
resource = AuthRails.retrieve_resource(params: params)
|
8
8
|
|
9
|
-
raise AuthRails.error_class, :unauthenticated if resource.blank? || !
|
9
|
+
raise AuthRails.error_class, :unauthenticated if resource.blank? || !AuthRails.authenticate(resource: resource, password: params[:password])
|
10
10
|
|
11
11
|
respond_to_create(generate_token(resource))
|
12
12
|
end
|
@@ -22,6 +22,11 @@ module AuthRails
|
|
22
22
|
|
23
23
|
raise AuthRails.error_class, :unauthenticated if resource.blank?
|
24
24
|
|
25
|
+
resource.allowed_tokens.find_by(
|
26
|
+
jti: decoded_payload[:jti],
|
27
|
+
aud: decoded_payload[:aud]
|
28
|
+
)&.destroy!
|
29
|
+
|
25
30
|
respond_to_refresh(generate_token(resource))
|
26
31
|
end
|
27
32
|
|
@@ -38,7 +43,7 @@ module AuthRails
|
|
38
43
|
|
39
44
|
def payload(resource)
|
40
45
|
{
|
41
|
-
sub: resource.
|
46
|
+
sub: resource.send(AuthRails.identifier_name)
|
42
47
|
}
|
43
48
|
end
|
44
49
|
|
@@ -10,7 +10,7 @@ module AuthRails
|
|
10
10
|
secret_key: Configuration::Jwt::AccessToken.secret_key
|
11
11
|
)
|
12
12
|
|
13
|
-
CurrentAuth.user = AuthRails.resource_class.find_by(
|
13
|
+
CurrentAuth.user = AuthRails.resource_class.find_by(AuthRails.identifier_name => payload[:sub])
|
14
14
|
|
15
15
|
raise AuthRails.error_class, :unauthenticated unless CurrentAuth.user
|
16
16
|
end
|
data/auth_rails.gemspec
CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
.git
|
29
29
|
.circleci
|
30
30
|
appveyor
|
31
|
+
examples/
|
31
32
|
Gemfile
|
32
33
|
.rubocop.yml
|
33
34
|
.vscode/settings.json
|
@@ -40,5 +41,5 @@ Gem::Specification.new do |spec|
|
|
40
41
|
|
41
42
|
spec.require_paths = ['lib']
|
42
43
|
|
43
|
-
spec.
|
44
|
+
spec.add_runtime_dependency 'jwt', '>= 2.7'
|
44
45
|
end
|
@@ -14,6 +14,10 @@ module AuthRails
|
|
14
14
|
@resource_class ||= Config.resource_class
|
15
15
|
end
|
16
16
|
|
17
|
+
def identifier_name
|
18
|
+
@identifier_name ||= Config.identifier_name.to_sym || :email
|
19
|
+
end
|
20
|
+
|
17
21
|
def error_class
|
18
22
|
@error_class ||= Config.error_class || Error
|
19
23
|
end
|
@@ -21,5 +25,45 @@ module AuthRails
|
|
21
25
|
def jwt_strategy
|
22
26
|
@jwt_strategy ||= Configuration::Jwt.strategy || Strategies::BaseStrategy
|
23
27
|
end
|
28
|
+
|
29
|
+
def authenticate(resource:, password:)
|
30
|
+
if Config.authenticate.present?
|
31
|
+
raise_if_not_proc(Config.authenticate, 'Config.authenticate')
|
32
|
+
|
33
|
+
Config.authenticate.call(resource, password)
|
34
|
+
else
|
35
|
+
raise 'Don\'t know how to authenticate resource with password' unless resource.respond_to?(:authenticate)
|
36
|
+
|
37
|
+
resource.authenticate(password)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def dig_params(params:)
|
42
|
+
if Config.dig_params.present?
|
43
|
+
raise_if_not_proc(Config.dig_params, 'Config.dig_params')
|
44
|
+
|
45
|
+
Config.dig_params.call(params)
|
46
|
+
else
|
47
|
+
params[AuthRails.identifier_name]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def retrieve_resource(params:)
|
52
|
+
identifier = dig_params(params: params)
|
53
|
+
|
54
|
+
if Config.retrieve_resource.present?
|
55
|
+
raise_if_not_proc(Config.retrieve_resource, 'Config.retrieve_resource')
|
56
|
+
|
57
|
+
return Config.retrieve_resource.call(identifier)
|
58
|
+
end
|
59
|
+
|
60
|
+
AuthRails.resource_class.find_by(AuthRails.identifier_name => identifier)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def raise_if_not_proc(source, name)
|
66
|
+
raise "#{name} must be a Proc" unless source.is_a?(Proc)
|
67
|
+
end
|
24
68
|
end
|
25
69
|
end
|
data/lib/auth_rails/config.rb
CHANGED
@@ -3,8 +3,12 @@
|
|
3
3
|
module AuthRails
|
4
4
|
class Config
|
5
5
|
class << self
|
6
|
-
attr_accessor :
|
7
|
-
:
|
6
|
+
attr_accessor :dig_params,
|
7
|
+
:error_class,
|
8
|
+
:authenticate,
|
9
|
+
:resource_class,
|
10
|
+
:identifier_name,
|
11
|
+
:retrieve_resource
|
8
12
|
|
9
13
|
def jwt
|
10
14
|
yield Configuration::Jwt
|
@@ -11,7 +11,7 @@ module AuthRails
|
|
11
11
|
.joins(:allowed_tokens)
|
12
12
|
.where(allowed_tokens: symbolized_payload.slice(:jti, :aud))
|
13
13
|
.where('allowed_tokens.exp > ?', Time.current)
|
14
|
-
.find_by(
|
14
|
+
.find_by(AuthRails.identifier_name => symbolized_payload[:sub])
|
15
15
|
end
|
16
16
|
|
17
17
|
def gen_token(resource:, payload:, exp: nil, secret_key: nil, algorithm: nil)
|
@@ -8,7 +8,7 @@ module AuthRails
|
|
8
8
|
symbolized_payload = payload.symbolize_keys
|
9
9
|
|
10
10
|
AuthRails.resource_class
|
11
|
-
.find_by(
|
11
|
+
.find_by(AuthRails.identifier_name => symbolized_payload[:sub])
|
12
12
|
end
|
13
13
|
|
14
14
|
def gen_token(payload:, exp: nil, secret_key: nil, algorithm: nil, jti: nil, **)
|
data/lib/auth_rails/version.rb
CHANGED
data/lib/auth_rails.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AuthRails
|
4
|
+
class MigrationGenerator < Rails::Generators::Base
|
5
|
+
include Rails::Generators::Migration
|
6
|
+
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
8
|
+
|
9
|
+
class_option :strategy,
|
10
|
+
aliases: '-strat',
|
11
|
+
type: :string,
|
12
|
+
desc: 'Strategy to use, default is AuthRails::Strategies::BaseStrategy',
|
13
|
+
default: 'base'
|
14
|
+
|
15
|
+
class_option :model,
|
16
|
+
aliases: '-m',
|
17
|
+
type: :string,
|
18
|
+
desc: 'Model for strategy to associate with',
|
19
|
+
default: 'user'
|
20
|
+
|
21
|
+
def create_migration_files
|
22
|
+
@model = (options[:model] || 'user').underscore.to_sym
|
23
|
+
|
24
|
+
case options[:strategy]
|
25
|
+
when 'allowed_token'
|
26
|
+
migration_template(
|
27
|
+
'allowed_tokens.tt',
|
28
|
+
'db/migrate/create_allowed_tokens.rb',
|
29
|
+
migration_version: migration_version
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def next_migration_number(dirname)
|
36
|
+
next_migration_number = current_migration_number(dirname) + 1
|
37
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def versioned_migrations?
|
44
|
+
Rails::VERSION::MAJOR >= 5
|
45
|
+
end
|
46
|
+
|
47
|
+
def migration_version
|
48
|
+
return unless versioned_migrations?
|
49
|
+
|
50
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateAllowedTokens < ActiveRecord::Migration<%= migration_version %>
|
4
|
+
def change
|
5
|
+
create_table :allowed_tokens do |t|
|
6
|
+
t.string :jti, null: false
|
7
|
+
t.string :aud
|
8
|
+
t.datetime :exp, null: false
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
|
12
|
+
t.references :<%= @model %>, foreign_key: { on_delete: :cascade }, null: false
|
13
|
+
|
14
|
+
t.index %i[jti aud]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class AuthRailsGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
5
|
+
|
6
|
+
class_option :strategy,
|
7
|
+
aliases: '-strat',
|
8
|
+
type: :string,
|
9
|
+
desc: 'Strategy to use, default is AuthRails::Strategies::BaseStrategy',
|
10
|
+
default: 'base'
|
11
|
+
|
12
|
+
class_option :model,
|
13
|
+
aliases: '-m',
|
14
|
+
type: :string,
|
15
|
+
desc: 'Model for strategy to associate with',
|
16
|
+
default: 'user'
|
17
|
+
|
18
|
+
def generate_auth_rails
|
19
|
+
@model = (options[:model] || 'user').camelcase
|
20
|
+
@is_allowed_token = options[:strategy] == 'allowed_token'
|
21
|
+
|
22
|
+
template(
|
23
|
+
'auth_rails.tt',
|
24
|
+
'config/initializers/auth_rails.rb'
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_allowed_tokens_strategy
|
29
|
+
return if options[:strategy].blank? || options[:strategy] != 'allowed_token'
|
30
|
+
|
31
|
+
invoke(
|
32
|
+
'auth_rails:migration',
|
33
|
+
[],
|
34
|
+
strategy: 'allowed_token',
|
35
|
+
model: (options[:model] || 'user').camelcase
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
AuthRails.configure do |config|
|
4
|
+
config.jwt do |jwt|
|
5
|
+
jwt.access_token do |access_token|
|
6
|
+
access_token.exp = 1.hour.since
|
7
|
+
access_token.secret_key = ENV.fetch('JWT_SECRET', '')
|
8
|
+
end
|
9
|
+
|
10
|
+
<%= @is_allowed_token ? '' : '# ' %>jwt.strategy = AuthRails::Strategies::AllowedTokenStrategy
|
11
|
+
|
12
|
+
# if you wanna use refresh token
|
13
|
+
# uncomment those lines below
|
14
|
+
# jwt.refresh_token do |refresh_token|
|
15
|
+
# refresh_token.http_only = true
|
16
|
+
# refresh_token.exp = 1.year.since
|
17
|
+
# refresh_token.algorithm = 'HS256'
|
18
|
+
# refresh_token.cookie_key = :ref_tok
|
19
|
+
# refresh_token.secret_key = ENV.fetch('JWT_SECRET', '')
|
20
|
+
# end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Rails.application.config.to_prepare do
|
25
|
+
AuthRails.configure do |config|
|
26
|
+
config.resource_class = <%= @model %>
|
27
|
+
|
28
|
+
# if you wanna use custom error classes
|
29
|
+
# uncomment code below
|
30
|
+
# config.error_class = AuthError
|
31
|
+
end
|
32
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: auth_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alpha
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jwt
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.7'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.7'
|
27
27
|
description: Simple authentication for Rails
|
28
28
|
email:
|
29
29
|
- alphanolucifer@gmail.com
|
@@ -47,6 +47,10 @@ files:
|
|
47
47
|
- lib/auth_rails/strategies/allowed_token_strategy.rb
|
48
48
|
- lib/auth_rails/strategies/base_strategy.rb
|
49
49
|
- lib/auth_rails/version.rb
|
50
|
+
- lib/generators/auth_rails/migration_generator.rb
|
51
|
+
- lib/generators/auth_rails/templates/allowed_tokens.tt
|
52
|
+
- lib/generators/auth_rails_generator.rb
|
53
|
+
- lib/generators/templates/auth_rails.tt
|
50
54
|
homepage: https://github.com/zgid123/auth_rails
|
51
55
|
licenses:
|
52
56
|
- MIT
|