class OmniAuth::Strategies::OpenIDConnect
Constants
- RESPONSE_TYPE_EXCEPTIONS
Public Instance Methods
callback_phase()
click to toggle source
Calls superclass method
# File lib/omniauth/strategies/openid_connect.rb, line 110 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
client()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 96 def client @client ||= ::OpenIDConnect::Client.new(client_options) end
config()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 100 def config @config ||= ::OpenIDConnect::Discovery::Provider::Config.discover!(options.issuer) end
end_session_uri()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 154 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
other_phase()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 141 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
public_key()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 183 def public_key @public_key ||= begin if options.discovery config.jwks elsif configured_public_key configured_public_key elsif client_options.jwks_uri fetch_key end end end
request_phase()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 104 def request_phase options.issuer = issuer if options.issuer.to_s.empty? discover! redirect authorize_uri end
secret()
click to toggle source
Some OpenID providers use the OAuth2 client secret as the shared secret, but Keycloak uses a separate key that's stored inside the database.
# File lib/omniauth/strategies/openid_connect.rb, line 197 def secret options.jwt_secret || base64_decoded_jwt_secret || client_options.secret end
uid()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 63 def uid user_info.raw_attributes[options.uid_field.to_sym] || user_info.sub end
Private Instance Methods
access_token()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 241 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
base64_decoded_jwt_secret()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 203 def base64_decoded_jwt_secret return unless options.jwt_secret_base64 Base64.decode64(options.jwt_secret_base64) end
client_options()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 312 def client_options options.client_options end
configured_public_key()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 345 def configured_public_key @configured_public_key ||= begin 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
configured_response_type()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 413 def configured_response_type @configured_response_type ||= options.response_type.to_s end
decode(str)
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 366 def decode(str) UrlSafeBase64.decode64(str).unpack1('B*').to_i(2).to_s end
decode!(id_token, key)
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 292 def decode!(id_token, key) ::OpenIDConnect::ResponseObject::IdToken.decode(id_token, key) end
decode_id_token(id_token)
click to toggle source
Unlike ::OpenIDConnect::ResponseObject::IdToken.decode, this method splits the decoding and verification of JWT into two steps. First, we decode the JWT without verifying it to determine the algorithm used to sign. Then, we verify it using the appropriate public key (e.g. if algorithm is RS256) or shared secret (e.g. if algorithm is HS256). This works around a limitation in the openid_connect gem: github.com/nov/openid_connect/issues/61
# File lib/omniauth/strategies/openid_connect.rb, line 262 def decode_id_token(id_token) decoded = JSON::JWT.decode(id_token, :skip_verification) algorithm = decoded.algorithm.to_sym keyset = case algorithm when :RS256, :RS384, :RS512 public_key when :HS256, :HS384, :HS512 secret end decoded.verify!(keyset) ::OpenIDConnect::ResponseObject::IdToken.new(decoded) rescue JSON::JWK::Set::KidNotFound # If the JWT has a key ID (kid), then we know that the set of # keys supplied doesn't contain the one we want, and we're # done. However, if there is no kid, then we try each key # individually to see if one works: # https://github.com/nov/json-jwt/pull/92#issuecomment-824654949 raise if decoded&.header&.key?('kid') decoded = decode_with_each_key!(id_token, keyset) raise unless decoded decoded end
decode_with_each_key!(id_token, keyset)
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 296 def decode_with_each_key!(id_token, keyset) return unless keyset.is_a?(JSON::JWK::Set) keyset.each do |key| begin decoded = decode!(id_token, key) rescue JSON::JWS::VerificationFailed, JSON::JWS::UnexpectedAlgorithm, JSON::JWS::UnknownAlgorithm next end return decoded if decoded end nil end
discover!()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 219 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
encoded_post_logout_redirect_uri()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 376 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
end_session_endpoint_is_valid?()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 384 def end_session_endpoint_is_valid? client_options.end_session_endpoint && client_options.end_session_endpoint =~ URI::DEFAULT_PARSER.make_regexp end
fetch_key()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 209 def fetch_key @fetch_key ||= parse_jwk_key(::OpenIDConnect.http_client.get_content(client_options.jwks_uri)) end
id_token_callback_phase()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 393 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
issuer()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 213 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
logout_path_pattern()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 389 def logout_path_pattern @logout_path_pattern ||= %r{\A#{Regexp.quote(request_path)}(/logout)} end
new_nonce()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 331 def new_nonce session['omniauth.nonce'] = SecureRandom.hex(16) end
new_state()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 316 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
parse_jwk_key(key)
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 359 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
parse_x509_key(key)
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 355 def parse_x509_key(key) OpenSSL::X509::Certificate.new(key).public_key end
redirect_uri()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 370 def redirect_uri return client_options.redirect_uri unless params['redirect_uri'] "#{ client_options.redirect_uri }?redirect_uri=#{ CGI.escape(params['redirect_uri']) }" end
session()
click to toggle source
Calls superclass method
# File lib/omniauth/strategies/openid_connect.rb, line 339 def session return {} if @env.nil? super end
stored_nonce()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 335 def stored_nonce session.delete('omniauth.nonce') end
stored_state()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 327 def stored_state session.delete('omniauth.state') end
user_info()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 229 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
valid_response_type?()
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 404 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
verify_id_token!(id_token)
click to toggle source
# File lib/omniauth/strategies/openid_connect.rb, line 417 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