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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b6047449dbddff4af60565f7c096eae14f8800b47bd129dceb3ea7f90fb42f7
4
- data.tar.gz: 80776e48b7c557c9ab11ec1e9b05ae06e31bc15cca56a4143bfbf46254d2a109
3
+ metadata.gz: c69df81c88cacdf202e2c49cf329423ded23e509fc2dc67fd60d07fda1fbe12e
4
+ data.tar.gz: abbad690e4211d181950af7bf542e2df367b79c0c79c049f32c5192188106f4f
5
5
  SHA512:
6
- metadata.gz: 03502ea907678c9ca3122701789e82a145f9c3a143d415153bbde798ad7b5a375cc464ba1e1bf4d8a583d42b298501c00fcf00aadbc7b2be221b8b2044a149fe
7
- data.tar.gz: f90cfeab74aa249aebbe6a33deec189901e90c74bfdc0092d42b18a468a3e3eee3fe806611b24accf61a4c1ffc9fc0d1034b4a35cc4bb460fa94b5008790366f
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.resource_class.find_by(email: params[:email])
7
+ resource = AuthRails.retrieve_resource(params: params)
8
8
 
9
- raise AuthRails.error_class, :unauthenticated if resource.blank? || !resource.authenticate(params[:password])
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.email
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(email: payload[:sub])
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.add_dependency 'jwt'
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
@@ -3,8 +3,12 @@
3
3
  module AuthRails
4
4
  class Config
5
5
  class << self
6
- attr_accessor :error_class,
7
- :resource_class
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(email: symbolized_payload[:sub])
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(email: symbolized_payload[:sub])
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, **)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AuthRails
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'
5
5
  end
data/lib/auth_rails.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'jwt'
4
+
3
5
  require_relative 'auth_rails/config'
4
6
  require_relative 'auth_rails/version'
5
7
  require_relative 'auth_rails/class_methods'
@@ -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.0.0
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: 2023-12-25 00:00:00.000000000 Z
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: '0'
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: '0'
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