lib/rodauth/features/oidc.rb in rodauth-oauth-0.3.0 vs lib/rodauth/features/oidc.rb in rodauth-oauth-0.4.0
- old
+ new
@@ -1,14 +1,15 @@
# frozen-string-literal: true
module Rodauth
Feature.define(:oidc) do
+ # https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
OIDC_SCOPES_MAP = {
"profile" => %i[name family_name given_name middle_name nickname preferred_username
profile picture website gender birthdate zoneinfo locale updated_at].freeze,
"email" => %i[email email_verified].freeze,
- "address" => %i[address].freeze,
+ "address" => %i[formatted street_address locality region postal_code country].freeze,
"phone" => %i[phone_number phone_number_verified].freeze
}.freeze
VALID_METADATA_KEYS = %i[
issuer
@@ -72,11 +73,11 @@
auth_value_method :oauth_prompt_login_cookie_key, "_rodauth_oauth_prompt_login"
auth_value_method :oauth_prompt_login_cookie_options, {}.freeze
auth_value_method :oauth_prompt_login_interval, 5 * 60 * 60 # 5 minutes
- auth_value_methods(:get_oidc_param)
+ auth_value_methods(:get_oidc_param, :get_additional_param)
# /userinfo
route(:userinfo) do |r|
next unless is_authorization_server?
@@ -243,34 +244,49 @@
fill_with_account_claims(id_token_claims, account, oauth_scopes)
oauth_token[:id_token] = jwt_encode(id_token_claims)
end
+ # aka fill_with_standard_claims
def fill_with_account_claims(claims, account, scopes)
- scopes_by_oidc = scopes.each_with_object({}) do |scope, by_oidc|
+ scopes_by_claim = scopes.each_with_object({}) do |scope, by_oidc|
+ next if scope == "openid"
+
oidc, param = scope.split(".", 2)
by_oidc[oidc] ||= []
by_oidc[oidc] << param.to_sym if param
end
- oidc_scopes = (OIDC_SCOPES_MAP.keys & scopes_by_oidc.keys)
+ oidc_scopes, additional_scopes = scopes_by_claim.keys.partition { |key| OIDC_SCOPES_MAP.key?(key) }
- return if oidc_scopes.empty?
+ unless oidc_scopes.empty?
+ if respond_to?(:get_oidc_param)
+ oidc_scopes.each do |scope|
+ scope_claims = claims
+ params = scopes_by_claim[scope]
+ params = params.empty? ? OIDC_SCOPES_MAP[scope] : (OIDC_SCOPES_MAP[scope] & params)
- if respond_to?(:get_oidc_param)
- oidc_scopes.each do |scope|
- params = scopes_by_oidc[scope]
- params = params.empty? ? OIDC_SCOPES_MAP[scope] : (OIDC_SCOPES_MAP[scope] & params)
-
- params.each do |param|
- claims[param] = __send__(:get_oidc_param, account, param)
+ scope_claims = (claims["address"] = {}) if scope == "address"
+ params.each do |param|
+ scope_claims[param] = __send__(:get_oidc_param, account, param)
+ end
end
+ else
+ warn "`get_oidc_param(account, claim)` must be implemented to use oidc scopes."
end
+ end
+
+ return if additional_scopes.empty?
+
+ if respond_to?(:get_additional_param)
+ additional_scopes.each do |scope|
+ claims[scope] = __send__(:get_additional_param, account, scope.to_sym)
+ end
else
- warn "`get_oidc_param(token, param)` must be implemented to use oidc scopes."
+ warn "`get_additional_param(account, claim)` must be implemented to use oidc scopes."
end
end
def json_access_token_payload(oauth_token)
payload = super
@@ -288,37 +304,31 @@
else
super
end
end
- def do_authorize(redirect_url, query_params = [], fragment_params = [])
+ def do_authorize(response_params = {}, response_mode = param_or_nil("response_mode"))
return super unless use_oauth_implicit_grant_type?
case param("response_type")
when "id_token"
- fragment_params.replace(_do_authorize_id_token.map { |k, v| "#{k}=#{v}" })
+ response_params.replace(_do_authorize_id_token)
when "code token"
redirect_response_error("invalid_request") unless use_oauth_implicit_grant_type?
- params = _do_authorize_code.merge(_do_authorize_token)
-
- fragment_params.replace(params.map { |k, v| "#{k}=#{v}" })
+ response_params.replace(_do_authorize_code.merge(_do_authorize_token))
when "code id_token"
- params = _do_authorize_code.merge(_do_authorize_id_token)
-
- fragment_params.replace(params.map { |k, v| "#{k}=#{v}" })
+ response_params.replace(_do_authorize_code.merge(_do_authorize_id_token))
when "id_token token"
- params = _do_authorize_id_token.merge(_do_authorize_token)
-
- fragment_params.replace(params.map { |k, v| "#{k}=#{v}" })
+ response_params.replace(_do_authorize_id_token.merge(_do_authorize_token))
when "code id_token token"
- params = _do_authorize_code.merge(_do_authorize_id_token).merge(_do_authorize_token)
- fragment_params.replace(params.map { |k, v| "#{k}=#{v}" })
+ response_params.replace(_do_authorize_code.merge(_do_authorize_id_token).merge(_do_authorize_token))
end
+ response_mode ||= "fragment" unless response_params.empty?
- super(redirect_url, query_params, fragment_params)
+ super(response_params, response_mode)
end
def _do_authorize_id_token
create_params = {
oauth_tokens_account_id_column => account_id,
@@ -349,16 +359,17 @@
end
end
scope_claims.unshift("auth_time") if last_account_login_at
+ response_types_supported = metadata[:response_types_supported]
+ if use_oauth_implicit_grant_type?
+ response_types_supported += ["none", "id_token", "code token", "code id_token", "id_token token", "code id_token token"]
+ end
+
metadata.merge(
userinfo_endpoint: userinfo_url,
- response_types_supported: metadata[:response_types_supported] +
- ["none", "id_token", "code token", "code id_token", "id_token token", "code id_token token"],
- response_modes_supported: %w[query fragment],
- grant_types_supported: %w[authorization_code implicit],
-
+ response_types_supported: response_types_supported,
subject_types_supported: [oauth_jwt_subject_type],
id_token_signing_alg_values_supported: metadata[:token_endpoint_auth_signing_alg_values_supported],
id_token_encryption_alg_values_supported: [oauth_jwt_jwe_algorithm].compact,
id_token_encryption_enc_values_supported: [oauth_jwt_jwe_encryption_method].compact,