Skip to content

Commit 7464277

Browse files
committed
Add client_secret_expires_at to Dynamic Client Registration response
RFC 7591 §3.2.1 and OIDC Dynamic Client Registration 1.0 §3.2 require client_secret_expires_at whenever a client_secret is issued. Doorkeeper secrets never expire, so emit 0 (no expiration) for confidential clients.
1 parent 75d1613 commit 7464277

3 files changed

Lines changed: 9 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- [#304] allow handle auth_time per grant
55
- [#305] Document the `auth_time_from_access_token` config option in the README (per-grant `auth_time`), clarifying that it only affects the ID Token `auth_time` claim and not `max_age` enforcement
66
- [#307] Fix `bundle exec rake server` for the test application
7+
- [#311] Include the REQUIRED `client_secret_expires_at` member (value `0`, never expires) in the Dynamic Client Registration response whenever a `client_secret` is issued (RFC 7591 §3.2.1 / OpenID Connect Dynamic Client Registration 1.0 §3.2)
78

89
## v1.10.1 (2026-06-03)
910

app/controllers/doorkeeper/openid_connect/dynamic_client_registration_controller.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ def registration_response(doorkeeper_application, registration)
7272
if registration.confidential_client?
7373
response[:client_secret] =
7474
doorkeeper_application.plaintext_secret || doorkeeper_application.secret
75+
# RFC 7591 §3.2.1 / OIDC Dynamic Client Registration 1.0 §3.2:
76+
# client_secret_expires_at is REQUIRED when a client_secret is issued.
77+
# Doorkeeper secrets never expire, so the value is 0 (no expiration).
78+
response[:client_secret_expires_at] = 0
7579
end
7680

7781
response

spec/controllers/dynamic_client_registration_controller_spec.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
body = JSON.parse(response.body)
3838
expect(body).to eq({
3939
"client_secret" => doorkeeper_application.plaintext_secret || doorkeeper_application.secret,
40+
"client_secret_expires_at" => 0,
4041
"client_id" => doorkeeper_application.uid,
4142
"client_id_issued_at" => doorkeeper_application.created_at.to_i,
4243
"redirect_uris" => redirect_uris,
@@ -67,6 +68,7 @@
6768
body = JSON.parse(response.body)
6869
expect(body["token_endpoint_auth_method"]).to eq("client_secret_basic")
6970
expect(body["client_secret"]).to be_present
71+
expect(body["client_secret_expires_at"]).to eq(0)
7072
end
7173
end
7274

@@ -87,6 +89,7 @@
8789
body = JSON.parse(response.body)
8890
expect(body["token_endpoint_auth_method"]).to eq("client_secret_post")
8991
expect(body["client_secret"]).to be_present
92+
expect(body["client_secret_expires_at"]).to eq(0)
9093
end
9194
end
9295

@@ -107,6 +110,7 @@
107110
body = JSON.parse(response.body)
108111
expect(body["token_endpoint_auth_method"]).to eq("none")
109112
expect(body).not_to have_key("client_secret")
113+
expect(body).not_to have_key("client_secret_expires_at")
110114
end
111115
end
112116

0 commit comments

Comments
 (0)