standard_id 0.51.2 → 0.53.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.
@@ -266,6 +266,16 @@ module StandardId
266
266
 
267
267
  def registered?(scope_name) = !!self[scope_name.to_sym]&.resolver
268
268
 
269
+ # Applies a named settings bundle (StandardId::Config::Presets). Only
270
+ # settings the host has not assigned are filled, so explicit
271
+ # assignments win in either order.
272
+ #
273
+ # StandardId.configure do |c|
274
+ # c.preset :inertia_web
275
+ # c.web.passwordless_login = true
276
+ # end
277
+ def preset(name) = StandardId::Config::Presets.apply(self, name)
278
+
269
279
  def [](key)
270
280
  sym = key.to_sym
271
281
  return super if key?(sym) || __schema__.nil? || __schema__.scope?(sym)
@@ -166,7 +166,7 @@ module StandardId
166
166
  # inclusion. Extracted to a module method so specs can exercise it
167
167
  # directly without booting a second Rails app.
168
168
  def self.warn_if_allowed_audiences_empty_in_production!
169
- return unless Rails.env.production?
169
+ return unless StandardId::AppEnvironment.production?
170
170
  return if StandardId.config.oauth.allowed_audiences.present?
171
171
 
172
172
  Rails.logger.warn(
@@ -247,14 +247,14 @@ module StandardId
247
247
  end
248
248
 
249
249
  # Whether the bypass code is forbidden because this is a production
250
- # deploy. Defers to config.passwordless.production_env_detector when set
251
- # (host apps that distinguish a physical deploy env from RAILS_ENV), else
252
- # falls back to Rails.env.production?.
250
+ # deploy. Defers to config.passwordless.production_env_detector when set,
251
+ # else StandardId::AppEnvironment (APP_ENVIRONMENT, then Rails.env; fails
252
+ # closed).
253
253
  def bypass_production_deploy?
254
254
  detector = StandardId.config.passwordless.production_env_detector
255
255
  return !!detector.call if detector
256
256
 
257
- defined?(Rails) && Rails.env.production?
257
+ StandardId::AppEnvironment.production?
258
258
  end
259
259
 
260
260
  def resolve_target_and_channel!(email, phone)
@@ -201,7 +201,7 @@ module StandardId
201
201
  end
202
202
 
203
203
  def production?
204
- defined?(Rails) && Rails.respond_to?(:env) && Rails.env.production?
204
+ StandardId::AppEnvironment.production?
205
205
  end
206
206
 
207
207
  def validate_provider!(provider_class)
@@ -55,6 +55,105 @@ module StandardId
55
55
  def bearer_auth_header(token)
56
56
  { "Authorization" => "Bearer #{token}" }
57
57
  end
58
+
59
+ # Bearer headers for an account, in one call: build_jwt + bearer_auth_header.
60
+ #
61
+ # get "/api/orders", headers: bearer_headers_for(account, scope: "orders.read")
62
+ #
63
+ # @param account [Object] the account (its id becomes the sub claim)
64
+ # @param jwt_options [Hash] any other build_jwt keyword (scope:, client_id:, extra:, ...)
65
+ # @return [Hash] header hash
66
+ #
67
+ def bearer_headers_for(account, **jwt_options)
68
+ bearer_auth_header(build_jwt(account: account, **jwt_options))
69
+ end
70
+
71
+ # Sign an account in for the following requests in a request spec,
72
+ # without driving a login form or stubbing controllers.
73
+ #
74
+ # Creates a real BrowserSession and writes the same encrypted
75
+ # `session_token` cookie that Web::SessionManager writes on a real
76
+ # sign-in, so `current_account`, `require_browser_session!` and the
77
+ # session's expiry and revocation all behave exactly as in production.
78
+ # With `scope:`, it also writes the Rails session cookie carrying
79
+ # `standard_id_scopes` (what `current_scope_names` reads), which needs
80
+ # the cookie session store; any other store raises ArgumentError.
81
+ #
82
+ # Nothing is added to the app: the cookies are built with the app's own
83
+ # encrypted cookie jar and secret_key_base, so there is no test-only route
84
+ # to forget to remove. Like a real sign-in (which resets the session), it
85
+ # always replaces the Rails session cookie: an earlier request's session
86
+ # data, and an earlier sign_in_as's scopes, never carry over to this
87
+ # account.
88
+ #
89
+ # sign_in_as(account)
90
+ # sign_in_as(account, scope: :platform)
91
+ # get "/dashboard"
92
+ #
93
+ # @param account [Object] the account to sign in
94
+ # @param scope [Symbol, String, Array, nil] scope name(s) to grant
95
+ # @param user_agent [String] stored on the BrowserSession
96
+ # @return [StandardId::BrowserSession] the session that was created
97
+ #
98
+ def sign_in_as(account, scope: nil, user_agent: "RSpec")
99
+ ensure_cookie_session_store! if scope
100
+ browser_session = create_browser_session(account, user_agent: user_agent)
101
+ jar = standard_id_cookie_jar
102
+
103
+ jar.encrypted[:session_token] = {
104
+ value: browser_session.token,
105
+ expires: browser_session.expires_at,
106
+ httponly: true
107
+ }
108
+ cookies[:session_token] = jar[:session_token]
109
+
110
+ if scope
111
+ write_standard_id_scopes(jar, scope)
112
+ elsif (key = rails_session_key)
113
+ cookies.delete(key)
114
+ end
115
+ browser_session
116
+ end
117
+
118
+ private
119
+
120
+ # The host app's cookie jar, outside any request: same secret_key_base,
121
+ # salts, serializer and rotations as a real request, so the cookies it
122
+ # writes read back exactly as Web::SessionManager's own.
123
+ def standard_id_cookie_jar
124
+ ActionDispatch::Request.new(Rails.application.env_config.deep_dup).cookie_jar
125
+ end
126
+
127
+ # Checked before anything is created, so a refused call leaves no session.
128
+ def ensure_cookie_session_store!
129
+ store = Rails.application.config.session_store
130
+ unless store.is_a?(Class) && store <= ActionDispatch::Session::CookieStore
131
+ raise ArgumentError,
132
+ "sign_in_as(scope:) writes the Rails session cookie, so it needs the cookie " \
133
+ "session store; this app uses #{store.inspect}. " \
134
+ "Sign in through the app's own scoped login request instead."
135
+ end
136
+ return if rails_session_key
137
+
138
+ raise ArgumentError,
139
+ "sign_in_as(scope:) needs the session cookie name (config.session_options[:key]), and it is not set"
140
+ end
141
+
142
+ def rails_session_key
143
+ Rails.application.config.session_options[:key]&.to_s
144
+ end
145
+
146
+ def write_standard_id_scopes(jar, scope)
147
+ key = rails_session_key
148
+ jar.encrypted[key] = {
149
+ value: {
150
+ "session_id" => SecureRandom.hex(16),
151
+ "standard_id_scopes" => Array(scope).map(&:to_s)
152
+ },
153
+ httponly: true
154
+ }
155
+ cookies[key] = jar[key]
156
+ end
58
157
  end
59
158
  end
60
159
  end
@@ -1,3 +1,3 @@
1
1
  module StandardId
2
- VERSION = "0.51.2"
2
+ VERSION = "0.53.0"
3
3
  end
data/lib/standard_id.rb CHANGED
@@ -7,6 +7,8 @@ require "standard_id/web_engine"
7
7
  require "standard_id/api_engine"
8
8
  require "standard_id/config_schema"
9
9
  require "standard_id/config/schema"
10
+ require "standard_id/config/presets"
11
+ require "standard_id/app_environment"
10
12
  require "standard_id/scope_config"
11
13
  require "standard_id/association_strict_loading"
12
14
  require "standard_id/migration_check"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: standard_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.51.2
4
+ version: 0.53.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jaryl Sim
@@ -245,6 +245,7 @@ files:
245
245
  - db/migrate/20261020120000_add_registration_access_token_digest_to_standard_id_client_applications.rb
246
246
  - lib/generators/standard_id/install/install_generator.rb
247
247
  - lib/generators/standard_id/install/templates/standard_id.rb
248
+ - lib/generators/standard_id/install/templates/standard_id_reference.rb
248
249
  - lib/standard_id.rb
249
250
  - lib/standard_id/account_cleanup.rb
250
251
  - lib/standard_id/account_locking.rb
@@ -253,12 +254,14 @@ files:
253
254
  - lib/standard_id/api/session_manager.rb
254
255
  - lib/standard_id/api/token_manager.rb
255
256
  - lib/standard_id/api_engine.rb
257
+ - lib/standard_id/app_environment.rb
256
258
  - lib/standard_id/association_strict_loading.rb
257
259
  - lib/standard_id/auth_lineage.rb
258
260
  - lib/standard_id/authorization_bypass.rb
259
261
  - lib/standard_id/bearer_token_extraction.rb
260
262
  - lib/standard_id/checks/migrations.rb
261
263
  - lib/standard_id/config/callable_validator.rb
264
+ - lib/standard_id/config/presets.rb
262
265
  - lib/standard_id/config/schema.rb
263
266
  - lib/standard_id/config/scope_claims_validator.rb
264
267
  - lib/standard_id/config_schema.rb