auth_rails 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|