omniauth_openid_connect-0.3.5/0000755000004100000410000000000013673167754016465 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/.travis.yml0000644000004100000410000000012113673167754020570 0ustar www-datawww-datalanguage: ruby rvm: - 2.4 - 2.5 - 2.6 - 2.7 - jruby-head - ruby-head omniauth_openid_connect-0.3.5/test/0000755000004100000410000000000013673167754017444 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/test/fixtures/0000755000004100000410000000000013673167754021315 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/test/fixtures/id_token.txt0000644000004100000410000000111513673167754023650 0ustar www-datawww-dataeyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6qJp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJNqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7TpdQyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoSK5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg omniauth_openid_connect-0.3.5/test/fixtures/test.crt0000644000004100000410000000217713673167754023015 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDJDCCAgwCCQC57Ob2JfXb+DANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJK UDEOMAwGA1UECBMFVG9reW8xITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5 IEx0ZDESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTE0MDgwMTA4NTAxM1oXDTE1MDgw MTA4NTAxM1owVDELMAkGA1UEBhMCSlAxDjAMBgNVBAgTBVRva3lvMSEwHwYDVQQK ExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCWxvY2FsaG9zdDCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN+7czSGHN2087T+oX2kBCY/ XN6UOS/mdU2Gn//omZlyxsQXIqvgBLNWeCVt4QdlFUbgPLggfXUelECV/RUOCIIi F2Th4t3x1LviN2XkUiva0DZBnOycqEaJdkyreEuGL1CLVZgZjKmSzNqLl0Yci3D0 zgVsXFZSadQebietm4CCmfJYREt9NJxXcrLxVDgat/Xm/KJBsohs3f+cbBT8EXer 7+2oZjZoVUgw1hu0alaOvAfE4mxsVwjn3g2mjDqRJLbbuWqgDobjMHah+d4zwJvN ePK8E0hfaz/XBLsJ4e6bQA3M3bANEgSvsicup/qb/0th4gUdc/kj4aJGj0RP7oEC AwEAATANBgkqhkiG9w0BAQUFAAOCAQEADuVec/8u2qJiq6K2W/gSLGYCBZq64OrA s7L2+S82m9/3gAb62wGcDNZjIGFDQubXmO6RhHv7JUT5YZqv9/kRGTJcHDUrwwoN IE99CIPizp7VfnrZ6GsYeszSsw3m+mKTETm+6ELmaSDbYAsrCg4IpGwUF0L88ATv CJ8QzW4X7b9dYVc7UAYyCie2N65GXfesBbRlSwFLuVqIzZfMdNpNijTIUwUqGSME b8IjLYzvekP53CO4wEBRrAVIPNXgftorxIE30OLWua2Qw3y6Pn+Qp5fLe47025S7 Lcec18/FbHG0Vbq0qO9cKQw80XyK31N6z556wr2GN2WyixkzVRddXA== -----END CERTIFICATE----- omniauth_openid_connect-0.3.5/test/fixtures/jwks.json0000644000004100000410000000070213673167754023165 0ustar www-datawww-data{"keys": [{ "kty": "RSA", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e": "AQAB", "alg": "RS256", "kid": "1e9gdk7" }] } omniauth_openid_connect-0.3.5/test/lib/0000755000004100000410000000000013673167754020212 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/test/lib/omniauth/0000755000004100000410000000000013673167754022036 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/test/lib/omniauth/strategies/0000755000004100000410000000000013673167754024210 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/test/lib/omniauth/strategies/openid_connect_test.rb0000644000004100000410000006473413673167754030601 0ustar www-datawww-datarequire_relative '../../../test_helper' module OmniAuth module Strategies class OpenIDConnectTest < StrategyTestCase def test_client_options_defaults assert_equal 'https', strategy.options.client_options.scheme assert_equal 443, strategy.options.client_options.port assert_equal '/authorize', strategy.options.client_options.authorization_endpoint assert_equal '/token', strategy.options.client_options.token_endpoint end def test_request_phase expected_redirect = /^https:\/\/example\.com\/authorize\?client_id=1234&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}$/ strategy.options.issuer = 'example.com' strategy.options.client_options.host = 'example.com' strategy.expects(:redirect).with(regexp_matches(expected_redirect)) strategy.request_phase end def test_logout_phase_with_discovery expected_redirect = %r{^https:\/\/example\.com\/logout$} strategy.options.client_options.host = 'example.com' strategy.options.discovery = true issuer = stub('OpenIDConnect::Discovery::Issuer') issuer.stubs(:issuer).returns('https://example.com/') ::OpenIDConnect::Discovery::Provider.stubs(:discover!).returns(issuer) config = stub('OpenIDConnect::Discovery::Provder::Config') config.stubs(:authorization_endpoint).returns('https://example.com/authorization') config.stubs(:token_endpoint).returns('https://example.com/token') config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo') config.stubs(:jwks_uri).returns('https://example.com/jwks') config.stubs(:end_session_endpoint).returns('https://example.com/logout') ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config) request.stubs(:path_info).returns('/auth/openid_connect/logout') strategy.expects(:redirect).with(regexp_matches(expected_redirect)) strategy.other_phase end def test_logout_phase_with_discovery_and_post_logout_redirect_uri expected_redirect = 'https://example.com/logout?post_logout_redirect_uri=https%3A%2F%2Fmysite.com' strategy.options.client_options.host = 'example.com' strategy.options.discovery = true strategy.options.post_logout_redirect_uri = 'https://mysite.com' issuer = stub('OpenIDConnect::Discovery::Issuer') issuer.stubs(:issuer).returns('https://example.com/') ::OpenIDConnect::Discovery::Provider.stubs(:discover!).returns(issuer) config = stub('OpenIDConnect::Discovery::Provder::Config') config.stubs(:authorization_endpoint).returns('https://example.com/authorization') config.stubs(:token_endpoint).returns('https://example.com/token') config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo') config.stubs(:jwks_uri).returns('https://example.com/jwks') config.stubs(:end_session_endpoint).returns('https://example.com/logout') ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config) request.stubs(:path_info).returns('/auth/openid_connect/logout') strategy.expects(:redirect).with(expected_redirect) strategy.other_phase end def test_logout_phase strategy.options.issuer = 'example.com' strategy.options.client_options.host = 'example.com' request.stubs(:path_info).returns('/auth/openid_connect/logout') strategy.expects(:call_app!) strategy.other_phase end def test_request_phase_with_params expected_redirect = /^https:\/\/example\.com\/authorize\?claims_locales=es&client_id=1234&login_hint=john.doe%40example.com&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}&ui_locales=en$/ strategy.options.issuer = 'example.com' strategy.options.client_options.host = 'example.com' request.stubs(:params).returns('login_hint' => 'john.doe@example.com', 'ui_locales' => 'en', 'claims_locales' => 'es') strategy.expects(:redirect).with(regexp_matches(expected_redirect)) strategy.request_phase end def test_request_phase_with_discovery expected_redirect = /^https:\/\/example\.com\/authorization\?client_id=1234&nonce=\w{32}&response_type=code&scope=openid&state=\w{32}$/ strategy.options.client_options.host = 'example.com' strategy.options.discovery = true issuer = stub('OpenIDConnect::Discovery::Issuer') issuer.stubs(:issuer).returns('https://example.com/') ::OpenIDConnect::Discovery::Provider.stubs(:discover!).returns(issuer) config = stub('OpenIDConnect::Discovery::Provder::Config') config.stubs(:authorization_endpoint).returns('https://example.com/authorization') config.stubs(:token_endpoint).returns('https://example.com/token') config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo') config.stubs(:jwks_uri).returns('https://example.com/jwks') ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config) strategy.expects(:redirect).with(regexp_matches(expected_redirect)) strategy.request_phase assert_equal strategy.options.issuer, 'https://example.com/' assert_equal strategy.options.client_options.authorization_endpoint, 'https://example.com/authorization' assert_equal strategy.options.client_options.token_endpoint, 'https://example.com/token' assert_equal strategy.options.client_options.userinfo_endpoint, 'https://example.com/userinfo' assert_equal strategy.options.client_options.jwks_uri, 'https://example.com/jwks' assert_nil strategy.options.client_options.end_session_endpoint end def test_request_phase_with_response_mode expected_redirect = /^https:\/\/example\.com\/authorize\?client_id=1234&nonce=\w{32}&response_mode=form_post&response_type=id_token&scope=openid&state=\w{32}$/ strategy.options.issuer = 'example.com' strategy.options.response_mode = 'form_post' strategy.options.response_type = 'id_token' strategy.options.client_options.host = 'example.com' strategy.expects(:redirect).with(regexp_matches(expected_redirect)) strategy.request_phase end def test_request_phase_with_response_mode_symbol expected_redirect = /^https:\/\/example\.com\/authorize\?client_id=1234&nonce=\w{32}&response_mode=form_post&response_type=id_token&scope=openid&state=\w{32}$/ strategy.options.issuer = 'example.com' strategy.options.response_mode = 'form_post' strategy.options.response_type = :id_token strategy.options.client_options.host = 'example.com' strategy.expects(:redirect).with(regexp_matches(expected_redirect)) strategy.request_phase end def test_option_acr_values strategy.options.client_options[:host] = 'foobar.com' assert(!(strategy.authorize_uri =~ /acr_values=/), 'URI must not contain acr_values') strategy.options.acr_values = 'urn:some:acr:values:value' assert(strategy.authorize_uri =~ /acr_values=/, 'URI must contain acr_values') end def test_option_custom_attributes strategy.options.client_options[:host] = 'foobar.com' strategy.options.extra_authorize_params = {resource: 'xyz'} assert(strategy.authorize_uri =~ /resource=xyz/, 'URI must contain custom params') end def test_uid assert_equal user_info.sub, strategy.uid strategy.options.uid_field = 'preferred_username' assert_equal user_info.preferred_username, strategy.uid strategy.options.uid_field = 'something' assert_equal user_info.sub, strategy.uid end def test_callback_phase(session = {}, params = {}) code = SecureRandom.hex(16) state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('code' => code, 'state' => state) request.stubs(:path_info).returns('') strategy.options.issuer = 'example.com' strategy.options.client_signing_alg = :RS256 strategy.options.client_jwk_signing_key = File.read('test/fixtures/jwks.json') strategy.options.response_type = 'code' strategy.unstub(:user_info) access_token = stub('OpenIDConnect::AccessToken') access_token.stubs(:access_token) access_token.stubs(:refresh_token) access_token.stubs(:expires_in) access_token.stubs(:scope) access_token.stubs(:id_token).returns(File.read('test/fixtures/id_token.txt')) client.expects(:access_token!).at_least_once.returns(access_token) access_token.expects(:userinfo!).returns(user_info) id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email') id_token.stubs(:verify!).with(issuer: strategy.options.issuer, client_id: @identifier, nonce: nonce).returns(true) id_token.expects(:verify!) strategy.expects(:decode_id_token).twice.with(access_token.id_token).returns(id_token) strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.callback_phase end def test_callback_phase_with_id_token code = SecureRandom.hex(16) state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('id_token' => code, 'state' => state) request.stubs(:path_info).returns('') strategy.options.issuer = 'example.com' strategy.options.client_signing_alg = :RS256 strategy.options.client_jwk_signing_key = File.read('test/fixtures/jwks.json') strategy.options.response_type = 'id_token' strategy.unstub(:user_info) access_token = stub('OpenIDConnect::AccessToken') access_token.stubs(:access_token) access_token.stubs(:refresh_token) access_token.stubs(:expires_in) access_token.stubs(:scope) access_token.stubs(:id_token).returns(File.read('test/fixtures/id_token.txt')) id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email') id_token.stubs(:verify!).with(issuer: strategy.options.issuer, client_id: @identifier, nonce: nonce).returns(true) ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token) id_token.expects(:verify!) strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.callback_phase end def test_callback_phase_with_discovery code = SecureRandom.hex(16) state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) jwks = JSON::JWK::Set.new(JSON.parse(File.read('test/fixtures/jwks.json'))['keys']) request.stubs(:params).returns('code' => code, 'state' => state) request.stubs(:path_info).returns('') strategy.options.client_options.host = 'example.com' strategy.options.discovery = true issuer = stub('OpenIDConnect::Discovery::Issuer') issuer.stubs(:issuer).returns('https://example.com/') ::OpenIDConnect::Discovery::Provider.stubs(:discover!).returns(issuer) config = stub('OpenIDConnect::Discovery::Provder::Config') config.stubs(:authorization_endpoint).returns('https://example.com/authorization') config.stubs(:token_endpoint).returns('https://example.com/token') config.stubs(:userinfo_endpoint).returns('https://example.com/userinfo') config.stubs(:jwks_uri).returns('https://example.com/jwks') config.stubs(:jwks).returns(jwks) ::OpenIDConnect::Discovery::Provider::Config.stubs(:discover!).with('https://example.com/').returns(config) id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:raw_attributes).returns('sub' => 'sub', 'name' => 'name', 'email' => 'email') id_token.stubs(:verify!).with(issuer: 'https://example.com/', client_id: @identifier, nonce: nonce).returns(true) ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token) strategy.unstub(:user_info) access_token = stub('OpenIDConnect::AccessToken') access_token.stubs(:access_token) access_token.stubs(:refresh_token) access_token.stubs(:expires_in) access_token.stubs(:scope) access_token.stubs(:id_token).returns(File.read('test/fixtures/id_token.txt')) client.expects(:access_token!).at_least_once.returns(access_token) access_token.expects(:userinfo!).returns(user_info) strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.callback_phase end def test_callback_phase_with_error state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('error' => 'invalid_request') request.stubs(:path_info).returns('') strategy.call!({'rack.session' => {'omniauth.state' => state, 'omniauth.nonce' => nonce}}) strategy.expects(:fail!) strategy.callback_phase end def test_callback_phase_with_invalid_state code = SecureRandom.hex(16) state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('code' => code, 'state' => 'foobar') request.stubs(:path_info).returns('') strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.expects(:fail!) strategy.callback_phase end def test_callback_phase_without_code state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('state' => state) request.stubs(:path_info).returns('') strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.expects(:fail!).with(:missing_code, is_a(OmniAuth::OpenIDConnect::MissingCodeError)) strategy.callback_phase end def test_callback_phase_without_id_token state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('state' => state) request.stubs(:path_info).returns('') strategy.options.response_type = 'id_token' strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.expects(:fail!).with(:missing_id_token, is_a(OmniAuth::OpenIDConnect::MissingIdTokenError)) strategy.callback_phase end def test_callback_phase_without_id_token_symbol state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('state' => state) request.stubs(:path_info).returns('') strategy.options.response_type = :id_token strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.expects(:fail!).with(:missing_id_token, is_a(OmniAuth::OpenIDConnect::MissingIdTokenError)) strategy.callback_phase end def test_callback_phase_with_timeout code = SecureRandom.hex(16) state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('code' => code, 'state' => state) request.stubs(:path_info).returns('') strategy.options.issuer = 'example.com' strategy.stubs(:access_token).raises(::Timeout::Error.new('error')) strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.expects(:fail!) id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:verify!).with(issuer: 'example.com', client_id: @identifier, nonce: nonce).returns(true) ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token) strategy.callback_phase end def test_callback_phase_with_etimeout code = SecureRandom.hex(16) state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('code' => code, 'state' => state) request.stubs(:path_info).returns('') strategy.options.issuer = 'example.com' strategy.stubs(:access_token).raises(::Errno::ETIMEDOUT.new('error')) strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.expects(:fail!) id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:verify!).with(issuer: 'example.com', client_id: @identifier, nonce: nonce).returns(true) ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token) strategy.callback_phase end def test_callback_phase_with_socket_error code = SecureRandom.hex(16) state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('code' => code, 'state' => state) request.stubs(:path_info).returns('') strategy.options.issuer = 'example.com' strategy.stubs(:access_token).raises(::SocketError.new('error')) strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.expects(:fail!) id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:verify!).with(issuer: 'example.com', client_id: @identifier, nonce: nonce).returns(true) ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token) strategy.callback_phase end def test_callback_phase_with_rack_oauth2_client_error code = SecureRandom.hex(16) state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) request.stubs(:params).returns('code' => code, 'state' => state) request.stubs(:path_info).returns('') strategy.options.issuer = 'example.com' strategy.stubs(:access_token).raises(::Rack::OAuth2::Client::Error.new('error', error: 'Unknown')) strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.expects(:fail!) id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:verify!).with(issuer: 'example.com', client_id: @identifier, nonce: nonce).returns(true) ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token) strategy.callback_phase end def test_info info = strategy.info assert_equal user_info.name, info[:name] assert_equal user_info.email, info[:email] assert_equal user_info.preferred_username, info[:nickname] assert_equal user_info.given_name, info[:first_name] assert_equal user_info.family_name, info[:last_name] assert_equal user_info.gender, info[:gender] assert_equal user_info.picture, info[:image] assert_equal user_info.phone_number, info[:phone] assert_equal({ website: user_info.website }, info[:urls]) end def test_extra assert_equal({ raw_info: user_info.as_json }, strategy.extra) end def test_credentials strategy.options.issuer = 'example.com' strategy.options.client_signing_alg = :RS256 strategy.options.client_jwk_signing_key = File.read('test/fixtures/jwks.json') id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:verify!).returns(true) ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token) access_token = stub('OpenIDConnect::AccessToken') access_token.stubs(:access_token).returns(SecureRandom.hex(16)) access_token.stubs(:refresh_token).returns(SecureRandom.hex(16)) access_token.stubs(:expires_in).returns(Time.now) access_token.stubs(:scope).returns('openidconnect') access_token.stubs(:id_token).returns(File.read('test/fixtures/id_token.txt')) client.expects(:access_token!).returns(access_token) access_token.expects(:refresh_token).returns(access_token.refresh_token) access_token.expects(:expires_in).returns(access_token.expires_in) assert_equal( { id_token: access_token.id_token, token: access_token.access_token, refresh_token: access_token.refresh_token, expires_in: access_token.expires_in, scope: access_token.scope }, strategy.credentials ) end def test_option_send_nonce strategy.options.client_options[:host] = 'foobar.com' assert(strategy.authorize_uri =~ /nonce=/, 'URI must contain nonce') strategy.options.send_nonce = false assert(!(strategy.authorize_uri =~ /nonce=/), 'URI must not contain nonce') end def test_failure_endpoint_redirect OmniAuth.config.stubs(:failure_raise_out_environments).returns([]) strategy.stubs(:env).returns({}) request.stubs(:params).returns('error' => 'access denied') result = strategy.callback_phase assert(result.is_a? Array) assert(result[0] == 302, 'Redirect') assert(result[1]["Location"] =~ /\/auth\/failure/) end def test_state strategy.options.state = -> { 42 } expected_redirect = /&state=42/ strategy.options.issuer = 'example.com' strategy.options.client_options.host = 'example.com' strategy.expects(:redirect).with(regexp_matches(expected_redirect)) strategy.request_phase session = { 'state' => 42 } # this should succeed as the correct state is passed with the request test_callback_phase(session, { 'state' => 42 }) # the following should fail because the wrong state is passed to the callback code = SecureRandom.hex(16) request.stubs(:params).returns('code' => code, 'state' => 43) request.stubs(:path_info).returns('') strategy.call!('rack.session' => session) strategy.expects(:fail!) strategy.callback_phase end def test_dynamic_state # Stub request parameters request.stubs(:path_info).returns('') strategy.call!('rack.session' => { }, QUERY_STRING: { state: 'abc', client_id: '123' } ) strategy.options.state = lambda { |env| # Get params from request, e.g. CGI.parse(env['QUERY_STRING']) env[:QUERY_STRING][:state] + env[:QUERY_STRING][:client_id] } expected_redirect = /&state=abc123/ strategy.options.issuer = 'example.com' strategy.options.client_options.host = 'example.com' strategy.expects(:redirect).with(regexp_matches(expected_redirect)) strategy.request_phase end def test_option_client_auth_method state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) opts = strategy.options.client_options opts[:host] = 'foobar.com' strategy.options.issuer = 'foobar.com' strategy.options.client_auth_method = :not_basic strategy.options.client_signing_alg = :RS256 strategy.options.client_jwk_signing_key = File.read('test/fixtures/jwks.json') json_response = { access_token: 'test_access_token', id_token: File.read('test/fixtures/id_token.txt'), token_type: 'Bearer', }.to_json success = Struct.new(:status, :body).new(200, json_response) request.stubs(:path_info).returns('') strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:verify!).with(issuer: strategy.options.issuer, client_id: @identifier, nonce: nonce).returns(true) ::OpenIDConnect::ResponseObject::IdToken.stubs(:decode).returns(id_token) HTTPClient.any_instance.stubs(:post).with( "#{ opts.scheme }://#{ opts.host }:#{ opts.port }#{ opts.token_endpoint }", { scope: 'openid', grant_type: :client_credentials, client_id: @identifier, client_secret: @secret }, {} ).returns(success) assert(strategy.send :access_token) end def test_public_key_with_jwks strategy.options.client_signing_alg = :RS256 strategy.options.client_jwk_signing_key = File.read('./test/fixtures/jwks.json') assert_equal JSON::JWK::Set, strategy.public_key.class end def test_public_key_with_jwk strategy.options.client_signing_alg = :RS256 jwks_str = File.read('./test/fixtures/jwks.json') jwks = JSON.parse(jwks_str) jwk = jwks['keys'].first strategy.options.client_jwk_signing_key = jwk.to_json assert_equal JSON::JWK, strategy.public_key.class end def test_public_key_with_x509 strategy.options.client_signing_alg = :RS256 strategy.options.client_x509_signing_key = File.read('./test/fixtures/test.crt') assert_equal OpenSSL::PKey::RSA, strategy.public_key.class end def test_public_key_with_hmac strategy.options.client_options.secret = 'secret' strategy.options.client_signing_alg = :HS256 assert_equal strategy.options.client_options.secret, strategy.public_key end def test_id_token_auth_hash state = SecureRandom.hex(16) nonce = SecureRandom.hex(16) strategy.options.response_type = 'id_token' strategy.options.issuer = 'example.com' id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:verify!).returns(true) id_token.stubs(:raw_attributes, :to_h).returns( { "iss": "http://server.example.com", "sub": "248289761001", "aud": "s6BhdRkqt3", "nonce": "n-0S6_WzA2Mj", "exp": 1311281970, "iat": 1311280970, } ) request.stubs(:params).returns('state' => state, 'nounce' => nonce, 'id_token' => id_token) request.stubs(:path_info).returns('') strategy.stubs(:decode_id_token).returns(id_token) strategy.stubs(:stored_state).returns(state) strategy.call!('rack.session' => { 'omniauth.state' => state, 'omniauth.nonce' => nonce }) strategy.callback_phase auth_hash = strategy.send(:env)['omniauth.auth'] assert auth_hash.key?('provider') assert auth_hash.key?('uid') assert auth_hash.key?('info') assert auth_hash.key?('extra') assert auth_hash['extra'].key?('raw_info') end end end end omniauth_openid_connect-0.3.5/test/strategy_test_case.rb0000644000004100000410000000255413673167754023673 0ustar www-datawww-dataclass StrategyTestCase < MiniTest::Test class DummyApp def call(env); end end attr_accessor :identifier, :secret def setup @identifier = '1234' @secret = '1234asdgat3' end def client strategy.client end def user_info @user_info ||= OpenIDConnect::ResponseObject::UserInfo.new( sub: SecureRandom.hex(16), name: Faker::Name.name, email: Faker::Internet.email, nickname: Faker::Name.first_name, preferred_username: Faker::Internet.user_name, given_name: Faker::Name.first_name, family_name: Faker::Name.last_name, gender: 'female', picture: Faker::Internet.url + '.png', phone_number: Faker::PhoneNumber.phone_number, website: Faker::Internet.url, ) end def request @request ||= stub('Request').tap do |request| request.stubs(:params).returns({}) request.stubs(:cookies).returns({}) request.stubs(:env).returns({}) request.stubs(:scheme).returns({}) request.stubs(:ssl?).returns(false) end end def strategy @strategy ||= OmniAuth::Strategies::OpenIDConnect.new(DummyApp.new).tap do |strategy| strategy.options.client_options.identifier = @identifier strategy.options.client_options.secret = @secret strategy.stubs(:request).returns(request) strategy.stubs(:user_info).returns(user_info) end end end omniauth_openid_connect-0.3.5/test/test_helper.rb0000644000004100000410000000062413673167754022311 0ustar www-datawww-datalib = File.expand_path('../../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'simplecov' require 'coveralls' require 'minitest/autorun' require 'mocha/minitest' require 'faker' require 'active_support' require 'omniauth_openid_connect' require_relative 'strategy_test_case' SimpleCov.command_name 'test' SimpleCov.start Coveralls.wear! OmniAuth.config.test_mode = true omniauth_openid_connect-0.3.5/README.md0000644000004100000410000002424713673167754017755 0ustar www-datawww-data# OmniAuth::OpenIDConnect Originally was [omniauth-openid-connect](https://github.com/jjbohn/omniauth-openid-connect) I've forked this repository and launch as separate gem because maintaining of original was dropped. [![Build Status](https://travis-ci.org/m0n9oose/omniauth_openid_connect.png?branch=master)](https://travis-ci.org/m0n9oose/omniauth_openid_connect) ## Installation Add this line to your application's Gemfile: gem 'omniauth_openid_connect' And then execute: $ bundle Or install it yourself as: $ gem install omniauth_openid_connect ## Supported Ruby Versions OmniAuth::OpenIDConnect is tested under 2.4, 2.5, 2.6, 2.7 ## Usage Example configuration ```ruby config.omniauth :openid_connect, { name: :my_provider, scope: [:openid, :email, :profile, :address], response_type: :code, uid_field: "preferred_username", client_options: { port: 443, scheme: "https", host: "myprovider.com", identifier: ENV["OP_CLIENT_ID"], secret: ENV["OP_SECRET_KEY"], redirect_uri: "http://myapp.com/users/auth/openid_connect/callback", }, } ``` ### Options Overview | Field | Description | Required | Default | Example/Options | |------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|----------------------------|-----------------------------------------------------| | name | Arbitrary string to identify connection and identify it from other openid_connect providers | no | String: openid_connect | :my_idp | | issuer | Root url for the authorization server | yes | | https://myprovider.com | | discovery | Should OpenID discovery be used. This is recommended if the IDP provides a discovery endpoint. See client config for how to manually enter discovered values. | no | false | one of: true, false | | client_auth_method | Which authentication method to use to authenticate your app with the authorization server | no | Sym: basic | "basic", "jwks" | | scope | Which OpenID scopes to include (:openid is always required) | no | Array [:openid] | [:openid, :profile, :email] | | response_type | Which OAuth2 response type to use with the authorization request | no | String: code | one of: 'code', 'id_token' | | state | A value to be used for the OAuth2 state parameter on the authorization request. Can be a proc that generates a string. | no | Random 16 character string | Proc.new { SecureRandom.hex(32) } | | response_mode | The response mode per [spec](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html) | no | nil | one of: :query, :fragment, :form_post, :web_message | | display | An optional parameter to the authorization request to determine how the authorization and consent page | no | nil | one of: :page, :popup, :touch, :wap | | prompt | An optional parameter to the authrization request to determine what pages the user will be shown | no | nil | one of: :none, :login, :consent, :select_account | | send_scope_to_token_endpoint | Should the scope parameter be sent to the authorization token endpoint? | no | true | one of: true, false | | post_logout_redirect_uri | The logout redirect uri to use per the [session management draft](https://openid.net/specs/openid-connect-session-1_0.html) | no | empty | https://myapp.com/logout/callback | | uid_field | The field of the user info response to be used as a unique id | no | 'sub' | "sub", "preferred_username" | | client_options | A hash of client options detailed in its own section | yes | | | ### Client Config Options These are the configuration options for the client_options hash of the configuration. | Field | Description | Default | Replaced by discovery? | |------------------------|-----------------------------------------------------------------|------------|------------------------| | identifier | The OAuth2 client_id | | | | secret | The OAuth2 client secret | | | | redirect_uri | The OAuth2 authorization callback url in your app | | | | scheme | The http scheme to use | https | | | host | The host of the authorization server | nil | | | port | The port for the authorization server | 443 | | | authorization_endpoint | The authorize endpoint on the authorization server | /authorize | yes | | token_endpoint | The token endpoint on the authorization server | /token | yes | | userinfo_endpoint | The user info endpoint on the authorization server | /userinfo | yes | | jwks_uri | The jwks_uri on the authorization server | /jwk | yes | | end_session_endpoint | The url to call to log the user out at the authorization server | nil | yes | ### Additional Configuration Notes * `name` is arbitrary, I recommend using the name of your provider. The name configuration exists because you could be using multiple OpenID Connect providers in a single app. **NOTE**: if you use this gem with Devise you should use `:openid_connect` name, or Devise would route to 'users/auth/:provider' rather than 'users/auth/openid_connect' * `response_type` tells the authorization server which grant type the application wants to use, currently, only `:code` (Authorization Code grant) and `:id_token` (Implicit grant) are valid. * If you want to pass `state` paramete by yourself. You can set Proc Object. e.g. `state: Proc.new { SecureRandom.hex(32) }` * `nonce` is optional. If don't want to pass "nonce" parameter to provider, You should specify `false` to `send_nonce` option. (default true) * Support for other client authentication methods. If don't specified `:client_auth_method` option, automatically set `:basic`. * Use "OpenID Connect Discovery", You should specify `true` to `discovery` option. (default false) * In "OpenID Connect Discovery", generally provider should have Webfinger endpoint. If provider does not have Webfinger endpoint, You can specify "Issuer" to option. e.g. `issuer: "https://myprovider.com"` It means to get configuration from "https://myprovider.com/.well-known/openid-configuration". * The uid is by default using the `sub` value from the `user_info` response, which in some applications is not the expected value. To avoid such limitations, the uid label can be configured by providing the omniauth `uid_field` option to a different label (i.e. `preferred_username`) that appears in the `user_info` details. * The `issuer` property should exactly match the provider's issuer link. * The `response_mode` option is optional and specifies how the result of the authorization request is formatted. * Some OpenID Connect providers require the `scope` attribute in requests to the token endpoint, even if this is not in the protocol specifications. In those cases, the `send_scope_to_token_endpoint` property can be used to add the attribute to the token request. Initial value is `true`, which means that the scope attribute is included by default. For the full low down on OpenID Connect, please check out [the spec](http://openid.net/specs/openid-connect-core-1_0.html). ## Contributing 1. Fork it ( http://github.com/m0n9oose/omniauth-openid-connect/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Cover your changes with tests and make sure they're green (`bundle install && bundle exec rake test`) 4. Commit your changes (`git commit -am 'Add some feature'`) 5. Push to the branch (`git push origin my-new-feature`) 6. Create new Pull Request omniauth_openid_connect-0.3.5/CHANGELOG.md0000644000004100000410000000636613673167754020311 0ustar www-datawww-data# v0.3.5 (07.06.2020) - bugfix: Info from decoded id_token is not exposed into `request.env['omniauth.auth']` [#61](https://github.com/m0n9oose/omniauth_openid_connect/pull/61) - bugfix: NoMethodError (`undefined method 'count' for #`) [#60](https://github.com/m0n9oose/omniauth_openid_connect/pull/60) # v0.3.4 (21.05.2020) - Try to verify id_token when response_type is code [#44](https://github.com/m0n9oose/omniauth_openid_connect/pull/44) - Provide more information on error [#49](https://github.com/m0n9oose/omniauth_openid_connect/pull/49) - Update configuration documentation [#53](https://github.com/m0n9oose/omniauth_openid_connect/pull/53) - Add documentation about the send_scope_to_token_endpoint config property [#52](https://github.com/m0n9oose/omniauth_openid_connect/pull/52) - refactor: take uid_field from raw_attributes [#54](https://github.com/m0n9oose/omniauth_openid_connect/pull/54) - chore(ci): add 2.7, ruby-head and jruby-head [#55](https://github.com/m0n9oose/omniauth_openid_connect/pull/55) # v0.3.3 (09.11.2019) - Pass `acr_values` to authorize url [#43](https://github.com/m0n9oose/omniauth_openid_connect/pull/43) - Add raw info for id token [#42](https://github.com/m0n9oose/omniauth_openid_connect/pull/42) - Fixed `id_token` verification when `id_token` is not used [#41](https://github.com/m0n9oose/omniauth_openid_connect/pull/41) - Cast `response_type` to string when checking if it is set in params [#36](https://github.com/m0n9oose/omniauth_openid_connect/pull/36) - Support both symbol and string version of `response_type` option [#35](https://github.com/m0n9oose/omniauth_openid_connect/pull/35) - Fix gemspec homepage [#33](https://github.com/m0n9oose/omniauth_openid_connect/pull/33) - Add support for `response_type` `id_token` [#32](https://github.com/m0n9oose/omniauth_openid_connect/pull/32) # v0.3.2 (03.08.2019) - Use response_mode in `authorize_uri` if the option is defined [#30](https://github.com/m0n9oose/omniauth_openid_connect/pull/30) - Move verification of `id_token` to before accessing tokens [#28](https://github.com/m0n9oose/omniauth_openid_connect/pull/28) - Update omniauth dependency [#26](https://github.com/m0n9oose/omniauth_openid_connect/pull/26) # v0.3.1 (08.06.2019) - Set default OmniAuth name to openid_connect [#23](https://github.com/m0n9oose/omniauth_openid_connect/pull/23) # v0.3.0 (27.04.2019) - RP-Initiated Logout phase [#5](https://github.com/m0n9oose/omniauth_openid_connect/pull/5) - Allows `ui_locales`, `claims_locales` and `login_hint` as request params [#6](https://github.com/m0n9oose/omniauth_openid_connect/pull/6) - Make uid label configurable [#11](https://github.com/m0n9oose/omniauth_openid_connect/pull/11) - Allow rails applications to handle state mismatch [#14](https://github.com/m0n9oose/omniauth_openid_connect/pull/14) - Handle errors when fetching access_token at callback_phase [#17](https://github.com/m0n9oose/omniauth_openid_connect/pull/17) - Allow state method to receive env [#19](https://github.com/m0n9oose/omniauth_openid_connect/pull/19) # v0.2.4 (06.01.2019) - Prompt and login hint [#4](https://github.com/m0n9oose/omniauth_openid_connect/pull/4) - Bump openid_connect dependency [#9](https://github.com/m0n9oose/omniauth_openid_connect/pull/9) omniauth_openid_connect-0.3.5/.rubocop.yml0000644000004100000410000000202213673167754020733 0ustar www-datawww-dataLineLength: Description: 'Limit lines to 130 characters.' Max: 130 Layout/SpaceInsideStringInterpolation: Enabled: false Layout/MultilineOperationIndentation: EnforcedStyle: indented StringLiterals: EnforcedStyle: single_quotes Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: comma Style/TrailingCommaInHashLiteral: EnforcedStyleForMultiline: comma Style/SafeNavigation: Enabled: false Style/EmptyMethod: Description: 'Checks the formatting of empty method definitions.' StyleGuide: '#no-single-line-methods' Enabled: false HashSyntax: Description: "Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax\n{ :a => 1, :b => 2 }" EnforcedStyle: ruby19 Enabled: true RedundantBegin: Enabled: true Documentation: Enabled: false Metrics/AbcSize: Max: 50 Metrics/CyclomaticComplexity: Max: 50 Metrics/PerceivedComplexity: Max: 15 Metrics/BlockLength: Max: 40 Metrics/MethodLength: Max: 45 AllCops: Exclude: - bin/**/* - Rakefile - config/**/* - test/**/* omniauth_openid_connect-0.3.5/.gitignore0000644000004100000410000000027313673167754020457 0ustar www-datawww-data*.gem *.rbc .bundle .config .idea .yardoc InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp .ruby-version .ruby-gemset Gemfile.lock omniauth_openid_connect-0.3.5/Rakefile0000644000004100000410000000031213673167754020126 0ustar www-datawww-datarequire 'bundler/gem_tasks' require 'rake/testtask' Rake::TestTask.new do |t| t.libs << 'test' t.test_files = FileList['test/lib/omniauth/**/*_test.rb'] t.verbose = true end task default: :test omniauth_openid_connect-0.3.5/lib/0000755000004100000410000000000013673167754017233 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/lib/omniauth/0000755000004100000410000000000013673167754021057 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/lib/omniauth/strategies/0000755000004100000410000000000013673167754023231 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/lib/omniauth/strategies/openid_connect.rb0000644000004100000410000002566013673167754026556 0ustar www-datawww-data# frozen_string_literal: true require 'addressable/uri' require 'timeout' require 'net/http' require 'open-uri' require 'omniauth' require 'openid_connect' require 'forwardable' module OmniAuth module Strategies class OpenIDConnect include OmniAuth::Strategy extend Forwardable RESPONSE_TYPE_EXCEPTIONS = { 'id_token' => { exception_class: OmniAuth::OpenIDConnect::MissingIdTokenError, key: :missing_id_token }.freeze, 'code' => { exception_class: OmniAuth::OpenIDConnect::MissingCodeError, key: :missing_code }.freeze, }.freeze def_delegator :request, :params option :name, 'openid_connect' option(:client_options, identifier: nil, secret: nil, redirect_uri: nil, scheme: 'https', host: nil, port: 443, authorization_endpoint: '/authorize', token_endpoint: '/token', userinfo_endpoint: '/userinfo', jwks_uri: '/jwk', end_session_endpoint: nil) option :issuer option :discovery, false option :client_signing_alg option :client_jwk_signing_key option :client_x509_signing_key option :scope, [:openid] option :response_type, 'code' # ['code', 'id_token'] option :state option :response_mode # [:query, :fragment, :form_post, :web_message] option :display, nil # [:page, :popup, :touch, :wap] option :prompt, nil # [:none, :login, :consent, :select_account] option :hd, nil option :max_age option :ui_locales option :id_token_hint option :acr_values option :send_nonce, true option :send_scope_to_token_endpoint, true option :client_auth_method option :post_logout_redirect_uri option :extra_authorize_params, {} option :uid_field, 'sub' def uid user_info.raw_attributes[options.uid_field.to_sym] || user_info.sub end info do { name: user_info.name, email: user_info.email, nickname: user_info.preferred_username, first_name: user_info.given_name, last_name: user_info.family_name, gender: user_info.gender, image: user_info.picture, phone: user_info.phone_number, urls: { website: user_info.website }, } end extra do { raw_info: user_info.raw_attributes } end credentials do { id_token: access_token.id_token, token: access_token.access_token, refresh_token: access_token.refresh_token, expires_in: access_token.expires_in, scope: access_token.scope, } end def client @client ||= ::OpenIDConnect::Client.new(client_options) end def config @config ||= ::OpenIDConnect::Discovery::Provider::Config.discover!(options.issuer) end def request_phase options.issuer = issuer if options.issuer.to_s.empty? discover! redirect authorize_uri end def callback_phase error = params['error_reason'] || params['error'] error_description = params['error_description'] || params['error_reason'] invalid_state = params['state'].to_s.empty? || params['state'] != stored_state raise CallbackError, error: params['error'], reason: error_description, uri: params['error_uri'] if error raise CallbackError, error: :csrf_detected, reason: "Invalid 'state' parameter" if invalid_state return unless valid_response_type? options.issuer = issuer if options.issuer.nil? || options.issuer.empty? verify_id_token!(params['id_token']) if configured_response_type == 'id_token' discover! client.redirect_uri = redirect_uri return id_token_callback_phase if configured_response_type == 'id_token' client.authorization_code = authorization_code access_token super rescue CallbackError => e fail!(e.error, e) rescue ::Rack::OAuth2::Client::Error => e fail!(e.response[:error], e) rescue ::Timeout::Error, ::Errno::ETIMEDOUT => e fail!(:timeout, e) rescue ::SocketError => e fail!(:failed_to_connect, e) end def other_phase if logout_path_pattern.match?(current_path) options.issuer = issuer if options.issuer.to_s.empty? discover! return redirect(end_session_uri) if end_session_uri end call_app! end def authorization_code params['code'] end def end_session_uri return unless end_session_endpoint_is_valid? end_session_uri = URI(client_options.end_session_endpoint) end_session_uri.query = encoded_post_logout_redirect_uri end_session_uri.to_s end def authorize_uri client.redirect_uri = redirect_uri opts = { response_type: options.response_type, response_mode: options.response_mode, scope: options.scope, state: new_state, login_hint: params['login_hint'], ui_locales: params['ui_locales'], claims_locales: params['claims_locales'], prompt: options.prompt, nonce: (new_nonce if options.send_nonce), hd: options.hd, acr_values: options.acr_values, } opts.merge!(options.extra_authorize_params) unless options.extra_authorize_params.empty? client.authorization_uri(opts.reject { |_k, v| v.nil? }) end def public_key return config.jwks if options.discovery key_or_secret end private def issuer resource = "#{ client_options.scheme }://#{ client_options.host }" resource = "#{ resource }:#{ client_options.port }" if client_options.port ::OpenIDConnect::Discovery::Provider.discover!(resource).issuer end def discover! return unless options.discovery client_options.authorization_endpoint = config.authorization_endpoint client_options.token_endpoint = config.token_endpoint client_options.userinfo_endpoint = config.userinfo_endpoint client_options.jwks_uri = config.jwks_uri client_options.end_session_endpoint = config.end_session_endpoint if config.respond_to?(:end_session_endpoint) end def user_info return @user_info if @user_info if access_token.id_token decoded = decode_id_token(access_token.id_token).raw_attributes @user_info = ::OpenIDConnect::ResponseObject::UserInfo.new access_token.userinfo!.raw_attributes.merge(decoded) else @user_info = access_token.userinfo! end end def access_token return @access_token if @access_token @access_token = client.access_token!( scope: (options.scope if options.send_scope_to_token_endpoint), client_auth_method: options.client_auth_method ) verify_id_token!(@access_token.id_token) if configured_response_type == 'code' @access_token end def decode_id_token(id_token) ::OpenIDConnect::ResponseObject::IdToken.decode(id_token, public_key) end def client_options options.client_options end def new_state state = if options.state.respond_to?(:call) if options.state.arity == 1 options.state.call(env) else options.state.call end end session['omniauth.state'] = state || SecureRandom.hex(16) end def stored_state session.delete('omniauth.state') end def new_nonce session['omniauth.nonce'] = SecureRandom.hex(16) end def stored_nonce session.delete('omniauth.nonce') end def session return {} if @env.nil? super end def key_or_secret case options.client_signing_alg when :HS256, :HS384, :HS512 client_options.secret when :RS256, :RS384, :RS512 if options.client_jwk_signing_key parse_jwk_key(options.client_jwk_signing_key) elsif options.client_x509_signing_key parse_x509_key(options.client_x509_signing_key) end end end def parse_x509_key(key) OpenSSL::X509::Certificate.new(key).public_key end def parse_jwk_key(key) json = JSON.parse(key) return JSON::JWK::Set.new(json['keys']) if json.key?('keys') JSON::JWK.new(json) end def decode(str) UrlSafeBase64.decode64(str).unpack1('B*').to_i(2).to_s end def redirect_uri return client_options.redirect_uri unless params['redirect_uri'] "#{ client_options.redirect_uri }?redirect_uri=#{ CGI.escape(params['redirect_uri']) }" end def encoded_post_logout_redirect_uri return unless options.post_logout_redirect_uri URI.encode_www_form( post_logout_redirect_uri: options.post_logout_redirect_uri ) end def end_session_endpoint_is_valid? client_options.end_session_endpoint && client_options.end_session_endpoint =~ URI::DEFAULT_PARSER.make_regexp end def logout_path_pattern @logout_path_pattern ||= %r{\A#{Regexp.quote(request_path)}(/logout)} end def id_token_callback_phase user_data = decode_id_token(params['id_token']).raw_attributes env['omniauth.auth'] = AuthHash.new( provider: name, uid: user_data['sub'], info: { name: user_data['name'], email: user_data['email'] }, extra: { raw_info: user_data } ) call_app! end def valid_response_type? return true if params.key?(configured_response_type) error_attrs = RESPONSE_TYPE_EXCEPTIONS[configured_response_type] fail!(error_attrs[:key], error_attrs[:exception_class].new(params['error'])) false end def configured_response_type @configured_response_type ||= options.response_type.to_s end def verify_id_token!(id_token) return unless id_token decode_id_token(id_token).verify!(issuer: options.issuer, client_id: client_options.identifier, nonce: stored_nonce) end class CallbackError < StandardError attr_accessor :error, :error_reason, :error_uri def initialize(data) self.error = data[:error] self.error_reason = data[:reason] self.error_uri = data[:uri] end def message [error, error_reason, error_uri].compact.join(' | ') end end end end end OmniAuth.config.add_camelization 'openid_connect', 'OpenIDConnect' omniauth_openid_connect-0.3.5/lib/omniauth/openid_connect.rb0000644000004100000410000000023713673167754024375 0ustar www-datawww-data# frozen_string_literal: true require 'omniauth/openid_connect/errors' require 'omniauth/openid_connect/version' require 'omniauth/strategies/openid_connect' omniauth_openid_connect-0.3.5/lib/omniauth/openid_connect/0000755000004100000410000000000013673167754024046 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/lib/omniauth/openid_connect/version.rb0000644000004100000410000000014613673167754026061 0ustar www-datawww-data# frozen_string_literal: true module OmniAuth module OpenIDConnect VERSION = '0.3.5' end end omniauth_openid_connect-0.3.5/lib/omniauth/openid_connect/errors.rb0000644000004100000410000000030713673167754025707 0ustar www-datawww-data# frozen_string_literal: true module OmniAuth module OpenIDConnect class Error < RuntimeError; end class MissingCodeError < Error; end class MissingIdTokenError < Error; end end end omniauth_openid_connect-0.3.5/lib/omniauth_openid_connect.rb0000644000004100000410000000010113673167754024443 0ustar www-datawww-data# frozen_string_literal: true require 'omniauth/openid_connect' omniauth_openid_connect-0.3.5/Guardfile0000644000004100000410000000055713673167754020321 0ustar www-datawww-data# frozen_string_literal: true # A sample Guardfile # More info at https://github.com/guard/guard#readme guard 'minitest' do # with Minitest::Unit watch(%r{^test/(.*)\/(.*)_test\.rb}) watch(%r{^lib/(.*)\.rb}) { |m| "test/lib/#{m[1]}_test.rb" } watch(%r{^test/test_helper\.rb}) { 'test' } end guard :bundler do watch('Gemfile') watch(/^.+\.gemspec/) end omniauth_openid_connect-0.3.5/Gemfile0000644000004100000410000000010513673167754017754 0ustar www-datawww-data# frozen_string_literal: true source 'https://rubygems.org' gemspec omniauth_openid_connect-0.3.5/.github/0000755000004100000410000000000013673167754020025 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/.github/stale.yml0000644000004100000410000000125413673167754021662 0ustar www-datawww-data# Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - security # Label to use when marking an issue as stale staleLabel: wontfix # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false omniauth_openid_connect-0.3.5/.github/workflows/0000755000004100000410000000000013673167754022062 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/.github/workflows/rubocop.yml0000644000004100000410000000073213673167754024260 0ustar www-datawww-dataname: Rubocop check on: pull_request: branches: - "*" push: branches: - master jobs: build: name: RuboCop Action runs-on: ubuntu-latest steps: - name: Checkout Action uses: actions/checkout@v1 - name: Rubocop Linter Action uses: andrewmcodes/rubocop-linter-action@v3.2.0 with: action_config_path: '.github/config/rubocop_linter_action.yml' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} omniauth_openid_connect-0.3.5/.github/config/0000755000004100000410000000000013673167754021272 5ustar www-datawww-dataomniauth_openid_connect-0.3.5/.github/config/rubocop_linter_action.yml0000644000004100000410000000512013673167754026376 0ustar www-datawww-data# Description: The name of the check that will be created. # Valid Options: A reasonably sized string. # Default: 'Rubocop Action' check_name: 'Rubocop Results' # Description: Versions required to run your RuboCop checks. # Valid options: RuboCop and any RuboCop extension, by default the latest gem version will be used. You can explicitly state that # (not required) or use a version number, like '1.5.1'. # Default: # versions: # - rubocop: 'latest' versions: - rubocop - rubocop-minitest - rubocop-performance: '1.5.1' # Description: Rubocop configuration file path relative to the workspace. # Valid options: A valid file path inside of the workspace. # Default: nil # Note: This does not need to be filled out for Rubocop to still find your config. # Resource: https://rubocop.readthedocs.io/en/stable/configuration/ rubocop_config_path: '.rubocop.yml' # Run all cops enabled by configuration except this list. # Valid options: list of valid cop(s) and/or departments. # Default: nil # Resource: https://rubocop.readthedocs.io/en/stable/cops/ # rubocop_excluded_cops: # - 'Style/FrozenStringLiteralComment' # Minimum severity for exit with error code # Valid options: 'refactor', 'convention', 'warning', 'error', or 'fatal'. # Default: 'warning' # Resource: https://rubocop.readthedocs.io/en/stable/configuration/#severity # rubocop_fail_level: 'warning' # Whether or not to use --force-exclusion when building the rubocop command. Use this if you are only linting modified # files and typically excluded files have been changed. For example, if you exclude db/schema.rb in your rubocop.yml # but a change gets made, then with the check_scope config set to 'modified' rubocop will lint db/schema.rb. If you set # this to true, rubocop will ignore it. # Valid options: true || false # Default: false # Instead of installing gems from rubygems, we can run `bundle install` on your project, # you would need to do this if you are using something like 'rubocop-github' or if you don't # want to list out dependencies with the `versions` key. # Valid options: true || false # Default: false # bundle: false # The scope of code that Rubocop should lint. Use this if you only want to lint changed files. If this is not set # or not equal to 'modified', Rubocop is run against the entire codebase. Note that this will not work on the master branch. # Valid options: 'modified' # Default: nil # The base branch against which changes will be compared, if check_scope config is set to 'modified'. # This setting is not used if check_scope != 'modified'. # Valid options: 'origin/another_branch' # Default: 'origin/master' omniauth_openid_connect-0.3.5/omniauth_openid_connect.gemspec0000644000004100000410000000306113673167754024725 0ustar www-datawww-data# frozen_string_literal: true lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'omniauth/openid_connect/version' Gem::Specification.new do |spec| spec.name = 'omniauth_openid_connect' spec.version = OmniAuth::OpenIDConnect::VERSION spec.authors = ['John Bohn', 'Ilya Shcherbinin'] spec.email = ['jjbohn@gmail.com', 'm0n9oose@gmail.com'] spec.summary = 'OpenID Connect Strategy for OmniAuth' spec.description = 'OpenID Connect Strategy for OmniAuth.' spec.homepage = 'https://github.com/m0n9oose/omniauth_openid_connect' spec.license = 'MIT' spec.files = `git ls-files -z`.split("\x0") spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.add_dependency 'addressable', '~> 2.5' spec.add_dependency 'omniauth', '~> 1.9' spec.add_dependency 'openid_connect', '~> 1.1' spec.add_development_dependency 'coveralls', '~> 0.8' spec.add_development_dependency 'faker', '~> 1.6' spec.add_development_dependency 'guard', '~> 2.14' spec.add_development_dependency 'guard-bundler', '~> 2.2' spec.add_development_dependency 'guard-minitest', '~> 2.4' spec.add_development_dependency 'minitest', '~> 5.1' spec.add_development_dependency 'mocha', '~> 1.7' spec.add_development_dependency 'rake', '~> 12.0' spec.add_development_dependency 'rubocop', '~> 0.63' spec.add_development_dependency 'simplecov', '~> 0.12' end omniauth_openid_connect-0.3.5/LICENSE.txt0000644000004100000410000000205213673167754020307 0ustar www-datawww-dataCopyright (c) 2014 John Bohn MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.