Class: OmniAuth::Strategies::OpenIDConnect

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
OmniAuth::Strategy
Defined in:
lib/omniauth/strategies/openid_connect.rb

Overview

rubocop:disable Metrics/ClassLength

Defined Under Namespace

Classes: CallbackError

Constant Summary collapse

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

Instance Method Summary collapse

Instance Method Details

#authorization_codeObject



166
167
168
# File 'lib/omniauth/strategies/openid_connect.rb', line 166

def authorization_code
  params['code']
end

#authorize_uriObject

rubocop:disable Metrics/AbcSize



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/omniauth/strategies/openid_connect.rb', line 178

def authorize_uri # rubocop:disable Metrics/AbcSize
  client.redirect_uri = redirect_uri
  opts = {
    response_type: options.response_type,
    response_mode: options.response_mode,
    scope: options.scope,
    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[:state] = new_state if options.send_state
  opts.merge!(options.extra_authorize_params) unless options.extra_authorize_params.empty?

  options.allow_authorize_params.each do |key|
    opts[key] = request.params[key.to_s] unless opts.key?(key)
  end

  if options.pkce
    verifier = options.pkce_verifier ? options.pkce_verifier.call : SecureRandom.hex(64)

    opts.merge!(pkce_authorize_params(verifier))
    session['omniauth.pkce.verifier'] = verifier
  end

  client.authorization_uri(opts.reject { |_k, v| v.nil? })
end

#callback_phaseObject



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/omniauth/strategies/openid_connect.rb', line 121

def callback_phase
  error = params['error_reason'] || params['error']
  error_description = params['error_description'] || params['error_reason']
  invalid_state =
    if options.send_state
      (options.require_state && params['state'].to_s.empty?) || params['state'] != stored_state
    else
      false
    end

  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

#clientObject



107
108
109
# File 'lib/omniauth/strategies/openid_connect.rb', line 107

def client
  @client ||= ::OpenIDConnect::Client.new(client_options)
end

#configObject



111
112
113
# File 'lib/omniauth/strategies/openid_connect.rb', line 111

def config
  @config ||= ::OpenIDConnect::Discovery::Provider::Config.discover!(options.issuer)
end

#end_session_uriObject



170
171
172
173
174
175
176
# File 'lib/omniauth/strategies/openid_connect.rb', line 170

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_phaseObject



157
158
159
160
161
162
163
164
# File 'lib/omniauth/strategies/openid_connect.rb', line 157

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

#pkce_authorize_params(verifier) ⇒ Object



226
227
228
229
230
231
232
# File 'lib/omniauth/strategies/openid_connect.rb', line 226

def pkce_authorize_params(verifier)
  # NOTE: see https://tools.ietf.org/html/rfc7636#appendix-A
  {
    code_challenge: options.pkce_options[:code_challenge].call(verifier),
    code_challenge_method: options.pkce_options[:code_challenge_method],
  }
end

#public_keyObject



210
211
212
213
214
215
216
217
218
# File 'lib/omniauth/strategies/openid_connect.rb', line 210

def public_key
  @public_key ||= if options.discovery
                    config.jwks
                  elsif configured_public_key
                    configured_public_key
                  elsif client_options.jwks_uri
                    fetch_key
                  end
end

#request_phaseObject



115
116
117
118
119
# File 'lib/omniauth/strategies/openid_connect.rb', line 115

def request_phase
  options.issuer = issuer if options.issuer.to_s.empty?
  discover!
  redirect authorize_uri
end

#secretObject

Some OpenID providers use the OAuth2 client secret as the shared secret, but Keycloak uses a separate key that's stored inside the database.



222
223
224
# File 'lib/omniauth/strategies/openid_connect.rb', line 222

def secret
  base64_decoded_jwt_secret || client_options.secret
end

#uidObject



74
75
76
# File 'lib/omniauth/strategies/openid_connect.rb', line 74

def uid
  .raw_attributes[options.uid_field.to_sym] || .sub
end