Skip to content

Commit 1bd20e2

Browse files
authored
Merge pull request #246 from 55728/fix/at-hash-algorithm-mismatch
Fix `at_hash` to use correct hash algorithm based on `signing_algorithm`
2 parents f6c0116 + c6f45a3 commit 1bd20e2

3 files changed

Lines changed: 44 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- Please add here
44
- [#241] Fix NameError on doorkeeper master by deferring AR model loading in run_hooks (see [Doorkeeper PR](https://github.com/doorkeeper-gem/doorkeeper/pull/1804))
5+
- [#246] Fix `at_hash` to use correct hash algorithm based on `signing_algorithm`
56

67
## v1.9.0 (2026-03-16)
78

lib/doorkeeper/openid_connect/id_token_token.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,19 @@ def claims
2424
# access_token value with SHA-256, then take the left-most 128 bits and
2525
# base64url-encode them. The at_hash value is a case-sensitive string.
2626
def at_hash
27-
sha256 = Digest::SHA256.new
28-
token = @access_token.token
29-
hashed_token = sha256.digest(token)
27+
hashed_token = at_hash_digest.digest(@access_token.token)
3028
first_half = hashed_token[0...hashed_token.length / 2]
3129
Base64.urlsafe_encode64(first_half).tr('=', '')
3230
end
31+
32+
def at_hash_digest
33+
case Doorkeeper::OpenidConnect.signing_algorithm.to_s
34+
when /256\z/ then Digest::SHA256
35+
when /384\z/ then Digest::SHA384
36+
when /512\z/ then Digest::SHA512
37+
else Digest::SHA256
38+
end
39+
end
3340
end
3441
end
3542
end

spec/lib/id_token_token_spec.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,37 @@
3333
})
3434
end
3535
end
36+
37+
describe '#at_hash' do
38+
# Per OIDC Core 1.0 §3.1.3.6 / §3.2.2.9, at_hash must use the hash algorithm
39+
# that matches the alg of the ID Token's JOSE header (e.g. HS512 -> SHA-512).
40+
let(:token_value) { 'jHkWEdUXMU1BwAsC4vtUsZwnNvTIxEl0z9K3vx5KF0Y' }
41+
42+
before { access_token.update(token: token_value) }
43+
44+
def expected_at_hash(token, hasher)
45+
digest = hasher.digest(token)
46+
Base64.urlsafe_encode64(digest[0, digest.length / 2]).tr('=', '')
47+
end
48+
49+
it 'uses SHA-256 for the default RS256 signing algorithm' do
50+
expect(subject.claims[:at_hash]).to eq(expected_at_hash(token_value, Digest::SHA256))
51+
end
52+
53+
context 'when signing_algorithm is HS512' do
54+
before { configure_hmac }
55+
56+
it 'uses SHA-512' do
57+
expect(subject.claims[:at_hash]).to eq(expected_at_hash(token_value, Digest::SHA512))
58+
end
59+
end
60+
61+
context 'when signing_algorithm is ES512' do
62+
before { configure_ec }
63+
64+
it 'uses SHA-512' do
65+
expect(subject.claims[:at_hash]).to eq(expected_at_hash(token_value, Digest::SHA512))
66+
end
67+
end
68+
end
3669
end

0 commit comments

Comments
 (0)