doorkeeper-openid_connect 2.0.0 → 2.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 +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/doorkeeper/openid_connect/claims_builder.rb +3 -4
- data/lib/doorkeeper/openid_connect/claims_hash.rb +85 -0
- data/lib/doorkeeper/openid_connect/config.rb +6 -0
- data/lib/doorkeeper/openid_connect/engine.rb +7 -3
- data/lib/doorkeeper/openid_connect/helpers/controller/max_age.rb +105 -1
- data/lib/doorkeeper/openid_connect/orm/active_record/access_grant.rb +41 -7
- data/lib/doorkeeper/openid_connect/version.rb +1 -1
- data/lib/doorkeeper/openid_connect.rb +1 -0
- data/lib/generators/doorkeeper/openid_connect/templates/initializer.rb +7 -3
- metadata +3 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8eba70212cad992603afa10fe0f3ae1db4461c03938897418e8659f6a95c64bb
|
|
4
|
+
data.tar.gz: 74cc66dd6b502f6897428d36173f8faa0b68293dd456b99d7d80343714b2bb54
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 751bf66f9a2a58009f787a13c50d9a22a9f7b76117c8fbf1062e22896237ef7d8b66778e4834d59092fa7cdc623f48850b4e61eb796ba36f035418f3d4a39512
|
|
7
|
+
data.tar.gz: 1df8e6e7c4600a43e81833f5dcc3a210d99d4003a7f8a5bcde2ae53b8df662ced2147e7961d022b13a20607692f9147c44c7eee31704fd2ef11f289b0dd1ddac
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ Upgrading? [Migration from Old Versions](https://github.com/doorkeeper-gem/doork
|
|
|
4
4
|
|
|
5
5
|
- Add entry here
|
|
6
6
|
|
|
7
|
+
## v2.1.0 (2026-10-20)
|
|
8
|
+
|
|
9
|
+
- [#401] Fix `max_age` reauthentication loop when `auth_time_from_session` returns a String — parse it as a timestamp instead of falling through to `to_i` (same class of bug as [#248]) — and treat a future auth_time as stale (60 s clock-skew leeway)
|
|
10
|
+
- [#407] Prepend `AuthorizationsExtension` and `MetadataExtension` when Zeitwerk loads `Doorkeeper::AuthorizationsController` and `Doorkeeper::MetadataController`, instead of from `to_prepare`. Naming the controllers in `to_prepare` autoloaded them on every boot, together with `Doorkeeper::ApplicationController` and, through its default `ActionController::Base` superclass, every helper in the host application
|
|
11
|
+
- This gem no longer loads `ActionController::Base` during boot, so an Action Controller load hook that names an application constant such as `ApplicationController` should move to `config.to_prepare`
|
|
12
|
+
- [#408] Drop the `ostruct` runtime dependency (#210, added in #218). `Doorkeeper::OpenidConnect.configuration.claims` is now a Hash keyed by Symbol claim name instead of an `OpenStruct`. The OpenStruct-only forms (`claims.nickname`, `claims.nickname = ...`, String keys in `[]`, `[]=` and `dig`, and `delete_field`) still work but print a deprecation warning and will be removed in the next major version; use `claims[:nickname]`.
|
|
13
|
+
- [#402] Fix a boot failure (`MissingConfiguration`) when the access grant model loads before `Doorkeeper::OpenidConnect.configure` — e.g. from an initializer sorting between `doorkeeper.rb` and `doorkeeper_openid_connect.rb` (regression since v1.10.3)
|
|
14
|
+
|
|
7
15
|
## v2.0.0 (2026-09-22)
|
|
8
16
|
|
|
9
17
|
>[!IMPORTANT]
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "ostruct"
|
|
4
|
-
|
|
5
3
|
module Doorkeeper
|
|
6
4
|
module OpenidConnect
|
|
7
5
|
class ClaimsBuilder
|
|
@@ -33,7 +31,7 @@ module Doorkeeper
|
|
|
33
31
|
private_class_method :claim_applies?
|
|
34
32
|
|
|
35
33
|
def initialize(&block)
|
|
36
|
-
@claims =
|
|
34
|
+
@claims = ClaimsHash.new
|
|
37
35
|
instance_eval(&block)
|
|
38
36
|
end
|
|
39
37
|
|
|
@@ -42,13 +40,14 @@ module Doorkeeper
|
|
|
42
40
|
end
|
|
43
41
|
|
|
44
42
|
def normal_claim(name, response: [:user_info], scope: nil, &block)
|
|
45
|
-
|
|
43
|
+
claim =
|
|
46
44
|
Claims::NormalClaim.new(
|
|
47
45
|
name: name,
|
|
48
46
|
response: response,
|
|
49
47
|
scope: scope,
|
|
50
48
|
generator: block,
|
|
51
49
|
)
|
|
50
|
+
@claims[claim.name] = claim
|
|
52
51
|
end
|
|
53
52
|
alias claim normal_claim
|
|
54
53
|
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper
|
|
4
|
+
module OpenidConnect
|
|
5
|
+
# The configured claims, keyed by Symbol claim name.
|
|
6
|
+
#
|
|
7
|
+
# This used to be an `OpenStruct`. It is a Hash now, but it still answers the
|
|
8
|
+
# OpenStruct-only forms (`claims.nickname`, `claims.nickname = ...`, String keys
|
|
9
|
+
# in `[]`, `[]=` and `dig`, and `delete_field`) with a deprecation warning, so
|
|
10
|
+
# apps that relied on them keep working until the next major version, when this
|
|
11
|
+
# becomes a plain Hash.
|
|
12
|
+
class ClaimsHash < Hash
|
|
13
|
+
# Emit the deprecation at most once per process; claims are read on every
|
|
14
|
+
# ID Token and UserInfo response.
|
|
15
|
+
@deprecation_warned = false
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def warn_deprecation
|
|
19
|
+
return if @deprecation_warned
|
|
20
|
+
|
|
21
|
+
@deprecation_warned = true
|
|
22
|
+
warn "DEPRECATION WARNING: `Doorkeeper::OpenidConnect.configuration.claims` is a Hash " \
|
|
23
|
+
"keyed by Symbol claim name. Reading or writing it method-style " \
|
|
24
|
+
"(`claims.nickname`), with String keys (`claims[\"nickname\"]`) or with " \
|
|
25
|
+
"`delete_field` is deprecated and will stop working in the next major version. " \
|
|
26
|
+
"Use `claims[:nickname]` and `claims.delete(:nickname)` instead."
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Reset the deprecation flag (test helper).
|
|
30
|
+
def reset_deprecation_warning!
|
|
31
|
+
@deprecation_warned = false
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def [](name)
|
|
36
|
+
return super unless name.is_a?(String)
|
|
37
|
+
|
|
38
|
+
self.class.warn_deprecation
|
|
39
|
+
super(name.to_sym)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def []=(name, value)
|
|
43
|
+
if name.is_a?(String)
|
|
44
|
+
self.class.warn_deprecation
|
|
45
|
+
name = name.to_sym
|
|
46
|
+
end
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def dig(name, *rest)
|
|
51
|
+
return super unless name.is_a?(String)
|
|
52
|
+
|
|
53
|
+
self.class.warn_deprecation
|
|
54
|
+
super(name.to_sym, *rest)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Like OpenStruct#delete_field: raises NameError for an unknown claim unless given a block.
|
|
58
|
+
def delete_field(name)
|
|
59
|
+
self.class.warn_deprecation
|
|
60
|
+
key = name.to_sym
|
|
61
|
+
return delete(key) if key?(key)
|
|
62
|
+
return yield if block_given?
|
|
63
|
+
|
|
64
|
+
raise NameError.new("no field `#{key}' in #{self.class}", key)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def method_missing(method_name, *args, &block)
|
|
68
|
+
name = method_name.to_s
|
|
69
|
+
if name.end_with?("=") && args.size == 1 && block.nil?
|
|
70
|
+
self.class.warn_deprecation
|
|
71
|
+
self[name.chomp("=").to_sym] = args.first
|
|
72
|
+
elsif args.empty? && block.nil?
|
|
73
|
+
self.class.warn_deprecation
|
|
74
|
+
fetch(method_name, nil)
|
|
75
|
+
else
|
|
76
|
+
super
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
81
|
+
key?(method_name) || method_name.to_s.end_with?("=") || super
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -10,6 +10,12 @@ module Doorkeeper
|
|
|
10
10
|
|
|
11
11
|
@config = Config::Builder.new(&block).build
|
|
12
12
|
validate_issuer_consistency
|
|
13
|
+
# Access grant models that loaded before this point deferred their
|
|
14
|
+
# `openid_request` association because it needs `open_id_request_class`.
|
|
15
|
+
# The namespace is spelled out: a bare `AccessGrant` would resolve to
|
|
16
|
+
# Doorkeeper's own grant model through the enclosing lexical scope if the
|
|
17
|
+
# autoload were ever dropped.
|
|
18
|
+
OpenidConnect::AccessGrant.wire_pending_hosts
|
|
13
19
|
@config
|
|
14
20
|
end
|
|
15
21
|
|
|
@@ -7,14 +7,18 @@ module Doorkeeper
|
|
|
7
7
|
Doorkeeper::OpenidConnect::Rails::Routes.install!
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
Doorkeeper::AuthorizationsController
|
|
10
|
+
initializer "doorkeeper.openid_connect.controller_extensions" do |app|
|
|
11
|
+
app.autoloaders.main.on_load("Doorkeeper::AuthorizationsController") do |controller|
|
|
12
|
+
controller.prepend Doorkeeper::OpenidConnect::AuthorizationsExtension
|
|
13
|
+
end
|
|
12
14
|
|
|
13
15
|
# Doorkeeper >= 6.0 serves its own RFC 8414 metadata document at
|
|
14
16
|
# /.well-known/oauth-authorization-server; enrich it with the OpenID
|
|
15
17
|
# Connect metadata (see MetadataExtension).
|
|
16
18
|
if Doorkeeper::OpenidConnect.doorkeeper_metadata_endpoint?
|
|
17
|
-
Doorkeeper::MetadataController
|
|
19
|
+
app.autoloaders.main.on_load("Doorkeeper::MetadataController") do |controller|
|
|
20
|
+
controller.prepend Doorkeeper::OpenidConnect::MetadataExtension
|
|
21
|
+
end
|
|
18
22
|
end
|
|
19
23
|
end
|
|
20
24
|
end
|
|
@@ -8,6 +8,38 @@ module Doorkeeper
|
|
|
8
8
|
# §3.1.2.1): when the resource owner's last authentication is older
|
|
9
9
|
# than `max_age` seconds, reauthentication is required.
|
|
10
10
|
module MaxAge
|
|
11
|
+
# The shapes a `Time` takes once it has been serialized into the
|
|
12
|
+
# session:
|
|
13
|
+
#
|
|
14
|
+
# * `"2026-09-05T03:59:10.270Z"` — `ActiveSupport::JSON`, which is
|
|
15
|
+
# what the JSON cookie serializer emits
|
|
16
|
+
# * `"2026/09/05 03:59:10 +0000"` — the same encoder with
|
|
17
|
+
# `ActiveSupport::JSON::Encoding.use_standard_json_time_format`
|
|
18
|
+
# turned off
|
|
19
|
+
# * `"2026-09-05 03:59:27 UTC"` — `JSON.generate` on a bare `Time`
|
|
20
|
+
#
|
|
21
|
+
# The date separator is captured and backreferenced so the two forms
|
|
22
|
+
# cannot be mixed, and the zone suffix is optional so that a
|
|
23
|
+
# serializer which drops it still reads as a local time.
|
|
24
|
+
OIDC_SERIALIZED_AUTH_TIME = %r{
|
|
25
|
+
\A
|
|
26
|
+
(?<year>\d{4})(?<sep>[-/])(?<month>\d{2})\k<sep>(?<day>\d{2}) # date
|
|
27
|
+
[T\ ]
|
|
28
|
+
\d{2}:\d{2}:\d{2} # time
|
|
29
|
+
(?:\.\d+)? # optional fractional seconds
|
|
30
|
+
(?:\ ?(?:Z|UTC|[+-]\d{2}:?\d{2}))? # optional zone
|
|
31
|
+
\z
|
|
32
|
+
}x
|
|
33
|
+
|
|
34
|
+
# How far ahead of the provider's clock an auth_time may sit and
|
|
35
|
+
# still be trusted. An authentication cannot have happened in the
|
|
36
|
+
# future, but the servers that write auth_time and the one that
|
|
37
|
+
# checks it do not share a clock, and a user who authenticated a
|
|
38
|
+
# moment ago must not be sent to reauthenticate over ordinary skew —
|
|
39
|
+
# the fresh auth_time from that reauthentication would be ahead
|
|
40
|
+
# again, looping forever.
|
|
41
|
+
OIDC_AUTH_TIME_FUTURE_LEEWAY = 60
|
|
42
|
+
|
|
11
43
|
private
|
|
12
44
|
|
|
13
45
|
def handle_oidc_max_age_param!(owner)
|
|
@@ -56,12 +88,84 @@ module Doorkeeper
|
|
|
56
88
|
def normalized_oidc_auth_time(owner)
|
|
57
89
|
auth_time = resolve_oidc_auth_time(owner)
|
|
58
90
|
return auth_time if !auth_time || auth_time.is_a?(Time) || auth_time.is_a?(DateTime)
|
|
91
|
+
return parse_oidc_auth_time_string(auth_time) if auth_time.is_a?(String)
|
|
59
92
|
|
|
60
93
|
Time.zone.at(auth_time.to_i)
|
|
61
94
|
end
|
|
62
95
|
|
|
96
|
+
# A `Time` written to the session comes back as a String once the
|
|
97
|
+
# session is serialized, which is the default since Rails 7.0
|
|
98
|
+
# (`config.load_defaults` selects the JSON cookie serializer). Running
|
|
99
|
+
# such a value through `to_i` yields the leading year — `"2026-09-05
|
|
100
|
+
# T03:59:10.270Z".to_i` is `2026` — placing auth_time in 1970 and
|
|
101
|
+
# making every `max_age` request look stale, so the RP and the
|
|
102
|
+
# provider bounce the user through reauthentication forever. Same
|
|
103
|
+
# class of bug as #248, which fixed the Integer case.
|
|
104
|
+
#
|
|
105
|
+
# A numeric String is an epoch. The grammar covers every *numeric*
|
|
106
|
+
# String `to_i` resolved — optional surrounding whitespace, an
|
|
107
|
+
# optional sign, an optional fractional part — and `to_f` rather than
|
|
108
|
+
# `to_i` keeps the sub-second part of `"1757150000.5"`. A signed
|
|
109
|
+
# value stays meaningful either way: `"+1757150000"` is the epoch it
|
|
110
|
+
# looks like, and a negative one resolves to a pre-1970 time, which
|
|
111
|
+
# is stale under any `max_age` and so requires reauthentication
|
|
112
|
+
# regardless.
|
|
113
|
+
#
|
|
114
|
+
# Anchoring it at both ends is the one deliberate narrowing here.
|
|
115
|
+
# `to_i` also truncated a String to its leading integer, so
|
|
116
|
+
# `"1788703912 seconds"` used to resolve and no longer does. Keeping
|
|
117
|
+
# that as a fallback would reinstate the bug above for every
|
|
118
|
+
# serialization this method does not recognise —
|
|
119
|
+
# `"20260905T035910Z".to_i` is `20260905`, i.e. 1970 and stale
|
|
120
|
+
# forever — while reading `"99999999999999 garbage"` as a year
|
|
121
|
+
# 3170843 timestamp, i.e. fresh. Truncation guesses wrong in both
|
|
122
|
+
# directions; a String that is not wholly a number is not an epoch.
|
|
123
|
+
#
|
|
124
|
+
# Anything else has to look like one of the
|
|
125
|
+
# serialized timestamps, which `Time.zone.parse` then reads. `parse`
|
|
126
|
+
# is not used on its own because it is far more permissive than any
|
|
127
|
+
# of those formats: it takes `"10 minutes"` for day 10 of the current
|
|
128
|
+
# month, and such a value landing within `max_age` of now would read
|
|
129
|
+
# as *fresh* and silently skip the reauthentication.
|
|
130
|
+
# `Time.zone.iso8601` is no alternative — it rejects the bare-`Time`
|
|
131
|
+
# form outright.
|
|
132
|
+
#
|
|
133
|
+
# Matching the shape is not enough on its own: `parse` rolls an
|
|
134
|
+
# impossible calendar date over instead of raising, so `"2026-02-31"`
|
|
135
|
+
# comes back as 2026-03-03 — a date that does not exist would read as
|
|
136
|
+
# fresh whenever its rollover lands within `max_age` of now. The day
|
|
137
|
+
# is therefore checked with `Date.valid_date?` before parsing.
|
|
138
|
+
#
|
|
139
|
+
# What is left over is rescued: an out-of-range hour, minute or
|
|
140
|
+
# second raises `ArgumentError`, and a numeric String too long to be
|
|
141
|
+
# a Float (`"9" * 400` overflows to `Float::INFINITY`) makes
|
|
142
|
+
# `Time.zone.at` raise `FloatDomainError`. A value of any of these
|
|
143
|
+
# shapes returns nil, which `oidc_auth_time_stale?` treats as "no
|
|
144
|
+
# known auth_time" and so requires reauthentication — a malformed
|
|
145
|
+
# callback value must fail closed, not 500.
|
|
146
|
+
def parse_oidc_auth_time_string(value)
|
|
147
|
+
return Time.zone.at(value.to_f) if value.match?(/\A\s*[+-]?\d+(?:\.\d+)?\s*\z/)
|
|
148
|
+
|
|
149
|
+
match = OIDC_SERIALIZED_AUTH_TIME.match(value)
|
|
150
|
+
return unless match
|
|
151
|
+
return unless Date.valid_date?(match[:year].to_i, match[:month].to_i, match[:day].to_i)
|
|
152
|
+
|
|
153
|
+
Time.zone.parse(value)
|
|
154
|
+
rescue ArgumentError, RangeError
|
|
155
|
+
nil
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# An auth_time further in the future than clock skew explains is not
|
|
159
|
+
# a time the resource owner authenticated at, whatever its type — a
|
|
160
|
+
# `Time`, an Integer epoch and a parsed String all reach here — so it
|
|
161
|
+
# is treated like an unknown one and requires reauthentication.
|
|
162
|
+
# Left unchecked, its negative age is below any `max_age` and it
|
|
163
|
+
# would read as fresh indefinitely.
|
|
63
164
|
def oidc_auth_time_stale?(auth_time, max_age)
|
|
64
|
-
|
|
165
|
+
return true unless auth_time
|
|
166
|
+
|
|
167
|
+
age = Time.zone.now - auth_time
|
|
168
|
+
age > max_age || age < -OIDC_AUTH_TIME_FUTURE_LEEWAY
|
|
65
169
|
end
|
|
66
170
|
|
|
67
171
|
# Resolve auth_time for max_age enforcement.
|
|
@@ -3,13 +3,47 @@
|
|
|
3
3
|
module Doorkeeper
|
|
4
4
|
module OpenidConnect
|
|
5
5
|
module AccessGrant
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
class << self
|
|
7
|
+
# Wiring the `openid_request` association needs
|
|
8
|
+
# `open_id_request_class`, so it can only run once
|
|
9
|
+
# `Doorkeeper::OpenidConnect.configure` has. The grant model is not
|
|
10
|
+
# guaranteed to load after that: Doorkeeper's own `initialize_models!`
|
|
11
|
+
# reaches for it from `Doorkeeper.configure` (immediately, when a
|
|
12
|
+
# railtie has already loaded `ActiveRecord::Base`), and any initializer
|
|
13
|
+
# sorting between `doorkeeper.rb` and `doorkeeper_openid_connect.rb`
|
|
14
|
+
# can name the model itself. Reading the configuration here would raise
|
|
15
|
+
# `MissingConfiguration` in both cases — with a message pointing at an
|
|
16
|
+
# initializer that does exist and simply has not run yet.
|
|
17
|
+
#
|
|
18
|
+
# So wire immediately when the configuration is there, and otherwise
|
|
19
|
+
# remember the host model and wire it from `configure`.
|
|
20
|
+
def prepended(base)
|
|
21
|
+
return pending_hosts << base unless OpenidConnect.configured?
|
|
22
|
+
|
|
23
|
+
wire_openid_request_association(base)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Called from `Doorkeeper::OpenidConnect.configure` for the models that
|
|
27
|
+
# loaded before it.
|
|
28
|
+
def wire_pending_hosts
|
|
29
|
+
pending_hosts.each { |base| wire_openid_request_association(base) }
|
|
30
|
+
pending_hosts.clear
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def pending_hosts
|
|
36
|
+
@pending_hosts ||= []
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def wire_openid_request_association(base)
|
|
40
|
+
base.class_eval do
|
|
41
|
+
has_one :openid_request,
|
|
42
|
+
class_name: Doorkeeper::OpenidConnect.configuration.open_id_request_class,
|
|
43
|
+
foreign_key: "access_grant_id",
|
|
44
|
+
inverse_of: :access_grant,
|
|
45
|
+
dependent: :delete
|
|
46
|
+
end
|
|
13
47
|
end
|
|
14
48
|
end
|
|
15
49
|
end
|
|
@@ -19,6 +19,7 @@ module Doorkeeper
|
|
|
19
19
|
module OpenidConnect
|
|
20
20
|
autoload :AtHashConcern, "doorkeeper/openid_connect/at_hash_concern"
|
|
21
21
|
autoload :ClaimsBuilder, "doorkeeper/openid_connect/claims_builder"
|
|
22
|
+
autoload :ClaimsHash, "doorkeeper/openid_connect/claims_hash"
|
|
22
23
|
autoload :DiscoveryHelpersMixin, "doorkeeper/openid_connect/discovery_helpers_mixin"
|
|
23
24
|
autoload :Errors, "doorkeeper/openid_connect/errors"
|
|
24
25
|
autoload :GrantTypesSupportedMixin, "doorkeeper/openid_connect/grant_types_supported_mixin"
|
|
@@ -59,9 +59,13 @@ Doorkeeper::OpenidConnect.configure do
|
|
|
59
59
|
# piggy-backing on a fresh login from another device.
|
|
60
60
|
#
|
|
61
61
|
# The block is executed in the controller's scope and receives the
|
|
62
|
-
# current `session` and `request`. Return value can be a `Time`,
|
|
63
|
-
# `DateTime`,
|
|
64
|
-
#
|
|
62
|
+
# current `session` and `request`. Return value can be a `Time`, a
|
|
63
|
+
# `DateTime`, epoch seconds (an Integer or a numeric String), or a
|
|
64
|
+
# serialized timestamp String such as `"2026-09-05T03:59:10.270Z"` — a
|
|
65
|
+
# `Time` stored in the session comes back as the latter once it
|
|
66
|
+
# round-trips through the JSON cookie serializer, which Rails 7.0+
|
|
67
|
+
# selects by default. A String of any other shape is read as an unknown
|
|
68
|
+
# auth_time and forces reauthentication, as does `nil`.
|
|
65
69
|
#
|
|
66
70
|
# auth_time_from_session do |session, _request|
|
|
67
71
|
# # Example implementation: capture auth_time on the session at login,
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: doorkeeper-openid_connect
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sam Dengler
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2026-
|
|
13
|
+
date: 2026-10-20 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: doorkeeper
|
|
@@ -46,20 +46,6 @@ dependencies:
|
|
|
46
46
|
- - ">="
|
|
47
47
|
- !ruby/object:Gem::Version
|
|
48
48
|
version: '2.5'
|
|
49
|
-
- !ruby/object:Gem::Dependency
|
|
50
|
-
name: ostruct
|
|
51
|
-
requirement: !ruby/object:Gem::Requirement
|
|
52
|
-
requirements:
|
|
53
|
-
- - ">="
|
|
54
|
-
- !ruby/object:Gem::Version
|
|
55
|
-
version: '0.5'
|
|
56
|
-
type: :runtime
|
|
57
|
-
prerelease: false
|
|
58
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
59
|
-
requirements:
|
|
60
|
-
- - ">="
|
|
61
|
-
- !ruby/object:Gem::Version
|
|
62
|
-
version: '0.5'
|
|
63
49
|
- !ruby/object:Gem::Dependency
|
|
64
50
|
name: bigdecimal
|
|
65
51
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -199,6 +185,7 @@ files:
|
|
|
199
185
|
- lib/doorkeeper/openid_connect/claims/claim.rb
|
|
200
186
|
- lib/doorkeeper/openid_connect/claims/normal_claim.rb
|
|
201
187
|
- lib/doorkeeper/openid_connect/claims_builder.rb
|
|
188
|
+
- lib/doorkeeper/openid_connect/claims_hash.rb
|
|
202
189
|
- lib/doorkeeper/openid_connect/config.rb
|
|
203
190
|
- lib/doorkeeper/openid_connect/discovery_helpers_mixin.rb
|
|
204
191
|
- lib/doorkeeper/openid_connect/engine.rb
|