Class: OmniAuth::Strategies::Himari

Inherits:
OAuth2
  • Object
show all
Defined in:
lib/omniauth/strategies/himari.rb

Defined Under Namespace

Classes: AccessToken, ConfigurationError, IdToken, IdTokenMissing, VerificationError

Instance Method Summary collapse

Instance Method Details

#authorize_paramsObject



142
143
144
145
146
147
148
# File 'lib/omniauth/strategies/himari.rb', line 142

def authorize_params
  super.tap do |params|
    # super reads options.scope; default to 'openid' if the caller cleared it.
    params[:scope] ||= options.scope || 'openid'
    params[:prompt] = request.GET['prompt'] if request.GET['prompt']
  end
end

#callback_phaseObject

RFC 9207: validate the authorization server’s issuer identifier returned alongside the authorization response before exchanging the code, defending against mix-up attacks.



58
59
60
61
62
63
64
65
66
67
# File 'lib/omniauth/strategies/himari.rb', line 58

def callback_phase
  if options.verify_iss
    iss = request.params['iss']
    if iss && iss != options.site
      return fail!(:issuer_mismatch, VerificationError.new("iss mismatch: #{iss.inspect} != #{options.site.inspect}"))
    end
  end

  super
end

#callback_urlObject



134
135
136
# File 'lib/omniauth/strategies/himari.rb', line 134

def callback_url
  options[:redirect_uri] || (full_host + callback_path) # https://github.com/omniauth/omniauth-oauth2/pull/142
end

#clientObject

Raises:



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/omniauth/strategies/himari.rb', line 37

def client
  options.client_options.site ||= options.site
  options.client_options.authorize_url ||= '/oidc/authorize'
  options.client_options.token_url ||= '/public/oidc/token'
  options.client_options.userinfo_url ||= '/public/oidc/userinfo'
  options.client_options.access_token_class ||= AccessToken # https://gitlab.com/oauth-xx/oauth2/-/issues/628

  options.client_options.connection_opts ||= {}
  options.client_options.connection_opts[:headers] ||= {}
  options.client_options.connection_opts[:headers] = {
    'User-Agent' => user_agent,
  }.merge(options.client_options.connection_opts[:headers])

  raise ConfigurationError, "client_id and client_secret is required" unless options.client_id && options.client_secret
  raise ConfigurationError, "site is required" unless options.client_options.site

  super
end

#faradayObject



127
128
129
130
131
132
# File 'lib/omniauth/strategies/himari.rb', line 127

def faraday
  @faraday ||= Faraday.new(options.site, headers: {'User-Agent' => user_agent}) do |b|
    b.response :json
    b.response :raise_error
  end
end

#id_tokenObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/omniauth/strategies/himari.rb', line 152

def id_token
  @id_token ||= begin
    jwt = access_token.params['id_token'] or raise(IdTokenMissing, 'id_token is missing')
    retval = IdToken.new(*JWT.decode(
      jwt,
      nil,
      true,
      {
        algorithms: jwks.map { |k| k[:alg] }.compact.uniq,
        jwks: jwks,
        verify_aud: true,
        aud: options.client_id,
        verify_iss: true,
        iss: options.site,
        verify_expiration: true,
      }.merge(options.verify_options),
    ))
    verify_at_hash!(retval)
    retval
  end
end

#jwksObject



174
175
176
177
178
# File 'lib/omniauth/strategies/himari.rb', line 174

def jwks
  JWT::JWK::Set.new(jwks_json).tap do |set|
    set.filter! { |k| k[:use] == 'sig' }
  end
end

#jwks_jsonObject



180
181
182
183
184
# File 'lib/omniauth/strategies/himari.rb', line 180

def jwks_json
  faraday.get(options.jwks_url || 'public/jwks').body
rescue Faraday::Error => e
  raise JwksUnavailable, "failed to retrieve jwks; #{e.inspect}"
end

#raw_infoObject



123
124
125
# File 'lib/omniauth/strategies/himari.rb', line 123

def raw_info
  @raw_info ||= !skip_info? && options.use_userinfo ? access_token.get('/public/oidc/userinfo').parsed : id_token.claims
end

#user_agentObject



138
139
140
# File 'lib/omniauth/strategies/himari.rb', line 138

def user_agent
  options.user_agent || "OmniauthHimari/#{Omniauth::Himari::VERSION}"
end

#verify_at_hash!(id_token) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/omniauth/strategies/himari.rb', line 102

def verify_at_hash!(id_token)
  return unless options.verify_at_hash

  function = case id_token.header['alg'] # this is safe as we've verified
  when 'ES256', 'RS256' then Digest::SHA256
  when 'ES384' then Digest::SHA384
  when 'ES512' then Digest::SHA512
  else
    raise VerificationError, "unknown hash function to verify at_hash for #{id_token.header["alg"]}"
  end

  dgst = function.digest(access_token.token)
  expected_at_hash = Base64.urlsafe_encode64(dgst[0, dgst.size / 2], padding: false)

  given_at_hash = id_token.claims['at_hash']

  unless given_at_hash == expected_at_hash
    raise VerificationError, "at_hash mismatch #{given_at_hash.inspect}, #{expected_at_hash.inspect}"
  end
end