standard_id 0.1.0 → 0.1.2

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +347 -7
  3. data/app/controllers/standard_id/api/providers_controller.rb +15 -16
  4. data/app/controllers/standard_id/web/verify_email/base_controller.rb +9 -0
  5. data/app/controllers/standard_id/web/verify_email/confirm_controller.rb +41 -0
  6. data/app/controllers/standard_id/web/verify_email/start_controller.rb +39 -0
  7. data/app/controllers/standard_id/web/verify_phone/base_controller.rb +9 -0
  8. data/app/controllers/standard_id/web/verify_phone/confirm_controller.rb +41 -0
  9. data/app/controllers/standard_id/web/verify_phone/start_controller.rb +39 -0
  10. data/app/forms/standard_id/web/signup_form.rb +3 -14
  11. data/app/models/standard_id/client_application.rb +2 -1
  12. data/app/models/standard_id/{passwordless_challenge.rb → code_challenge.rb} +9 -5
  13. data/app/models/standard_id/email_identifier.rb +2 -0
  14. data/app/models/standard_id/identifier.rb +17 -0
  15. data/app/models/standard_id/phone_number_identifier.rb +2 -0
  16. data/app/models/standard_id/username_identifier.rb +2 -0
  17. data/config/routes/web.rb +12 -0
  18. data/db/migrate/20250830000000_create_standard_id_client_applications.rb +3 -3
  19. data/db/migrate/20250830232800_create_standard_id_identifiers.rb +1 -1
  20. data/db/migrate/20250831075703_create_standard_id_credentials.rb +2 -2
  21. data/db/migrate/20250831154635_create_standard_id_sessions.rb +2 -2
  22. data/db/migrate/20250901134520_create_standard_id_client_secret_credentials.rb +2 -2
  23. data/db/migrate/20250907090000_create_standard_id_code_challenges.rb +29 -0
  24. data/lib/generators/standard_id/install/templates/standard_id.rb +36 -3
  25. data/lib/{standard_id → standard_config}/config.rb +1 -1
  26. data/lib/standard_config/config_provider.rb +82 -0
  27. data/lib/standard_config/manager.rb +86 -0
  28. data/lib/standard_config/schema.rb +137 -0
  29. data/lib/standard_config.rb +38 -0
  30. data/lib/standard_id/api/session_manager.rb +1 -1
  31. data/lib/standard_id/config/schema.rb +49 -0
  32. data/lib/standard_id/oauth/client_credentials_flow.rb +1 -5
  33. data/lib/standard_id/oauth/implicit_authorization_flow.rb +1 -1
  34. data/lib/standard_id/oauth/password_flow.rb +0 -4
  35. data/lib/standard_id/oauth/passwordless_otp_flow.rb +7 -10
  36. data/lib/standard_id/oauth/token_grant_flow.rb +6 -2
  37. data/lib/standard_id/oauth/token_lifetime_resolver.rb +50 -0
  38. data/lib/standard_id/passwordless/base_strategy.rb +8 -4
  39. data/lib/standard_id/version.rb +1 -1
  40. data/lib/standard_id.rb +13 -4
  41. metadata +16 -4
  42. data/db/migrate/20250903135906_create_standard_id_passwordless_challenges.rb +0 -22
@@ -10,6 +10,8 @@ module StandardId
10
10
  # Shared validations
11
11
  validates :value, presence: true, uniqueness: { scope: [:account_id, :type] }
12
12
 
13
+ after_commit :mark_account_verified!, on: :update, if: :just_verified?
14
+
13
15
  def verified?
14
16
  verified_at.present?
15
17
  end
@@ -21,5 +23,20 @@ module StandardId
21
23
  def unverify!
22
24
  update!(verified_at: nil)
23
25
  end
26
+
27
+ private
28
+
29
+ def just_verified?
30
+ saved_change_to_verified_at? && verified_at.present?
31
+ end
32
+
33
+ def mark_account_verified!
34
+ return if account.nil?
35
+
36
+ return unless account.has_attribute?(:verified)
37
+ return unless account.has_attribute?(:verified_at)
38
+
39
+ account.update!(verified: true, verified_at: Time.current)
40
+ end
24
41
  end
25
42
  end
@@ -1,5 +1,7 @@
1
1
  module StandardId
2
2
  class PhoneNumberIdentifier < Identifier
3
+ normalizes :value, with: ->(p) { p.strip }
4
+
3
5
  validates :value, format: { with: /\A\+?[1-9]\d{1,14}\z/ }
4
6
  end
5
7
  end
@@ -1,5 +1,7 @@
1
1
  module StandardId
2
2
  class UsernameIdentifier < Identifier
3
+ normalizes :value, with: ->(u) { u.strip }
4
+
3
5
  validates :value, format: { with: /\A[a-zA-Z0-9_]+\z/ }
4
6
  end
5
7
  end
data/config/routes/web.rb CHANGED
@@ -19,6 +19,18 @@ StandardId::WebEngine.routes.draw do
19
19
  resource :confirm, only: [:show, :update], controller: :confirm
20
20
  end
21
21
 
22
+ # Identifier verification (email)
23
+ namespace :verify_email do
24
+ resource :start, only: [:show, :create], controller: :start
25
+ resource :confirm, only: [:show, :update], controller: :confirm
26
+ end
27
+
28
+ # Identifier verification (phone)
29
+ namespace :verify_phone do
30
+ resource :start, only: [:show, :create], controller: :start
31
+ resource :confirm, only: [:show, :update], controller: :confirm
32
+ end
33
+
22
34
  # Account management
23
35
  resource :account, only: [:show, :edit, :update], controller: :account
24
36
  resources :sessions, only: [:index, :destroy], controller: :sessions
@@ -1,8 +1,8 @@
1
1
  class CreateStandardIdClientApplications < ActiveRecord::Migration[7.1]
2
2
  def change
3
- create_table :standard_id_client_applications do |t|
3
+ create_table :standard_id_client_applications, id: primary_key_type do |t|
4
4
  # Polymorphic owner association (Account, Organization, etc.)
5
- t.references :owner, null: false, polymorphic: true, index: true
5
+ t.references :owner, type: primary_key_type, null: false, polymorphic: true, index: true
6
6
 
7
7
  # Basic client information
8
8
  t.string :name, null: false
@@ -50,7 +50,7 @@ class CreateStandardIdClientApplications < ActiveRecord::Migration[7.1]
50
50
  end
51
51
 
52
52
  if connection.adapter_name.downcase.include?("postgres")
53
- add_index :standard_id_clients, :metadata, using: :gin
53
+ add_index :standard_id_client_applications, :metadata, if_not_exists: true, using: :gin
54
54
  end
55
55
  end
56
56
  end
@@ -1,7 +1,7 @@
1
1
  class CreateStandardIdIdentifiers < ActiveRecord::Migration[8.0]
2
2
  def change
3
3
  create_table :standard_id_identifiers, id: primary_key_type do |t|
4
- t.references :account, null: false, foreign_key: { to_table: StandardId.config.account_class.table_name }, index: true
4
+ t.references :account, type: primary_key_type, null: false, foreign_key: { to_table: StandardId.account_class.table_name }, index: true
5
5
 
6
6
  t.string :type, null: false
7
7
 
@@ -1,8 +1,8 @@
1
1
  class CreateStandardIdCredentials < ActiveRecord::Migration[8.0]
2
2
  def change
3
3
  create_table :standard_id_credentials, id: primary_key_type do |t|
4
- t.references :identifier, null: false, foreign_key: { to_table: :standard_id_identifiers }, index: true
5
- t.references :credentialable, null: false, polymorphic: true, index: true
4
+ t.references :identifier, type: primary_key_type, null: false, foreign_key: { to_table: :standard_id_identifiers }, index: true
5
+ t.references :credentialable, type: primary_key_type, null: false, polymorphic: true, index: true
6
6
 
7
7
  t.timestamps
8
8
  end
@@ -1,7 +1,7 @@
1
1
  class CreateStandardIdSessions < ActiveRecord::Migration[8.0]
2
2
  def change
3
3
  create_table :standard_id_sessions, id: primary_key_type do |t|
4
- t.references :account, null: false, foreign_key: true, index: true
4
+ t.references :account, type: primary_key_type, null: false, foreign_key: true, index: true
5
5
 
6
6
  # STI type column
7
7
  t.string :type, null: false, index: true
@@ -29,7 +29,7 @@ class CreateStandardIdSessions < ActiveRecord::Migration[8.0]
29
29
  t.datetime :last_refreshed_at
30
30
 
31
31
  # ServiceSession columns
32
- t.references :owner, polymorphic: true, null: true, index: true
32
+ t.references :owner, type: primary_key_type, polymorphic: true, null: true, index: true
33
33
  t.string :service_name
34
34
  t.string :service_version
35
35
 
@@ -1,9 +1,9 @@
1
1
  class CreateStandardIdClientSecretCredentials < ActiveRecord::Migration[7.1]
2
2
  def change
3
- create_table :standard_id_client_secret_credentials do |t|
3
+ create_table :standard_id_client_secret_credentials, id: primary_key_type do |t|
4
4
  t.string :name, null: false
5
5
 
6
- t.references :client_application, null: false, foreign_key: { to_table: :standard_id_client_applications }, index: true
6
+ t.references :client_application, type: primary_key_type, null: false, foreign_key: { to_table: :standard_id_client_applications }, index: true
7
7
 
8
8
  t.string :client_id, null: false, index: true # Denormalized for performance
9
9
  t.string :client_secret_digest, null: false
@@ -0,0 +1,29 @@
1
+ class CreateStandardIdCodeChallenges < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :standard_id_code_challenges, id: primary_key_type do |t|
4
+ t.string :realm, null: false # e.g., authentication, verification
5
+ t.string :channel, null: false # e.g., email, sms
6
+ t.string :target, null: false # recipient address (email/phone), normalized by caller
7
+ t.string :code, null: false
8
+
9
+ t.datetime :expires_at, null: false
10
+ t.datetime :used_at
11
+
12
+ t.string :ip_address
13
+ t.text :user_agent
14
+
15
+ if connection.adapter_name.downcase.include?("postgres")
16
+ t.jsonb :metadata, default: {}, null: false
17
+ t.index :metadata, using: :gin
18
+ else
19
+ t.json :metadata, default: {}, null: false
20
+ end
21
+
22
+ t.timestamps
23
+ end
24
+
25
+ add_index :standard_id_code_challenges, [:realm, :channel, :target, :code], name: "index_code_challenges_on_lookup"
26
+ add_index :standard_id_code_challenges, :expires_at
27
+ add_index :standard_id_code_challenges, :used_at
28
+ end
29
+ end
@@ -2,10 +2,43 @@
2
2
  # Generated by: rails g standard_id:install
3
3
 
4
4
  StandardId.configure do |c|
5
- # Set to the String name of your account model (e.g., "User" or "Account")
5
+ # Base configuration (account model, infrastructure settings)
6
6
  c.account_class_name = "User"
7
-
8
- # Optional: customize cache store and logger used internally by StandardId
9
7
  # c.cache_store = Rails.cache
10
8
  # c.logger = Rails.logger
9
+ # c.web_layout = "application"
10
+ # c.passwordless_email_sender = ->(email, code) { PasswordlessMailer.with(code: code, to: email).deliver_later }
11
+ # c.passwordless_sms_sender = ->(phone, code) { SmsProvider.send_code(phone: phone, code: code) }
12
+
13
+ # Configuration subsets (creates static OpenStruct objects)
14
+ # c.passwordless.code_ttl = 600 # 10 minutes
15
+ # c.passwordless.max_attempts = 3
16
+ # c.password.minimum_length = 8
17
+ # c.password.require_special_chars = true
18
+ # c.oauth.default_token_lifetime = 3600 # 1 hour
19
+ # c.oauth.refresh_token_lifetime = 2_592_000 # 30 days
20
+ # c.oauth.token_lifetimes = {
21
+ # password: 8.hours,
22
+ # client_credentials: 24.hours
23
+ # }
24
+
25
+ # Social login credentials (if enabled in your app)
26
+ # c.social.google_client_id = ENV["GOOGLE_CLIENT_ID"]
27
+ # c.social.google_client_secret = ENV["GOOGLE_CLIENT_SECRET"]
28
+ # c.social.apple_client_id = ENV["APPLE_CLIENT_ID"]
29
+ # c.social.apple_private_key = ENV["APPLE_PRIVATE_KEY"]
30
+ # c.social.apple_key_id = ENV["APPLE_KEY_ID"]
31
+ # c.social.apple_team_id = ENV["APPLE_TEAM_ID"]
32
+
33
+ # OIDC Logout allow list
34
+ # c.allowed_post_logout_redirect_uris = [
35
+ # "https://app.example.com/logged_out",
36
+ # "https://admin.example.com/signout"
37
+ # ]
11
38
  end
39
+
40
+ # Notes:
41
+ # - All configuration scopes and fields are defined via the schema DSL
42
+ # in: lib/standard_id/config/schema.rb
43
+ # - Values are automatically cast to the correct type (string, integer, boolean, etc.)
44
+ # - Invalid scopes or fields will raise ArgumentError with helpful messages
@@ -1,4 +1,4 @@
1
- module StandardId
1
+ module StandardConfig
2
2
  # Manages configuration for the StandardId engine
3
3
  #
4
4
  # Usage:
@@ -0,0 +1,82 @@
1
+ require "ostruct"
2
+
3
+ module StandardConfig
4
+ class ConfigProvider
5
+ def initialize(scope_name, resolver_proc, schema = nil)
6
+ @scope_name = scope_name
7
+ @resolver_proc = resolver_proc
8
+ @schema = schema
9
+ end
10
+
11
+ def method_missing(method_name, *args)
12
+ if method_name.to_s.end_with?('=')
13
+ # Setter - only works for static configs (OpenStruct objects)
14
+ field_name = method_name.to_s.chomp('=').to_sym
15
+ validate_field!(field_name)
16
+
17
+ config_object = @resolver_proc.call
18
+ if config_object.respond_to?(method_name)
19
+ config_object.send(method_name, args.first)
20
+ elsif config_object.respond_to?(:[]=)
21
+ # Support hash-like providers
22
+ value = args.first
23
+ config_object[field_name] = value
24
+ # Also set string key for convenience if symbol not used by provider
25
+ begin
26
+ config_object[field_name.to_s] = value
27
+ rescue StandardError
28
+ # ignore if provider doesn't accept string keys
29
+ end
30
+ else
31
+ raise NoMethodError, "Configuration object doesn't support setting #{field_name}"
32
+ end
33
+ else
34
+ # Getter
35
+ get_field(method_name)
36
+ end
37
+ end
38
+
39
+ def get_field(field_name)
40
+ validate_field!(field_name)
41
+
42
+ config_object = @resolver_proc.call
43
+ raw_value = if config_object.respond_to?(field_name)
44
+ config_object.send(field_name)
45
+ elsif config_object.respond_to?(:[])
46
+ config_object[field_name] || config_object[field_name.to_s]
47
+ else
48
+ nil
49
+ end
50
+
51
+ # Cast the value according to schema
52
+ field_def = @schema&.field_definition(@scope_name, field_name)
53
+ return raw_value unless field_def
54
+
55
+ casted = @schema&.cast_value(raw_value, field_def.type) || raw_value
56
+ # Return dup for mutable structures to prevent accidental mutation of shared defaults
57
+ if casted.is_a?(Array)
58
+ casted.dup
59
+ elsif casted.is_a?(Hash)
60
+ casted.dup
61
+ else
62
+ casted
63
+ end
64
+ end
65
+
66
+ def respond_to_missing?(method_name, include_private = false)
67
+ field_name = method_name.to_s.end_with?('=') ? method_name.to_s.chomp('=').to_sym : method_name.to_sym
68
+ @schema&.valid_field?(@scope_name, field_name) || super
69
+ end
70
+
71
+ private
72
+
73
+ def validate_field!(field_name)
74
+ return unless @schema # Skip validation if no schema provided
75
+
76
+ unless @schema.valid_field?(@scope_name, field_name)
77
+ valid_fields = @schema.scopes[@scope_name]&.fields&.keys || []
78
+ raise ArgumentError, "Unknown field '#{field_name}' for scope '#{@scope_name}'. Valid fields: #{valid_fields}"
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,86 @@
1
+ require "ostruct"
2
+ require "standard_config/config_provider"
3
+
4
+ module StandardConfig
5
+ class Manager
6
+ def initialize(schema)
7
+ @schema = schema
8
+ @providers = {}
9
+ end
10
+
11
+ # Register a configuration provider for a scope
12
+ def register(scope_name, resolver_proc)
13
+ scope_name = scope_name.to_sym
14
+
15
+ # Validate scope exists in schema
16
+ unless @schema.valid_scope?(scope_name)
17
+ raise ArgumentError, "Unknown configuration scope: #{scope_name}. Valid scopes: #{@schema.scopes.keys}"
18
+ end
19
+
20
+ @providers[scope_name] = ConfigProvider.new(scope_name, resolver_proc, @schema)
21
+ self
22
+ end
23
+
24
+ def registered?(scope_name)
25
+ @providers.key?(scope_name.to_sym)
26
+ end
27
+
28
+ # Access configuration scopes via method calls
29
+ def method_missing(method_name, *args)
30
+ method_str = method_name.to_s
31
+ scope_name = method_str.end_with?("=") ? method_str.chomp("=").to_sym : method_name.to_sym
32
+
33
+ # Handle field setter via unique scope resolution
34
+ if method_str.end_with?("=")
35
+ field = scope_name
36
+ scopes = @schema.scopes_with_field(field)
37
+ if scopes.size == 1
38
+ s = scopes.first
39
+ register(s, -> { create_static_config_for_scope(s) }) unless @providers.key?(s)
40
+ @providers[s].public_send(method_name, *args)
41
+ return args.first
42
+ end
43
+ end
44
+
45
+ # Handle field getter via unique scope resolution
46
+ scopes = @schema.scopes_with_field(scope_name)
47
+ if scopes.size == 1
48
+ s = scopes.first
49
+ register(s, -> { create_static_config_for_scope(s) }) unless @providers.key?(s)
50
+ return @providers[s].get_field(scope_name)
51
+ end
52
+
53
+ # Handle scope access
54
+ if @providers.key?(scope_name)
55
+ return @providers[scope_name]
56
+ end
57
+
58
+ # Create static provider for valid scopes on first access
59
+ if @schema.valid_scope?(scope_name)
60
+ register(scope_name, -> { create_static_config_for_scope(scope_name) })
61
+ return @providers[scope_name]
62
+ end
63
+
64
+ super
65
+ end
66
+
67
+ def respond_to_missing?(method_name, include_private = false)
68
+ method_str = method_name.to_s
69
+ scope_name = method_str.end_with?("=") ? method_str.chomp("=").to_sym : method_name.to_sym
70
+ @schema.valid_scope?(scope_name) ||
71
+ @schema.scopes_with_field(scope_name).any? ||
72
+ super
73
+ end
74
+
75
+ private
76
+
77
+ def create_static_config_for_scope(scope_name)
78
+ @static_configs ||= {}
79
+ @static_configs[scope_name] ||= OpenStruct.new.tap do |config|
80
+ @schema.scopes[scope_name].fields.each do |field_name, field_def|
81
+ config.send("#{field_name}=", field_def.default_value)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,137 @@
1
+ module StandardConfig
2
+ class Schema
3
+ def initialize
4
+ @scopes = {}
5
+ end
6
+
7
+ # DSL entry
8
+ def draw(&block)
9
+ Drawer.new(self).instance_eval(&block) if block_given?
10
+ self
11
+ end
12
+
13
+ def scopes
14
+ @scopes
15
+ end
16
+
17
+ def scope(name, &block)
18
+ name_sym = name.to_sym
19
+ builder = scopes[name_sym] ||= ScopeBuilder.new(name_sym)
20
+ builder.instance_eval(&block) if block_given?
21
+ builder
22
+ end
23
+
24
+ def valid_scope?(name)
25
+ scopes.key?(name.to_sym)
26
+ end
27
+
28
+ def valid_field?(scope_name, field_name)
29
+ return false unless valid_scope?(scope_name)
30
+ scopes[scope_name.to_sym].fields.key?(field_name.to_sym)
31
+ end
32
+
33
+ def field_definition(scope_name, field_name)
34
+ return nil unless valid_scope?(scope_name)
35
+ scopes[scope_name.to_sym].fields[field_name.to_sym]
36
+ end
37
+
38
+ # Return an array of scope names that define the given field
39
+ def scopes_with_field(field_name)
40
+ scopes.keys.select { |s| scopes[s].fields.key?(field_name.to_sym) }
41
+ end
42
+
43
+ def cast_value(value, type)
44
+ return value if value.nil?
45
+
46
+ case type
47
+ when :any
48
+ value
49
+ when :string
50
+ value.to_s
51
+ when :integer
52
+ value.to_i
53
+ when :float
54
+ value.to_f
55
+ when :boolean
56
+ case value
57
+ when true, false then value
58
+ when 'true', '1', 1 then true
59
+ when 'false', '0', 0 then false
60
+ else !!value
61
+ end
62
+ when :array
63
+ Array(value)
64
+ when :hash
65
+ value.is_a?(Hash) ? value : {}
66
+ else
67
+ value
68
+ end
69
+ end
70
+
71
+ class ScopeBuilder
72
+ attr_reader :name, :fields
73
+
74
+ def initialize(name)
75
+ @name = name.to_sym
76
+ @fields = {}
77
+ end
78
+
79
+ def field(name, type: :string, default: nil, readonly: false)
80
+ key = name.to_sym
81
+ if @fields.key?(key)
82
+ Kernel.warn("[StandardId::Configuration] Redefining field '#{key}' in scope '#{@name}'. Last definition wins.")
83
+ end
84
+ @fields[key] = FieldDefinition.new(name, type: type, default: default, readonly: readonly)
85
+ end
86
+ end
87
+
88
+ class FieldDefinition
89
+ attr_reader :name, :type, :default, :readonly
90
+
91
+ def initialize(name, type: :string, default: nil, readonly: false)
92
+ @name = name.to_sym
93
+ @type = type
94
+ @default = default
95
+ @readonly = readonly
96
+ end
97
+
98
+ def default_value
99
+ if @default.respond_to?(:call)
100
+ @default.call
101
+ elsif @default.is_a?(Array)
102
+ @default.dup
103
+ else
104
+ @default
105
+ end
106
+ end
107
+ end
108
+
109
+ # Internal DSL driver
110
+ class Drawer
111
+ def initialize(schema)
112
+ @schema = schema
113
+ end
114
+
115
+ # scope :base do ... end OR scope :passwordless do ... end
116
+ def scope(name, &block)
117
+ name_sym = name.to_sym
118
+ # Ensure scope exists, then evaluate the block in a scoped context
119
+ @schema.scope(name_sym)
120
+ ScopedScope.new(@schema, name_sym).instance_eval(&block) if block_given?
121
+ end
122
+ end
123
+
124
+ class ScopedScope
125
+ def initialize(schema, scope_name)
126
+ @schema = schema
127
+ @scope_name = scope_name
128
+ end
129
+
130
+ def field(name, type: :string, default: nil, readonly: false)
131
+ # Add field to the last declared scope by using ScopeBuilder within @schema.scope
132
+ # This method will be called inside Schema.scope block via Drawer
133
+ @schema.scopes[@scope_name].field(name, type: type, default: default, readonly: readonly)
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,38 @@
1
+ require "standard_config/config"
2
+ require "standard_config/config_provider"
3
+ require "standard_config/manager"
4
+ require "standard_config/schema"
5
+
6
+ module StandardConfig
7
+ class << self
8
+ def schema
9
+ @schema ||= Schema.new
10
+ end
11
+
12
+ def configure(&block)
13
+ config.register(:base, block) unless config.registered?(:base) if block_given? && block.arity == 0
14
+
15
+ yield config if block_given?
16
+
17
+ config
18
+ end
19
+
20
+ def config
21
+ @manager ||= Manager.new(schema)
22
+ end
23
+
24
+ private
25
+
26
+ def create_default_config
27
+ require "ostruct"
28
+ static_config = OpenStruct.new
29
+ base_scope = schema.scopes[:base]
30
+ if base_scope
31
+ base_scope.fields.each do |field_name, field_def|
32
+ static_config.send("#{field_name}=", field_def.default_value)
33
+ end
34
+ end
35
+ static_config
36
+ end
37
+ end
38
+ end
@@ -12,7 +12,7 @@ module StandardId
12
12
 
13
13
  def current_account
14
14
  return unless current_session
15
- @current_account ||= StandardId.config.account_class.find_by(id: current_session.account_id)
15
+ @current_account ||= StandardId.account_class.find_by(id: current_session.account_id)
16
16
  end
17
17
 
18
18
  def revoke_current_session!
@@ -0,0 +1,49 @@
1
+ # Schema definitions for StandardId
2
+ # This file defines the configuration schema structure
3
+
4
+ require "standard_config"
5
+
6
+ StandardConfig.schema.draw do
7
+ scope :base do
8
+ field :account_class_name, type: :string, default: "User"
9
+ field :cache_store, type: :any, default: nil
10
+ field :logger, type: :any, default: nil
11
+ field :web_layout, type: :string, default: nil
12
+ field :passwordless_email_sender, type: :any, default: nil
13
+ field :passwordless_sms_sender, type: :any, default: nil
14
+ field :issuer, type: :string, default: nil
15
+ field :login_url, type: :string, default: nil
16
+ field :allowed_post_logout_redirect_uris, type: :array, default: []
17
+ end
18
+
19
+ scope :passwordless do
20
+ field :code_ttl, type: :integer, default: 600 # 10 minutes in seconds
21
+ field :max_attempts, type: :integer, default: 3
22
+ field :retry_delay, type: :integer, default: 30 # 30 seconds
23
+ end
24
+
25
+ scope :password do
26
+ field :minimum_length, type: :integer, default: 8
27
+ field :require_special_chars, type: :boolean, default: false
28
+ field :require_uppercase, type: :boolean, default: false
29
+ field :require_numbers, type: :boolean, default: false
30
+ end
31
+
32
+ scope :oauth do
33
+ field :default_token_lifetime, type: :integer, default: 3600 # 1 hour in seconds
34
+ field :refresh_token_lifetime, type: :integer, default: 2592000 # 30 days in seconds
35
+ field :token_lifetimes, type: :hash, default: -> { {} }
36
+ field :client_id, type: :string, default: nil
37
+ field :client_secret, type: :string, default: nil
38
+ end
39
+
40
+ scope :social do
41
+ field :google_client_id, type: :string, default: nil
42
+ field :google_client_secret, type: :string, default: nil
43
+ field :apple_client_id, type: :string, default: nil
44
+ field :apple_client_secret, type: :string, default: nil
45
+ field :apple_private_key, type: :string, default: nil
46
+ field :apple_key_id, type: :string, default: nil
47
+ field :apple_team_id, type: :string, default: nil
48
+ end
49
+ end
@@ -11,7 +11,7 @@ module StandardId
11
11
  private
12
12
 
13
13
  def subject_id
14
- @credential.account_id
14
+ @credential.client_id
15
15
  end
16
16
 
17
17
  def client_id
@@ -29,10 +29,6 @@ module StandardId
29
29
  def audience
30
30
  params[:audience]
31
31
  end
32
-
33
- def token_expiry
34
- 1.hour
35
- end
36
32
  end
37
33
  end
38
34
  end
@@ -68,7 +68,7 @@ module StandardId
68
68
  end
69
69
 
70
70
  def token_expiry
71
- 1.hour
71
+ TokenLifetimeResolver.access_token_for(:implicit)
72
72
  end
73
73
 
74
74
  def subject_id
@@ -39,10 +39,6 @@ module StandardId
39
39
  true
40
40
  end
41
41
 
42
- def token_expiry
43
- 8.hours # Longer expiry for user sessions
44
- end
45
-
46
42
  def authenticate_account(username, password)
47
43
  StandardId::PasswordCredential
48
44
  .includes(credential: :account)