FIX: Ensure nonce mismatch causes auth to fail correctly (#15)
This commit is contained in:
parent
f32c23eece
commit
4aa0e1b4ab
|
@ -9,6 +9,8 @@ module ::OmniAuth
|
||||||
|
|
||||||
module Strategies
|
module Strategies
|
||||||
class OpenIDConnect < OmniAuth::Strategies::OAuth2
|
class OpenIDConnect < OmniAuth::Strategies::OAuth2
|
||||||
|
class NonceVerifyError < StandardError; end
|
||||||
|
|
||||||
option :scope, "openid"
|
option :scope, "openid"
|
||||||
option :discovery, true
|
option :discovery, true
|
||||||
option :discovery_document, nil
|
option :discovery_document, nil
|
||||||
|
@ -95,14 +97,13 @@ module ::OmniAuth
|
||||||
oauth2_callback_phase = super
|
oauth2_callback_phase = super
|
||||||
return oauth2_callback_phase if env['omniauth.error']
|
return oauth2_callback_phase if env['omniauth.error']
|
||||||
|
|
||||||
if id_token_info["nonce"].nil? || id_token_info["nonce"].empty? || id_token_info["nonce"] != session.delete("omniauth.nonce")
|
|
||||||
return fail!(:csrf_detected, CallbackError.new(:csrf_detected, "CSRF detected"))
|
|
||||||
end
|
|
||||||
oauth2_callback_phase
|
oauth2_callback_phase
|
||||||
rescue ::OmniAuth::OpenIDConnect::DiscoveryError => e
|
rescue ::OmniAuth::OpenIDConnect::DiscoveryError => e
|
||||||
fail!(:openid_connect_discovery_error, e)
|
fail!(:openid_connect_discovery_error, e)
|
||||||
rescue ::JWT::DecodeError => e
|
rescue ::JWT::DecodeError => e
|
||||||
fail!(:jwt_decode_failed, e)
|
fail!(:jwt_decode_failed, e)
|
||||||
|
rescue NonceVerifyError => e
|
||||||
|
fail!(:jwt_nonce_verify_failed, e)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -124,6 +125,11 @@ module ::OmniAuth
|
||||||
verify_iat: false,
|
verify_iat: false,
|
||||||
verify_jti: false
|
verify_jti: false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if decoded["nonce"].nil? || decoded["nonce"].empty? || decoded["nonce"] != session.delete("omniauth.nonce")
|
||||||
|
raise NonceVerifyError.new "JWT nonce is missing, or does not match"
|
||||||
|
end
|
||||||
|
|
||||||
verbose_log("Verified JWT\n\n#{decoded.to_yaml}")
|
verbose_log("Verified JWT\n\n#{decoded.to_yaml}")
|
||||||
|
|
||||||
decoded
|
decoded
|
||||||
|
|
|
@ -5,7 +5,9 @@ require 'rails_helper'
|
||||||
|
|
||||||
describe OmniAuth::Strategies::OpenIDConnect do
|
describe OmniAuth::Strategies::OpenIDConnect do
|
||||||
let(:app) do
|
let(:app) do
|
||||||
|
@app_called = false
|
||||||
lambda do |*args|
|
lambda do |*args|
|
||||||
|
@app_called = true
|
||||||
[200, {}, ['Hello.']]
|
[200, {}, ['Hello.']]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -109,6 +111,7 @@ describe OmniAuth::Strategies::OpenIDConnect do
|
||||||
subject.options.error_handler = lambda { |error, message| return "https://example.com/error_redirect" if message.include?("forgot password") }
|
subject.options.error_handler = lambda { |error, message| return "https://example.com/error_redirect" if message.include?("forgot password") }
|
||||||
expect(subject.callback_phase[0]).to eq(302)
|
expect(subject.callback_phase[0]).to eq(302)
|
||||||
expect(subject.callback_phase[1]["Location"]).to eq("https://example.com/error_redirect")
|
expect(subject.callback_phase[1]["Location"]).to eq("https://example.com/error_redirect")
|
||||||
|
expect(@app_called).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with userinfo disabled" do
|
context "with userinfo disabled" do
|
||||||
|
@ -128,16 +131,19 @@ describe OmniAuth::Strategies::OpenIDConnect do
|
||||||
expect(subject.info[:name]).to eq("My Auth Token Name")
|
expect(subject.info[:name]).to eq("My Auth Token Name")
|
||||||
expect(subject.info[:email]).to eq("tokenemail@example.com")
|
expect(subject.info[:email]).to eq("tokenemail@example.com")
|
||||||
expect(subject.extra[:id_token]).to eq(@token)
|
expect(subject.extra[:id_token]).to eq(@token)
|
||||||
|
expect(@app_called).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "checks the nonce" do
|
it "checks the nonce" do
|
||||||
subject.session["omniauth.nonce"] = "overriddenNonce"
|
subject.session["omniauth.nonce"] = "overriddenNonce"
|
||||||
expect(subject.callback_phase[0]).to eq(302)
|
expect(subject.callback_phase[0]).to eq(302)
|
||||||
|
expect(@app_called).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "checks the issuer" do
|
it "checks the issuer" do
|
||||||
subject.options.client_id = "overriddenclientid"
|
subject.options.client_id = "overriddenclientid"
|
||||||
expect(subject.callback_phase[0]).to eq(302)
|
expect(subject.callback_phase[0]).to eq(302)
|
||||||
|
expect(@app_called).to eq(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -165,6 +171,7 @@ describe OmniAuth::Strategies::OpenIDConnect do
|
||||||
expect(subject.uid).to eq("someuserid")
|
expect(subject.uid).to eq("someuserid")
|
||||||
expect(subject.info[:name]).to eq("My Userinfo Name")
|
expect(subject.info[:name]).to eq("My Userinfo Name")
|
||||||
expect(subject.info[:email]).to eq("userinfoemail@example.com")
|
expect(subject.info[:email]).to eq("userinfoemail@example.com")
|
||||||
|
expect(@app_called).to eq(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue