custos 0.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 +7 -0
- data/CHANGELOG.md +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +117 -0
- data/lib/custos/authenticatable.rb +30 -0
- data/lib/custos/callback_registry.rb +37 -0
- data/lib/custos/configuration.rb +35 -0
- data/lib/custos/controller_helpers.rb +47 -0
- data/lib/custos/mfa_encryptor.rb +63 -0
- data/lib/custos/model_config.rb +36 -0
- data/lib/custos/models/api_token.rb +23 -0
- data/lib/custos/models/magic_link_token.rb +13 -0
- data/lib/custos/models/mfa_credential.rb +22 -0
- data/lib/custos/models/remember_token.rb +11 -0
- data/lib/custos/plugin.rb +30 -0
- data/lib/custos/plugins/api_tokens.rb +48 -0
- data/lib/custos/plugins/email_confirmation.rb +59 -0
- data/lib/custos/plugins/lockout.rb +116 -0
- data/lib/custos/plugins/magic_link.rb +60 -0
- data/lib/custos/plugins/mfa.rb +185 -0
- data/lib/custos/plugins/password.rb +98 -0
- data/lib/custos/plugins/remember_me.rb +56 -0
- data/lib/custos/railtie.rb +15 -0
- data/lib/custos/session.rb +16 -0
- data/lib/custos/session_manager.rb +51 -0
- data/lib/custos/tasks/cleanup.rake +28 -0
- data/lib/custos/token_generator.rb +37 -0
- data/lib/custos/version.rb +5 -0
- data/lib/custos.rb +57 -0
- data/lib/generators/custos/install/install_generator.rb +23 -0
- data/lib/generators/custos/install/templates/create_custos_sessions.rb.tt +19 -0
- data/lib/generators/custos/install/templates/custos_initializer.rb.tt +12 -0
- data/lib/generators/custos/model/model_generator.rb +89 -0
- data/lib/generators/custos/model/templates/add_custos_columns.rb.tt +18 -0
- data/lib/generators/custos/model/templates/create_custos_api_tokens.rb.tt +18 -0
- data/lib/generators/custos/model/templates/create_custos_magic_links.rb.tt +17 -0
- data/lib/generators/custos/model/templates/create_custos_mfa_credentials.rb.tt +16 -0
- data/lib/generators/custos/model/templates/create_custos_remember_tokens.rb.tt +16 -0
- metadata +129 -0
data/lib/custos.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support'
|
|
4
|
+
require 'active_record'
|
|
5
|
+
|
|
6
|
+
require 'custos/version'
|
|
7
|
+
require 'custos/configuration'
|
|
8
|
+
require 'custos/plugin'
|
|
9
|
+
require 'custos/callback_registry'
|
|
10
|
+
require 'custos/token_generator'
|
|
11
|
+
require 'custos/authenticatable'
|
|
12
|
+
require 'custos/model_config'
|
|
13
|
+
require 'custos/session'
|
|
14
|
+
require 'custos/session_manager'
|
|
15
|
+
require 'custos/controller_helpers'
|
|
16
|
+
require 'custos/mfa_encryptor'
|
|
17
|
+
|
|
18
|
+
require 'custos/models/magic_link_token'
|
|
19
|
+
require 'custos/models/api_token'
|
|
20
|
+
require 'custos/models/mfa_credential'
|
|
21
|
+
require 'custos/models/remember_token'
|
|
22
|
+
|
|
23
|
+
require 'custos/plugins/password'
|
|
24
|
+
require 'custos/plugins/magic_link'
|
|
25
|
+
require 'custos/plugins/api_tokens'
|
|
26
|
+
require 'custos/plugins/mfa'
|
|
27
|
+
require 'custos/plugins/lockout'
|
|
28
|
+
require 'custos/plugins/email_confirmation'
|
|
29
|
+
require 'custos/plugins/remember_me'
|
|
30
|
+
|
|
31
|
+
require 'custos/railtie' if defined?(Rails::Railtie)
|
|
32
|
+
|
|
33
|
+
module Custos
|
|
34
|
+
class Error < StandardError; end
|
|
35
|
+
class UnknownPluginError < Error; end
|
|
36
|
+
class NotAuthenticatedError < Error; end
|
|
37
|
+
class DecryptionError < Error; end
|
|
38
|
+
class TokenExpiredError < Error; end
|
|
39
|
+
class TokenInvalidError < Error; end
|
|
40
|
+
class AccountLockedError < Error; end
|
|
41
|
+
|
|
42
|
+
@mutex = Mutex.new
|
|
43
|
+
|
|
44
|
+
class << self
|
|
45
|
+
def configure
|
|
46
|
+
yield configuration
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def configuration
|
|
50
|
+
@mutex.synchronize { @configuration ||= Configuration.new }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def reset_configuration!
|
|
54
|
+
@mutex.synchronize { @configuration = Configuration.new }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
require 'rails/generators/active_record'
|
|
5
|
+
|
|
6
|
+
module Custos
|
|
7
|
+
module Generators
|
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
|
9
|
+
include ActiveRecord::Generators::Migration
|
|
10
|
+
|
|
11
|
+
source_root File.expand_path('templates', __dir__)
|
|
12
|
+
|
|
13
|
+
def create_initializer
|
|
14
|
+
template 'custos_initializer.rb.tt', 'config/initializers/custos.rb'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create_sessions_migration
|
|
18
|
+
migration_template 'create_custos_sessions.rb.tt',
|
|
19
|
+
'db/migrate/create_custos_sessions.rb'
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class CreateCustosSessions < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :custos_sessions do |t|
|
|
4
|
+
t.string :authenticatable_type, null: false
|
|
5
|
+
t.bigint :authenticatable_id, null: false
|
|
6
|
+
t.string :session_token_digest, null: false
|
|
7
|
+
t.string :ip_address
|
|
8
|
+
t.string :user_agent
|
|
9
|
+
t.datetime :last_active_at
|
|
10
|
+
t.datetime :revoked_at
|
|
11
|
+
|
|
12
|
+
t.timestamps
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index :custos_sessions, :session_token_digest, unique: true
|
|
16
|
+
add_index :custos_sessions, %i[authenticatable_type authenticatable_id],
|
|
17
|
+
name: "index_custos_sessions_on_authenticatable"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Custos.configure do |config|
|
|
4
|
+
# Session expiry in seconds (default: 24 hours)
|
|
5
|
+
# config.session_expiry = 24 * 60 * 60
|
|
6
|
+
|
|
7
|
+
# Session renewal interval in seconds (default: 1 hour)
|
|
8
|
+
# config.session_renewal_interval = 60 * 60
|
|
9
|
+
|
|
10
|
+
# Token length in bytes (default: 32)
|
|
11
|
+
# config.token_length = 32
|
|
12
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
require 'rails/generators/active_record'
|
|
5
|
+
|
|
6
|
+
module Custos
|
|
7
|
+
module Generators
|
|
8
|
+
class ModelGenerator < Rails::Generators::Base
|
|
9
|
+
include ActiveRecord::Generators::Migration
|
|
10
|
+
|
|
11
|
+
KNOWN_PLUGINS = %w[password magic_link api_tokens mfa lockout email_confirmation remember_me].freeze
|
|
12
|
+
|
|
13
|
+
source_root File.expand_path('templates', __dir__)
|
|
14
|
+
|
|
15
|
+
argument :model_name, type: :string, desc: 'Model name (e.g. User)'
|
|
16
|
+
argument :plugins, type: :array, default: [], banner: 'plugin1 plugin2'
|
|
17
|
+
|
|
18
|
+
def validate_plugins
|
|
19
|
+
unknown = plugins.map(&:to_s) - KNOWN_PLUGINS
|
|
20
|
+
return if unknown.empty?
|
|
21
|
+
|
|
22
|
+
raise Thor::Error, "Unknown plugin(s): #{unknown.join(', ')}. " \
|
|
23
|
+
"Available: #{KNOWN_PLUGINS.join(', ')}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_column_migration
|
|
27
|
+
columns = column_plugins & plugins.map(&:to_s)
|
|
28
|
+
return if columns.empty?
|
|
29
|
+
|
|
30
|
+
migration_template 'add_custos_columns.rb.tt',
|
|
31
|
+
"db/migrate/add_custos_to_#{table_name}.rb"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def create_magic_links_migration
|
|
35
|
+
return unless plugins.include?('magic_link')
|
|
36
|
+
|
|
37
|
+
migration_template 'create_custos_magic_links.rb.tt',
|
|
38
|
+
'db/migrate/create_custos_magic_links.rb'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def create_api_tokens_migration
|
|
42
|
+
return unless plugins.include?('api_tokens')
|
|
43
|
+
|
|
44
|
+
migration_template 'create_custos_api_tokens.rb.tt',
|
|
45
|
+
'db/migrate/create_custos_api_tokens.rb'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def create_mfa_credentials_migration
|
|
49
|
+
return unless plugins.include?('mfa')
|
|
50
|
+
|
|
51
|
+
migration_template 'create_custos_mfa_credentials.rb.tt',
|
|
52
|
+
'db/migrate/create_custos_mfa_credentials.rb'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def create_remember_tokens_migration
|
|
56
|
+
return unless plugins.include?('remember_me')
|
|
57
|
+
|
|
58
|
+
migration_template 'create_custos_remember_tokens.rb.tt',
|
|
59
|
+
'db/migrate/create_custos_remember_tokens.rb'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def table_name
|
|
65
|
+
model_name.underscore.tr('/', '_').pluralize
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def migration_class_suffix
|
|
69
|
+
model_name.camelize.delete(':').pluralize
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def column_plugins
|
|
73
|
+
%w[password lockout email_confirmation]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def has_password?
|
|
77
|
+
plugins.include?('password')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def has_lockout?
|
|
81
|
+
plugins.include?('lockout')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def has_email_confirmation?
|
|
85
|
+
plugins.include?('email_confirmation')
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class AddCustosTo<%= migration_class_suffix %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
<%- if has_password? -%>
|
|
4
|
+
add_column :<%= table_name %>, :password_digest, :string
|
|
5
|
+
<%- end -%>
|
|
6
|
+
<%- if has_lockout? -%>
|
|
7
|
+
add_column :<%= table_name %>, :failed_auth_count, :integer, default: 0, null: false
|
|
8
|
+
add_column :<%= table_name %>, :locked_at, :datetime
|
|
9
|
+
add_column :<%= table_name %>, :failed_mfa_count, :integer, default: 0, null: false
|
|
10
|
+
add_column :<%= table_name %>, :mfa_locked_at, :datetime
|
|
11
|
+
<%- end -%>
|
|
12
|
+
<%- if has_email_confirmation? -%>
|
|
13
|
+
add_column :<%= table_name %>, :email_confirmed_at, :datetime
|
|
14
|
+
add_column :<%= table_name %>, :email_confirmation_token_digest, :string
|
|
15
|
+
add_column :<%= table_name %>, :email_confirmation_sent_at, :datetime
|
|
16
|
+
<%- end -%>
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class CreateCustosApiTokens < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :custos_api_tokens do |t|
|
|
4
|
+
t.string :authenticatable_type, null: false
|
|
5
|
+
t.bigint :authenticatable_id, null: false
|
|
6
|
+
t.string :token_digest, null: false
|
|
7
|
+
t.datetime :last_used_at
|
|
8
|
+
t.datetime :expires_at
|
|
9
|
+
t.datetime :revoked_at
|
|
10
|
+
|
|
11
|
+
t.timestamps
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
add_index :custos_api_tokens, :token_digest, unique: true
|
|
15
|
+
add_index :custos_api_tokens, %i[authenticatable_type authenticatable_id],
|
|
16
|
+
name: "index_custos_api_tokens_on_authenticatable"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class CreateCustosMagicLinks < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :custos_magic_links do |t|
|
|
4
|
+
t.string :authenticatable_type, null: false
|
|
5
|
+
t.bigint :authenticatable_id, null: false
|
|
6
|
+
t.string :token_digest, null: false
|
|
7
|
+
t.datetime :expires_at, null: false
|
|
8
|
+
t.datetime :used_at
|
|
9
|
+
|
|
10
|
+
t.datetime :created_at, null: false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_index :custos_magic_links, :token_digest, unique: true
|
|
14
|
+
add_index :custos_magic_links, %i[authenticatable_type authenticatable_id],
|
|
15
|
+
name: "index_custos_magic_links_on_authenticatable"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CreateCustosMfaCredentials < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :custos_mfa_credentials do |t|
|
|
4
|
+
t.string :authenticatable_type, null: false
|
|
5
|
+
t.bigint :authenticatable_id, null: false
|
|
6
|
+
t.string :method, null: false
|
|
7
|
+
t.text :secret_data, null: false
|
|
8
|
+
t.datetime :enabled_at
|
|
9
|
+
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_index :custos_mfa_credentials, %i[authenticatable_type authenticatable_id method],
|
|
14
|
+
unique: true, name: "index_custos_mfa_on_authenticatable_and_method"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CreateCustosRememberTokens < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :custos_remember_tokens do |t|
|
|
4
|
+
t.string :authenticatable_type, null: false
|
|
5
|
+
t.bigint :authenticatable_id, null: false
|
|
6
|
+
t.string :token_digest, null: false
|
|
7
|
+
t.datetime :expires_at, null: false
|
|
8
|
+
|
|
9
|
+
t.datetime :created_at, null: false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
add_index :custos_remember_tokens, :token_digest, unique: true
|
|
13
|
+
add_index :custos_remember_tokens, %i[authenticatable_type authenticatable_id],
|
|
14
|
+
name: "index_custos_remember_tokens_on_authenticatable"
|
|
15
|
+
end
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: custos
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ingvar
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-03-16 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: argon2
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rails
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '7.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '7.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rotp
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '6.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '6.0'
|
|
55
|
+
description: Modern, modular authentication for Rails applications. Supports password,
|
|
56
|
+
magic link, API tokens, MFA, and more — all as composable plugins.
|
|
57
|
+
email:
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- CHANGELOG.md
|
|
63
|
+
- LICENSE.txt
|
|
64
|
+
- README.md
|
|
65
|
+
- lib/custos.rb
|
|
66
|
+
- lib/custos/authenticatable.rb
|
|
67
|
+
- lib/custos/callback_registry.rb
|
|
68
|
+
- lib/custos/configuration.rb
|
|
69
|
+
- lib/custos/controller_helpers.rb
|
|
70
|
+
- lib/custos/mfa_encryptor.rb
|
|
71
|
+
- lib/custos/model_config.rb
|
|
72
|
+
- lib/custos/models/api_token.rb
|
|
73
|
+
- lib/custos/models/magic_link_token.rb
|
|
74
|
+
- lib/custos/models/mfa_credential.rb
|
|
75
|
+
- lib/custos/models/remember_token.rb
|
|
76
|
+
- lib/custos/plugin.rb
|
|
77
|
+
- lib/custos/plugins/api_tokens.rb
|
|
78
|
+
- lib/custos/plugins/email_confirmation.rb
|
|
79
|
+
- lib/custos/plugins/lockout.rb
|
|
80
|
+
- lib/custos/plugins/magic_link.rb
|
|
81
|
+
- lib/custos/plugins/mfa.rb
|
|
82
|
+
- lib/custos/plugins/password.rb
|
|
83
|
+
- lib/custos/plugins/remember_me.rb
|
|
84
|
+
- lib/custos/railtie.rb
|
|
85
|
+
- lib/custos/session.rb
|
|
86
|
+
- lib/custos/session_manager.rb
|
|
87
|
+
- lib/custos/tasks/cleanup.rake
|
|
88
|
+
- lib/custos/token_generator.rb
|
|
89
|
+
- lib/custos/version.rb
|
|
90
|
+
- lib/generators/custos/install/install_generator.rb
|
|
91
|
+
- lib/generators/custos/install/templates/create_custos_sessions.rb.tt
|
|
92
|
+
- lib/generators/custos/install/templates/custos_initializer.rb.tt
|
|
93
|
+
- lib/generators/custos/model/model_generator.rb
|
|
94
|
+
- lib/generators/custos/model/templates/add_custos_columns.rb.tt
|
|
95
|
+
- lib/generators/custos/model/templates/create_custos_api_tokens.rb.tt
|
|
96
|
+
- lib/generators/custos/model/templates/create_custos_magic_links.rb.tt
|
|
97
|
+
- lib/generators/custos/model/templates/create_custos_mfa_credentials.rb.tt
|
|
98
|
+
- lib/generators/custos/model/templates/create_custos_remember_tokens.rb.tt
|
|
99
|
+
homepage: https://github.com/supostat/Custos
|
|
100
|
+
licenses:
|
|
101
|
+
- MIT
|
|
102
|
+
metadata:
|
|
103
|
+
homepage_uri: https://github.com/supostat/Custos
|
|
104
|
+
source_code_uri: https://github.com/supostat/Custos
|
|
105
|
+
changelog_uri: https://github.com/supostat/Custos/blob/main/gem/CHANGELOG.md
|
|
106
|
+
documentation_uri: https://github.com/supostat/Custos
|
|
107
|
+
bug_tracker_uri: https://github.com/supostat/Custos/issues
|
|
108
|
+
rubygems_mfa_required: 'true'
|
|
109
|
+
allowed_push_host: https://rubygems.org
|
|
110
|
+
post_install_message:
|
|
111
|
+
rdoc_options: []
|
|
112
|
+
require_paths:
|
|
113
|
+
- lib
|
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - ">="
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '3.2'
|
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
124
|
+
requirements: []
|
|
125
|
+
rubygems_version: 3.4.19
|
|
126
|
+
signing_key:
|
|
127
|
+
specification_version: 4
|
|
128
|
+
summary: Plugin-based authentication for Rails
|
|
129
|
+
test_files: []
|