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.
@@ -1,571 +1,26 @@
1
1
  # StandardId configuration
2
2
  # Generated by: rails g standard_id:install
3
3
  #
4
- # All configuration fields are defined in lib/standard_id/config/schema.rb.
5
- # Commented-out lines show defaults; uncomment and edit to override.
4
+ # Keep only the settings you change here; everything else is the gem default.
5
+ # Every setting, its default and the reasoning: the README "Configuration"
6
+ # section, lib/standard_id/config/schema.rb, and the annotated initializer at
7
+ # lib/generators/standard_id/install/templates/standard_id_reference.rb (all in
8
+ # the gem: `bundle open standard_id`).
6
9
 
7
10
  StandardId.configure do |c|
8
- # ---------------------------------------------------------------------------
9
- # Required
10
- # ---------------------------------------------------------------------------
11
+ # An Inertia app whose users sign in passwordlessly or socially can start
12
+ # from a preset. :inertia_web sets use_inertia, alias_current_user,
13
+ # web.signup/password_login/password_reset = false and
14
+ # oauth.discovery_endpoint_base = :request. Anything you assign yourself
15
+ # wins, before or after this line.
16
+ # c.preset :inertia_web
11
17
 
12
18
  # The ActiveRecord class that represents a user/account in your app. Must
13
- # include StandardId::AccountAssociations (see post-install checklist).
14
- # Default: "User"
19
+ # include StandardId::AccountAssociations (see the post-install checklist).
15
20
  c.account_class_name = "User"
16
- # The enum key StandardId::AccountStatus uses for a deactivated account
17
- # (default :inactive). Set it to the value your status column already
18
- # carries, e.g. :suspended, to adopt the concern without renaming it.
19
- # c.inactive_account_status = :inactive
20
21
 
21
- # JWT "iss" claim — typically your application's canonical base URL. When
22
+ # JWT "iss" claim, typically your application's canonical base URL. When
22
23
  # set, tokens embed it and verification enforces it. Strongly recommended
23
24
  # for production; otherwise the engine logs a warning on boot.
24
- # Default: nil
25
25
  # c.issuer = "https://auth.example.com"
26
-
27
- # ---------------------------------------------------------------------------
28
- # Discovery documents (/.well-known/openid-configuration and
29
- # /.well-known/oauth-authorization-server)
30
- # ---------------------------------------------------------------------------
31
- # READ THIS IF YOU MOUNT ApiEngine UNDER A PREFIX (/api, /api/v1, ...).
32
- #
33
- # The issuer and the endpoint base are different things. `issuer` is a stable
34
- # security identifier (RFC 8414 §2) that clients compare byte-for-byte with the
35
- # URL they used for discovery and with the `iss` claim of issued tokens. It is
36
- # never derived from the request and cannot be overridden.
37
- #
38
- # `discovery_endpoint_base` is where /authorize, /oauth/token etc. ACTUALLY
39
- # are. It defaults to the issuer, which is right only if your issuer carries
40
- # the mount path (e.g. "https://host/auth/api"). If it does not, the document
41
- # advertises endpoints at the bare origin and every one of them 404s.
42
- #
43
- # nil (default) — the issuer
44
- # :request — request.base_url + the detected mount path <- prefixed mounts
45
- # "https://..." — verbatim
46
- # callable — receives (request:); for a proxy that rewrites base_url, or
47
- # an app mounting ApiEngine more than once
48
- #
49
- # Default: nil
50
- # c.oauth.discovery_endpoint_base = :request
51
-
52
- # Members that cannot be derived. Values static or callable; a callable gets
53
- # { origin:, endpoint_base:, issuer:, request: } (origin has no path). A nil
54
- # value REMOVES the member rather than emitting null. Setting `issuer` raises.
55
- # Default: {}
56
- # c.oauth.discovery_metadata_overrides = {
57
- # # a host-owned shim that injects the audience before handing off to the
58
- # # engine's /authorize
59
- # authorization_endpoint: ->(ctx) { "#{ctx[:origin]}/oauth/authorize" },
60
- # # narrower than everything the server can mint
61
- # scopes_supported: %w[mcp mcp:read],
62
- # # MUST mirror your dynamic-registration policy, or spec-following clients
63
- # # register in a way that policy then rejects
64
- # token_endpoint_auth_methods_supported: %w[none],
65
- # # omit a member entirely
66
- # userinfo_endpoint: nil
67
- # }
68
- #
69
- # RFC 8615 clients probe the ORIGIN ROOT, which is outside every engine mount,
70
- # so those routes must be drawn in config/routes.rb — the gem cannot:
71
- #
72
- # standard_id_well_known_routes at: "/api" # your ApiEngine mount path
73
- #
74
- # That draws the root form AND the RFC 8414 §3.1 path-inserted form
75
- # (/.well-known/oauth-authorization-server/api). The second looks redundant
76
- # and is not: it is the URL Claude Code actually requests against a
77
- # path-carrying issuer. Accepts only:/except: (:oauth_authorization_server,
78
- # :openid_configuration, :jwks) — e.g. `only: :jwks` if you keep your own
79
- # metadata controller. See docs/MIGRATION_GUIDE.md.
80
-
81
- # Login URL used for redirects when authentication is required but missing.
82
- # Default: "/login"
83
- # c.login_url = "/login"
84
-
85
- # ---------------------------------------------------------------------------
86
- # Infrastructure
87
- # ---------------------------------------------------------------------------
88
-
89
- # Cache backend for rate limits, JWKS, etc. Falls back to Rails.cache.
90
- # Default: Rails.cache
91
- # c.cache_store = Rails.cache
92
-
93
- # Logger used by StandardId. Falls back to Rails.logger.
94
- # Default: Rails.logger
95
- # c.logger = Rails.logger
96
-
97
- # Layout used by built-in ERB web views. Ignored when use_inertia is true.
98
- # Default: nil (StandardId's packaged layout)
99
- # c.web_layout = "application"
100
-
101
- # Expose current_account as current_user (useful for ActionPolicy / Pundit).
102
- # Default: false
103
- # c.alias_current_user = true
104
-
105
- # Opt StandardId's OWN associations out of an app-wide
106
- # `strict_loading_by_default = true`.
107
- #
108
- # Affects Account#identifiers/credentials/sessions/refresh_tokens/
109
- # client_applications, StandardId::Session#refresh_tokens and
110
- # StandardId::Identifier#credentials. Set it to false if strict loading is on
111
- # app-wide and you don't want to eager-load the gem's internals at every call
112
- # site (the gem lazy-loads some of them itself).
113
- #
114
- # Tri-state, and nil is NOT the same as false:
115
- # nil — declare no `strict_loading:` option at all; the associations
116
- # inherit your model's setting, exactly as before this option
117
- # existed. This is the default and preserves existing behaviour.
118
- # false — opt out: these associations may lazy-load even with strict
119
- # loading on app-wide.
120
- # true — opt in: force strict loading on them even if it is off app-wide.
121
- #
122
- # Must be set HERE, in an initializer — not in an `after_initialize` block.
123
- # The associations read it when your Account class body runs, so a later
124
- # assignment has no effect. StandardId raises a ConfigurationError at boot if
125
- # it detects that, rather than letting it fail silently.
126
- #
127
- # Default: nil
128
- # c.association_strict_loading = false
129
-
130
- # Default scope applied when loading the current account — eager-load the
131
- # associations your views/controllers touch to avoid N+1s.
132
- # Default: nil
133
- # c.account_scope = ->(scope) { scope.includes(:profiles, :identifiers) }
134
-
135
- # Inertia.js support (requires the inertia_rails gem). When enabled, web
136
- # controllers render Inertia components instead of ERB views. Provide the
137
- # matching frontend components (e.g. pages/auth/login/show.tsx).
138
- # Defaults: use_inertia = false, inertia_component_namespace = "standard_id"
139
- # c.use_inertia = true
140
- # c.inertia_component_namespace = "auth"
141
-
142
- # ---------------------------------------------------------------------------
143
- # Web engine feature toggles
144
- # ---------------------------------------------------------------------------
145
- # Each toggle enables or disables a built-in WebEngine route group. Turn
146
- # off features your app handles itself or does not expose.
147
-
148
- # Default: true — enables /login, /logout and related password routes.
149
- # c.web.password_login = true
150
-
151
- # Default: true — enables /signup.
152
- # c.web.signup = true
153
-
154
- # Default: false — enables OTP (email/SMS) login at /passwordless.
155
- # c.web.passwordless_login = false
156
-
157
- # Default: false — allows new account registration via passwordless OTP.
158
- # c.web.passwordless_registration = false
159
-
160
- # Default: true — enables /auth/:provider social login routes.
161
- # c.web.social_login = true
162
-
163
- # Default: true — enables /password/reset flow.
164
- # c.web.password_reset = true
165
-
166
- # Default: true — enables email verification endpoints.
167
- # c.web.email_verification = true
168
-
169
- # Default: true — enables phone verification endpoints.
170
- # c.web.phone_verification = true
171
-
172
- # Default: true — enables /sessions management UI.
173
- # c.web.sessions_management = true
174
-
175
- # ---------------------------------------------------------------------------
176
- # Session lifetimes (seconds)
177
- # ---------------------------------------------------------------------------
178
-
179
- # Default: 86_400 — 24 hours for browser (cookie) sessions.
180
- # c.session.browser_session_lifetime = 86_400
181
-
182
- # Default: 2_592_000 — 30 days for "remember me" cookies.
183
- # c.session.browser_session_remember_me_lifetime = 2_592_000
184
-
185
- # Default: 2_592_000 — 30 days for API / mobile device sessions.
186
- # c.session.device_session_lifetime = 2_592_000
187
-
188
- # Default: 7_776_000 — 90 days for service-to-service sessions.
189
- # c.session.service_session_lifetime = 7_776_000
190
-
191
- # ---------------------------------------------------------------------------
192
- # Password policy
193
- # ---------------------------------------------------------------------------
194
-
195
- # Default: 8
196
- # c.password.minimum_length = 8
197
-
198
- # Default: true
199
- # c.password.require_special_chars = true
200
-
201
- # Default: true
202
- # c.password.require_uppercase = true
203
-
204
- # Default: true
205
- # c.password.require_numbers = true
206
-
207
- # ---------------------------------------------------------------------------
208
- # Passwordless (OTP)
209
- # ---------------------------------------------------------------------------
210
-
211
- # OTP email delivery mode:
212
- # :custom — host app handles delivery via event subscriber (default)
213
- # :built_in — engine sends via PasswordlessMailer
214
- # Default: :custom
215
- # c.passwordless.delivery = :built_in
216
- # c.passwordless.mailer_from = "noreply@example.com"
217
- # c.passwordless.mailer_subject = "Your sign-in code"
218
-
219
- # Default: 600 (10 minutes) — TTL of OTP codes.
220
- # c.passwordless.code_ttl = 600
221
-
222
- # Default: 6 — length of generated OTP codes (digits). Clamped to 4..10.
223
- # Codes may be zero-padded (e.g. "000123") so host apps that display or
224
- # parse codes must treat them as strings, not integers. 8+ is recommended
225
- # for security-sensitive deployments.
226
- # c.passwordless.code_length = 6
227
-
228
- # Per-challenge global attempt ceiling (across all IPs). When reached the
229
- # challenge is burned so further submissions fail fast. Distinct from the
230
- # per-IP rate limit (c.rate_limits.otp_verify_per_ip) — this defends
231
- # against distributed brute-force against a single challenge.
232
- # Default: nil — falls back to :max_attempts (default 3) for backwards
233
- # compatibility, so the effective default ceiling is 3.
234
- # c.passwordless.max_attempts_per_challenge = 3
235
-
236
- # Default: 3 — deprecated alias for :max_attempts_per_challenge. Retained
237
- # for backwards compatibility; new installs should set the newer key.
238
- # c.passwordless.max_attempts = 3
239
-
240
- # Default: 30 — OTP-resend cooldown: minimum seconds before the same target
241
- # can request another code. Enforced in the passwordless start path; a resend
242
- # inside the window is rejected (the already-sent code stays valid). Set to 0
243
- # to disable the cooldown.
244
- # c.passwordless.retry_delay = 30
245
-
246
- # Custom username validator; return nil/false to accept, or an error message
247
- # string to reject. Useful for domain allow-lists or truemail integration.
248
- # Default: nil
249
- # c.passwordless.username_validator = ->(username, connection_type) {
250
- # "Invalid email" unless TruemailValidator.valid?(username)
251
- # }
252
-
253
- # Custom account factory invoked during passwordless registration.
254
- # Receives (identifier:, params:, request:) and must return an Account.
255
- # Default: nil (uses built-in find_or_create logic)
256
- # c.passwordless.account_factory = ->(identifier:, params:, request:) {
257
- # Account.create!(email: identifier.value)
258
- # }
259
-
260
- # With c.passwordless.delivery = :custom, deliver codes from an event
261
- # subscriber (c.passwordless_email_sender / _sms_sender were removed in
262
- # 0.43). It runs synchronously in the request, so I18n.locale etc. are still
263
- # available. The same subscriber delivers Otp.issue(delivery: :custom) codes.
264
- #
265
- # StandardId::Events.subscribe(StandardId::Events::PASSWORDLESS_CODE_GENERATED) do |event|
266
- # next if event[:skip_sender] # Otp.issue(delivery: :manual)
267
- # case event[:channel]
268
- # when "email" then PasswordlessMailer.with(code: event[:code_challenge].code, to: event[:identifier]).deliver_later
269
- # when "sms" then SmsProvider.send_code(phone: event[:identifier], code: event[:code_challenge].code)
270
- # end
271
- # end
272
-
273
- # ---------------------------------------------------------------------------
274
- # API engine — OAuth / JWT
275
- # ---------------------------------------------------------------------------
276
-
277
- # Default: 3600 — access token lifetime in seconds (1 hour).
278
- # c.oauth.default_token_lifetime = 3600
279
-
280
- # Default: 2_592_000 — refresh token lifetime in seconds (30 days).
281
- # c.oauth.refresh_token_lifetime = 2_592_000
282
-
283
- # Per-grant overrides. Keys map to OAuth grant types.
284
- # Default: {}
285
- # c.oauth.token_lifetimes = {
286
- # password: 8.hours,
287
- # client_credentials: 24.hours,
288
- # social: 24.hours
289
- # }
290
-
291
- # Audiences accepted in JWT "aud" claim. An empty array disables audience
292
- # validation entirely — set this in production.
293
- # Default: []
294
- # c.oauth.allowed_audiences = %w[web mobile admin]
295
-
296
- # Scope-to-claims mapping — when a scope is granted, every listed claim is
297
- # resolved via claim_resolvers below and embedded in the access token.
298
- # Default: {}
299
- # c.oauth.scope_claims = {
300
- # profile: %i[email display_name]
301
- # }
302
-
303
- # Claim resolvers: one per claim listed in scope_claims. Each is a callable
304
- # receiving keyword args — pick only the ones you need.
305
- # Available kwargs: account:, client:, request:, audience:, session:
306
- # Default: {}
307
- # c.oauth.claim_resolvers = {
308
- # email: ->(account:) { account.email },
309
- # display_name: ->(account:, client:) { "#{account.name} for #{client.client_id}" },
310
- # channel_id: ->(client:) { client.owner_type == "Channel" ? client.owner_id : nil }
311
- # }
312
- # c.oauth.allowed_audiences = %w[web mobile admin] # Empty = no validation
313
- #
314
- # Audience → profile-type binding. When set, StandardId::AudienceVerification
315
- # will additionally verify that the authenticated account holds a profile of
316
- # the configured type for the matched audience. Values may be a single
317
- # profile-type string or an Array<String> for multi-type audiences.
318
- # c.oauth.audience_profile_types = {
319
- # "admin_kit" => "PlatformProfile",
320
- # "companion_kit" => "DeviceUserProfile",
321
- # "harness" => ["PlatformProfile", "DeviceUserProfile"]
322
- # }
323
- #
324
- # Optional resolver for picking the profile used for a given audience. Called
325
- # with (account:, audience:, profile_types:). When nil (default), the gem
326
- # looks up `account.profiles` and picks one whose `profileable_type` is in
327
- # the configured profile_types (preferring an `active?` record).
328
- # c.oauth.audience_profile_resolver = ->(account:, audience:, profile_types:) {
329
- # account.profiles.active.find_by(profileable_type: profile_types)
330
- # }
331
- #
332
- # Custom claims added to every access token (independent of scopes).
333
- # Receives keyword arguments: account:, client:, request:, audience:
334
- # Must return a Hash. Reserved JWT keys (sub, exp, iat, etc.) are excluded.
335
- # c.oauth.custom_claims = ->(account:, **) { { channel_id: account.channel_id } }
336
-
337
- # RFC 7662 token introspection (POST /oauth/introspect).
338
- #
339
- # Off by default: the endpoint returns 404 and `introspection_endpoint` is not
340
- # advertised in the discovery documents. An endpoint that answers questions
341
- # about other people's tokens is not something to expose by accident.
342
- #
343
- # Confidential clients only — the caller presents client_id + client_secret
344
- # (HTTP Basic or form body). Every failure renders `{"active": false}` with no
345
- # other members, per RFC 7662 §2.2, including a tripped rate limit (a 429
346
- # would distinguish "throttled" from "token invalid" and make the limiter a
347
- # token-validity oracle).
348
- #
349
- # KNOW THE LIMIT BEFORE BUILDING AN AUTHORIZATION GATE ON THIS. Access tokens
350
- # are stateless — never persisted, and carrying no `sid` — so a revoked
351
- # session's access token introspects as `active: true` until its `exp`.
352
- # Introspection answers "did we mint this, and is it unexpired?", NOT "is it
353
- # still honoured?". The mitigation is a short access-token lifetime. Refresh
354
- # tokens ARE persisted and are checked against the row, so a revoked refresh
355
- # token introspects as inactive immediately.
356
- #
357
- # Default: false
358
- # c.oauth.introspection_enabled = true
359
-
360
- # JWT signing configuration.
361
- #
362
- # Default: :hs256 symmetric signing with Rails.application.secret_key_base.
363
- # For asymmetric signing, set signing_key and expose the public key via the
364
- # JWKS endpoint at /.well-known/jwks.json.
365
- #
366
- # Algorithms: :hs256/:hs384/:hs512 (HMAC), :rs256/:rs384/:rs512 (RSA),
367
- # :es256/:es384/:es512 (ECDSA — recommended for new projects).
368
- #
369
- # Generate keys:
370
- # ES256: openssl ecparam -name prime256v1 -genkey -noout -out signing_key.pem
371
- # RS256: openssl genrsa -out signing_key.pem 2048
372
- #
373
- # c.oauth.signing_algorithm = :es256
374
- # c.oauth.signing_key = Rails.application.credentials.dig(:standard_id, :signing_key)
375
- #
376
- # Key rotation — move the previous signing_key here so tokens issued under
377
- # it still verify during the grace period. Remove once the longest token
378
- # lifetime has elapsed.
379
- # c.oauth.previous_signing_keys = [
380
- # Rails.application.credentials.dig(:standard_id, :previous_signing_key)
381
- # ]
382
- # # Cross-algorithm rotation:
383
- # c.oauth.previous_signing_keys = [
384
- # { key: Rails.application.credentials.dig(:standard_id, :old_rsa_key), algorithm: :rs256 }
385
- # ]
386
-
387
- # ---------------------------------------------------------------------------
388
- # Lifecycle hooks (WebEngine, synchronous)
389
- # ---------------------------------------------------------------------------
390
-
391
- # Called after credential verification, BEFORE session creation.
392
- # Receives: (account, request, context)
393
- # context = { mechanism:, provider:, first_sign_in: }
394
- # Return nil/truthy to proceed, or { error: "message" } to reject.
395
- # Default: nil
396
- # c.before_sign_in = ->(account, request, context) {
397
- # { error: "Access restricted" } unless account.allowed?
398
- # }
399
-
400
- # Called after successful sign-in, before redirect.
401
- # Receives: (account, request, context)
402
- # context = { first_sign_in:, mechanism:, provider:, session: }
403
- # Return nil (use default redirect) or a path string to override.
404
- # Raise StandardId::AuthenticationDenied to reject after sign-in.
405
- # Default: nil
406
- # c.after_sign_in = ->(account, request, context) {
407
- # context[:first_sign_in] ? "/onboarding" : nil
408
- # }
409
-
410
- # Decides which authentication methods may sign an account in. Consulted in
411
- # EVERY web and API flow that establishes a new authentication, after the
412
- # credential is proven and before any session or token is created.
413
- # Receives any subset of: account:, auth_method: (:password, :passwordless,
414
- # :social, :remember_me, :unspecified), provider:, request:, flow:
415
- # Return truthy to allow, false/nil to refuse, or raise
416
- # StandardId::LoginMethodDenied.new("message") to refuse with a message.
417
- # Default: nil (every method allowed)
418
- # c.login_method_policy = ->(account:, auth_method:, provider:) {
419
- # next true unless account.staff?
420
- # auth_method == :social && provider == "void_which_binds"
421
- # }
422
-
423
- # Called after a new account is created via any mechanism.
424
- # Receives: (account, request, context)
425
- # context = { mechanism:, provider: }
426
- # Use for profile provisioning, welcome emails, analytics, etc.
427
- # Default: nil
428
- # c.after_account_created = ->(account, request, context) {
429
- # UserProfileProvisioning.call(account, source: context[:mechanism])
430
- # }
431
-
432
- # ---------------------------------------------------------------------------
433
- # Events
434
- # ---------------------------------------------------------------------------
435
-
436
- # Emit internal lifecycle events through Rails.logger.
437
- # Default: false
438
- # c.events.enable_logging = false
439
-
440
- # For audit logging, use the standard_audit gem — see the README.
441
-
442
- # ---------------------------------------------------------------------------
443
- # Social login
444
- # ---------------------------------------------------------------------------
445
- # These fields are declared by the PROVIDER PLUGIN GEMS, not by standard_id
446
- # itself, so each line below requires the matching gem in your Gemfile:
447
- # `standard_id-google` for the google_* fields, `standard_id-apple` for the
448
- # apple_* fields. Uncommenting a line without its gem raises
449
- # StandardId::ConfigurationError ("Unknown field ... for scope 'social'") —
450
- # the field genuinely does not exist, because nothing declared it.
451
- #
452
- # With the gem present, writing them here — in a plain initializer, in the
453
- # `social` scope, exactly as shown — is correct and supported. standard_id
454
- # >= 0.33.0 declares every loaded provider's fields before initializers run.
455
- # On 0.32.0 and earlier these same lines raised, because the fields were only
456
- # declared later from the plugin's Railtie, and apps worked around it by
457
- # wrapping the writes in `Rails.application.config.after_initialize`. That
458
- # wrapper is no longer needed, and it still works if you have one.
459
- #
460
- # ENV defaults (standard_id >= 0.42): every provider field you do not assign
461
- # falls back to the ENV variable named after it, upper-cased — so with the
462
- # canonical variables below set, you need none of these lines. Assign a
463
- # field only to read it from somewhere else (a differently named variable,
464
- # Rails credentials); an explicit assignment, even of nil, always wins.
465
- #
466
- # GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET
467
- # APPLE_CLIENT_ID, APPLE_MOBILE_CLIENT_ID,
468
- # APPLE_PRIVATE_KEY, APPLE_KEY_ID, APPLE_TEAM_ID
469
- #
470
- # A provider is enabled when its client ID is present
471
- # (StandardId.social_provider_enabled?(:google)). An enabled provider missing
472
- # fields its plugin marks required is reported at boot — see
473
- # c.social.provider_misconfiguration below.
474
-
475
- # c.social.google_client_id = Rails.application.credentials.dig(:google, :client_id)
476
- # c.social.google_client_secret = Rails.application.credentials.dig(:google, :client_secret)
477
- # c.social.apple_client_id = ENV["APPLE_CLIENT_ID"] # web (Services ID) flow
478
- # c.social.apple_mobile_client_id = ENV["APPLE_MOBILE_CLIENT_ID"] # native id_token flow
479
- # c.social.apple_private_key = ENV["APPLE_PRIVATE_KEY"] # .p8 PEM, newlines intact
480
- # c.social.apple_key_id = ENV["APPLE_KEY_ID"]
481
- # c.social.apple_team_id = ENV["APPLE_TEAM_ID"]
482
-
483
- # What to do at boot when an enabled provider is missing required fields
484
- # (e.g. APPLE_CLIENT_ID set without APPLE_PRIVATE_KEY — the sign-in flow
485
- # would start, then fail at the callback). :warn logs a warning; :raise
486
- # raises StandardId::ConfigurationError in production and warns elsewhere.
487
- # Default: :warn
488
- # c.social.provider_misconfiguration = :raise
489
-
490
- # Mobile redirect URI allow-list — custom schemes used by native apps.
491
- # Default: []
492
- # c.social.allowed_redirect_url_prefixes = ["example-app://"]
493
-
494
- # OAuth scopes the host app supports for social providers.
495
- # Default: []
496
- # c.social.available_scopes = %w[profile email offline_access]
497
-
498
- # Account linking strategy, for a social login whose (provider, sub) is not
499
- # linked yet but whose email belongs to an existing account:
500
- # :strict — link only if that email identifier came from the same
501
- # provider, predates provider tracking, or the account
502
- # already has an identifier from this provider (default)
503
- # :trust_provider — link to the account that holds the email
504
- # Under both, the provider must report the email as verified, and an email
505
- # already linked to a different sub from the same provider is refused.
506
- # Default: :strict
507
- # c.social.link_strategy = :trust_provider
508
-
509
- # Attributes written to the Account on social signup.
510
- # Default: nil
511
- # c.social.social_account_attributes = ->(social_info:, provider:) {
512
- # { email: social_info[:email], name: social_info[:name] || social_info[:given_name] }
513
- # }
514
-
515
- # ---------------------------------------------------------------------------
516
- # OIDC logout allow-list
517
- # ---------------------------------------------------------------------------
518
-
519
- # Valid post_logout_redirect_uri values accepted by the OIDC RP-initiated
520
- # logout endpoint.
521
- # Default: []
522
- # c.allowed_post_logout_redirect_uris = [
523
- # "https://app.example.com/logged_out",
524
- # "https://admin.example.com/signout"
525
- # ]
526
-
527
- # ---------------------------------------------------------------------------
528
- # Rate limits (Rails 8 rate_limit DSL)
529
- # ---------------------------------------------------------------------------
530
- # Defaults are conservative; tune per environment.
531
-
532
- # Login limits. The login action branches password OR passwordless, so on a
533
- # passwordless app these govern the OTP-SEND limit. Prefer the
534
- # mechanism-agnostic names (password_login_per_ip/_per_email were removed in 0.43).
535
- # c.rate_limits.login_per_ip = 20 # per 15 minutes
536
- # c.rate_limits.login_per_email = 5 # per 15 minutes
537
- # c.rate_limits.otp_verify_per_ip = 20 # per 15 minutes
538
- # c.rate_limits.verification_start_per_target = 3 # per 15 minutes
539
- # c.rate_limits.verification_start_per_ip = 10 # per hour
540
- # c.rate_limits.api_passwordless_start_per_ip = 10 # per hour
541
- # c.rate_limits.api_passwordless_start_per_target = 5 # per 15 minutes
542
- # c.rate_limits.api_token_per_ip = 30 # per 15 minutes
543
-
544
- # Token introspection (only relevant with c.oauth.introspection_enabled).
545
- # Throttles by IP so the endpoint can't be used to bulk-classify stolen
546
- # tokens. Note the limit renders the ordinary {"active": false} / 200 rather
547
- # than 429 — see the introspection_enabled comment above for why.
548
- # c.rate_limits.introspection_per_ip = 30 # per 15 minutes
549
-
550
- # Optional per-audience tightening on top of api_token_per_ip. Only token
551
- # requests targeting a configured audience count toward its cap (per IP,
552
- # per 15 minutes); unlisted audiences are governed by the global ceiling.
553
- # c.rate_limits.api_token_per_audience_per_ip = { "mobile_app" => 10 }
554
-
555
- # ---------------------------------------------------------------------------
556
- # Observability
557
- # ---------------------------------------------------------------------------
558
-
559
- # Extra fields merged into Sentry user context for authenticated requests.
560
- # Receives (account, session). Return a Hash; non-callable values ignored.
561
- # Default: nil
562
- # c.sentry_context = ->(account, _session) {
563
- # { segment: account.primary_profile&.type }
564
- # }
565
26
  end
566
-
567
- # Notes:
568
- # - All scopes/fields are defined via the schema DSL in
569
- # lib/standard_id/config/schema.rb
570
- # - Values are cast to the declared type (string, integer, boolean, ...).
571
- # - Invalid scopes or fields raise ArgumentError with a helpful message.