Skip to content

Commit 78e8d74

Browse files
committed
fix: validate userinfo sub claim matches ID token sub (OIDC Core §5.3.2)
1 parent c3c7835 commit 78e8d74

2 files changed

Lines changed: 24 additions & 0 deletions

File tree

lib/omniauth/strategies/openid_connect.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,12 @@ def user_info
285285
# gem calls res.body.with_indifferent_access which fails on a JWE string, so we
286286
# fetch and decrypt the userinfo ourselves.
287287
userinfo = fetch_userinfo_attributes
288+
# OIDC Core §5.3.2: the sub in the UserInfo response MUST exactly match the sub
289+
# in the ID Token; if they differ, the UserInfo response MUST NOT be used.
290+
if userinfo.present? && userinfo[:sub].to_s != decoded[:sub].to_s
291+
raise CallbackError, error: :userinfo_sub_mismatch,
292+
reason: "UserInfo sub (#{userinfo[:sub].inspect}) does not match ID token sub (#{decoded[:sub].inspect})"
293+
end
288294
@user_info = ::OpenIDConnect::ResponseObject::UserInfo.new(userinfo.merge(decoded))
289295
else
290296
@user_info = ::OpenIDConnect::ResponseObject::UserInfo.new access_token.userinfo!.raw_attributes.merge(decoded)

test/lib/omniauth/strategies/openid_connect_jwe_test.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,24 @@ def test_user_info_uses_fetch_userinfo_attributes_when_encryption_configured
298298
assert_equal '+32499000000', result.phone_number
299299
end
300300

301+
def test_user_info_raises_on_sub_mismatch
302+
rsa_key = OpenSSL::PKey::RSA.generate(2048)
303+
id_token_claims = { sub: 'user123' }
304+
id_token_jws = JSON::JWT.new(id_token_claims).sign(rsa_key, :RS256).to_s
305+
306+
mock_access_token = stub(id_token: id_token_jws)
307+
decoded = stub(raw_attributes: id_token_claims)
308+
309+
jwe_strategy.options.id_token_encryption_alg = 'RSA-OAEP'
310+
jwe_strategy.stubs(:access_token).returns(mock_access_token)
311+
jwe_strategy.stubs(:decode_id_token).with(id_token_jws).returns(decoded)
312+
jwe_strategy.stubs(:fetch_userinfo_attributes).returns({ sub: 'attacker', email: 'evil@example.com' })
313+
314+
assert_raises(OmniAuth::Strategies::OpenIDConnect::CallbackError) do
315+
jwe_strategy.send(:user_info)
316+
end
317+
end
318+
301319
def test_user_info_falls_back_to_standard_path_when_no_encryption
302320
jwe_strategy.options.id_token_encryption_alg = nil
303321
mock_access_token = stub(id_token: nil)

0 commit comments

Comments
 (0)