:nocov:
Methods
Public Class
Public Instance
- _before_register
- _do_authorize_code
- _do_authorize_token
- _generate_access_token
- _generate_jwt_access_token
- _generate_refresh_token
- _grant_with_access_token?
- _json_response_body
- _jwt_key
- _logout_response
- _oidc_logout_response
- _redirect_response_error
- accepts_json?
- account_from_bearer_assertion_subject
- account_from_jwt_bearer_assertion
- account_from_saml2_bearer_assertion
- active_sessions?
- allow_cors
- assertion_grant_type
- assertion_grant_type?
- auth_server_jwks_set
- authorization_encryption_alg_values_supported
- authorization_encryption_enc_values_supported
- authorization_required
- authorization_server_metadata
- authorization_server_url
- authorization_signing_alg_values_supported
- authorization_token
- authorize_response
- authorize_scopes
- before_authorize_route
- before_introspection_request
- check_csrf?
- check_valid_access_type?
- check_valid_approval_prompt?
- check_valid_grant_challenge?
- check_valid_no_fragment_uri?
- check_valid_response_type?
- check_valid_scopes?
- check_valid_uri?
- clear_session
- client_assertion_type
- client_assertion_type?
- client_certificate
- client_certificate_sans
- convert_to_boolean
- create_oauth_application
- create_oauth_grant
- create_oauth_grant_with_token
- create_token
- create_token_from_authorization_code
- create_token_from_token
- current_oauth_account
- current_oauth_application
- decode_access_token
- decode_request_object
- distinguished_name_match?
- do_authorize
- do_register
- dpop_bound_access_tokens_required?
- dpop_decode
- dpop_nonce_required
- dpop_use_nonce?
- fetch_access_token
- fetch_access_token_from_authorization_header
- fetch_dpop_token
- fill_with_account_claims
- form_post_error_response_html
- form_post_response_html
- generate_dpop_nonce
- generate_frontchannel_logout_urls
- generate_id_token
- generate_jti
- generate_logout_token
- generate_saml_settings
- generate_session_state
- generate_token
- generate_token_hash
- generate_user_code
- get_oidc_account_last_login_at
- grant_from_application?
- id_token_claims
- id_token_hash
- initialize_register_params
- introspection_request
- json_access_token_payload
- json_request?
- json_response_oauth_application
- json_response_success
- json_token_introspect_payload
- json_webfinger_payload
- jwk_export
- jwk_import
- jwk_key
- jwk_thumbprint
- jwks_set
- jwt_assertion
- jwt_claims
- jwt_decode
- jwt_decode_no_key
- jwt_decode_with_jwe
- jwt_encode
- jwt_encode_authorization_response_mode
- jwt_encode_with_jwe
- jwt_response_success
- jwt_subject
- key_to_jwk
- load_oauth_application_management_routes
- load_oauth_grant_management_routes
- load_oauth_server_metadata_route
- load_openid_configuration_route
- load_registration_client_uri_routes
- load_webfinger_route
- logout
- normalize_redirect_uri_for_comparison
- oauth_account_ds
- oauth_acr_values_supported
- oauth_application
- oauth_application_ds
- oauth_application_in_visited_sites
- oauth_application_jwks
- oauth_application_params
- oauth_application_path
- oauth_applications_path
- oauth_grant_by_refresh_token
- oauth_grant_by_refresh_token_ds
- oauth_grant_by_token
- oauth_grant_by_token_ds
- oauth_grant_path
- oauth_grant_types_supported
- oauth_grants_path
- oauth_grants_resource_owner_columns
- oauth_grants_unique_columns
- oauth_jwt_audience
- oauth_jwt_issuer
- oauth_jwt_jwe_algorithms_supported
- oauth_jwt_jwe_encryption_methods_supported
- oauth_management_pagination_link
- oauth_management_pagination_links
- oauth_oidc_session_management_salt
- oauth_response_modes_for_code_supported
- oauth_response_modes_for_token_supported
- oauth_response_modes_supported
- oauth_response_types_supported
- oauth_server_metadata_body
- oauth_token_endpoint_auth_methods_supported
- oauth_token_subject
- oauth_unique_id_generator
- oidc_authorize_on_prompt_none?
- oidc_grant_params
- openid_configuration_body
- parse_saml_assertion
- password_hash
- per_page_param
- perform_logout_requests
- post_configure
- private_jwk?
- proxy_get_param
- redirect_authorize_error
- redirect_logout_with_error
- redirect_response_error
- redirect_uri
- register_invalid_application_type_message
- register_invalid_client_metadata_message
- register_invalid_contacts_message
- register_invalid_jwks_param_message
- register_invalid_param_message
- register_invalid_response_type_for_grant_type_message
- register_invalid_response_type_message
- register_invalid_scopes_message
- register_invalid_uri_message
- register_oauth_invalid_grant_type_message
- register_required_param_message
- register_throw_json_response_error
- request_object_encryption_alg_values_supported
- request_object_encryption_enc_values_supported
- request_object_signing_alg_values_supported
- require_acr_value
- require_acr_value_phr
- require_acr_value_phrh
- require_authorizable_account
- require_oauth_application
- require_oauth_application_for_introspect
- require_oauth_application_from_account
- require_oauth_application_from_client_secret_basic
- require_oauth_application_from_client_secret_jwt
- require_oauth_application_from_client_secret_post
- require_oauth_application_from_jwt_bearer_assertion_issuer
- require_oauth_application_from_jwt_bearer_assertion_subject
- require_oauth_application_from_none
- require_oauth_application_from_private_key_jwt
- require_oauth_application_from_saml2_bearer_assertion_issuer
- require_oauth_application_from_saml2_bearer_assertion_subject
- require_oauth_authorization
- require_signed_request_object?
- requires_backchannel_logout_session?
- requires_frontchannel_logout_session?
- rescue_from_uniqueness_error
- resource_indicators
- resource_owner_identifier
- resource_owner_params
- resource_owner_params_from_jwt_claims
- response_error_params
- return_response
- revoke_oauth_grant
- scopes
- secret_hash
- secret_matches?
- session_id_in_claims
- set_client_secret
- should_set_oauth_application_in_visited_sites?
- should_set_sid_in_visited_sites?
- sid_in_visited_sites
- store_token
- supported_grant_type?
- supported_request_uri?
- supported_response_mode?
- supported_response_type?
- supports_auth_method?
- template_path
- throw_json_response_error
- translate
- try_acr_values
- try_approval_prompt
- try_prompt
- use_date_arithmetic?
- userinfo_encryption_alg_values_supported
- userinfo_encryption_enc_values_supported
- userinfo_signing_alg_values_supported
- valid_dpop_nonce?
- valid_dpop_proof_required
- valid_locked_oauth_grant
- valid_oauth_grant_ds
- validate_ath
- validate_authorize_params
- validate_client_registration_params
- validate_client_registration_response_type
- validate_dpop_jwt_claims
- validate_dpop_proof_usage
- validate_dpop_token
- validate_introspect_params
- validate_nonce
- validate_oauth_application_params
- validate_oidc_logout_params
- validate_par_params
- validate_pkce_challenge_params
- validate_revoke_params
- validate_token_params
- verify_access_token_headers
- verify_aud
- verify_dpop_jwt_headers
- verify_jti
- www_authenticate_header
Included modules
Classes and Modules
Constants
ALLOWED_REQUEST_URI_CONTENT_TYPES | = | %w[application/jose application/oauth-authz-req+jwt].freeze | ||
APPLICATION_REQUIRED_PARAMS | = | %w[name scopes homepage_url redirect_uri client_secret].freeze |
Application |
|
EMPTY_HASH | = | {}.freeze | ||
OAUTH_ACCESS_TYPES | = | %w[offline online].freeze | ||
OAUTH_APPROVAL_PROMPTS | = | %w[force auto].freeze | ||
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[formatted street_address locality region postal_code country].freeze, "phone" => %i[phone_number phone_number_verified].freeze }.freeze |
openid.net/specs/openid-connect-core-1_0.html#StandardClaims |
|
PROTECTED_APPLICATION_ATTRIBUTES | = | %w[account_id client_id].freeze | ||
REQUIRED_METADATA_KEYS | = | %i[ issuer authorization_endpoint token_endpoint jwks_uri response_types_supported subject_types_supported id_token_signing_alg_values_supported ].freeze | ||
SELF_ISSUED_DEFAULT_APPLICATION_PARAMS | = | { "scope" => "openid profile email address phone", "response_types" => ["id_token"], "subject_type" => "pairwise", "id_token_signed_response_alg" => "RS256", "request_object_signing_alg" => "RS256", "grant_types" => %w[implicit] }.freeze | ||
VALID_METADATA_KEYS | = | %i[ issuer authorization_endpoint end_session_endpoint backchannel_logout_session_supported token_endpoint userinfo_endpoint jwks_uri registration_endpoint scopes_supported response_types_supported response_modes_supported grant_types_supported acr_values_supported subject_types_supported id_token_signing_alg_values_supported id_token_encryption_alg_values_supported id_token_encryption_enc_values_supported userinfo_signing_alg_values_supported userinfo_encryption_alg_values_supported userinfo_encryption_enc_values_supported request_object_signing_alg_values_supported request_object_encryption_alg_values_supported request_object_encryption_enc_values_supported token_endpoint_auth_methods_supported token_endpoint_auth_signing_alg_values_supported display_values_supported claim_types_supported claims_supported service_documentation claims_locales_supported ui_locales_supported claims_parameter_supported request_parameter_supported request_uri_parameter_supported require_request_uri_registration op_policy_uri op_tos_uri check_session_iframe frontchannel_logout_supported frontchannel_logout_session_supported backchannel_logout_supported backchannel_logout_session_supported ].freeze |
Public Class methods
included(rodauth)
[show source]
# File lib/rodauth/features/oauth_resource_indicators.rb 159 def self.included(rodauth) 160 super 161 rodauth.send(:include, IndicatorAuthorizationCodeGrant) if rodauth.features.include?(:oauth_authorization_code_grant) 162 rodauth.send(:include, IndicatorIntrospection) if rodauth.features.include?(:oauth_token_introspection) 163 rodauth.send(:include, IndicatorJwt) if rodauth.features.include?(:oauth_jwt) 164 end
Public Instance methods
_before_register()
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 108 def _before_register 109 raise %{dynamic client registration requires authentication. 110 Override ´before_register` to perform it. 111 example: 112 113 before_register do 114 account = _account_from_login(request.env["HTTP_X_USER_EMAIL"]) 115 authorization_required unless account 116 @oauth_application_params[:account_id] = account[:id] 117 end 118 } 119 end
_do_authorize_code()
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 69 def _do_authorize_code 70 create_params = { 71 oauth_grants_type_column => "authorization_code", 72 **resource_owner_params 73 } 74 75 { "code" => create_oauth_grant(create_params) } 76 end
_do_authorize_token(grant_params = {})
[show source]
# File lib/rodauth/features/oauth_implicit_grant.rb 58 def _do_authorize_token(grant_params = {}) 59 grant_params = { 60 oauth_grants_type_column => "implicit", 61 oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column], 62 oauth_grants_scopes_column => scopes, 63 **resource_owner_params 64 }.merge(grant_params) 65 66 generate_token(grant_params, false) 67 end
_generate_access_token(params = {})
[show source]
# File lib/rodauth/features/oauth_base.rb 494 def _generate_access_token(params = {}) 495 token = oauth_unique_id_generator 496 497 if oauth_grants_token_hash_column 498 params[oauth_grants_token_hash_column] = generate_token_hash(token) 499 else 500 params[oauth_grants_token_column] = token 501 end 502 503 token 504 end
_generate_jwt_access_token(oauth_grant)
[show source]
# File lib/rodauth/features/oauth_jwt.rb 97 def _generate_jwt_access_token(oauth_grant) 98 claims = jwt_claims(oauth_grant) 99 100 # one of the points of using jwt is avoiding database lookups, so we put here all relevant 101 # token data. 102 claims[:scope] = oauth_grant[oauth_grants_scopes_column] 103 104 # RFC8725 section 3.11: Use Explicit Typing 105 # RFC9068 section 2.1 : The "typ" value used SHOULD be "at+jwt". 106 jwt_encode(claims, headers: { typ: "at+jwt" }) 107 end
_generate_refresh_token(params)
[show source]
# File lib/rodauth/features/oauth_base.rb 506 def _generate_refresh_token(params) 507 token = oauth_unique_id_generator 508 509 if oauth_grants_refresh_token_hash_column 510 params[oauth_grants_refresh_token_hash_column] = generate_token_hash(token) 511 else 512 params[oauth_grants_refresh_token_column] = token 513 end 514 515 token 516 end
_grant_with_access_token?(oauth_grant)
[show source]
# File lib/rodauth/features/oauth_base.rb 518 def _grant_with_access_token?(oauth_grant) 519 if oauth_grants_token_hash_column 520 oauth_grant[oauth_grants_token_hash_column] 521 else 522 oauth_grant[oauth_grants_token_column] 523 end 524 end
_json_response_body(hash)
[show source]
# File lib/rodauth/features/oauth_base.rb 836 def _json_response_body(hash) 837 return super if features.include?(:json) 838 839 if request.respond_to?(:convert_to_json) 840 request.send(:convert_to_json, hash) 841 else 842 JSON.dump(hash) 843 end 844 end
_jwt_key()
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 81 def _jwt_key 82 @_jwt_key ||= (oauth_application_jwks(oauth_application) if oauth_application) 83 end
_logout_response()
[show source]
# File lib/rodauth/features/oidc_frontchannel_logout.rb 35 def _logout_response 36 visited_sites = @visited_sites 37 38 return super unless visited_sites 39 40 logout_urls = db[oauth_applications_table] 41 .where(oauth_applications_client_id_column => visited_sites.map(&:first)) 42 .as_hash(oauth_applications_client_id_column, oauth_applications_frontchannel_logout_uri_column) 43 44 return super if logout_urls.empty? 45 46 generate_frontchannel_logout_urls(visited_sites, logout_urls) 47 48 @frontchannel_logout_redirect = logout_redirect 49 50 set_notice_flash logout_notice_flash 51 return_response frontchannel_logout_view 52 end
_oidc_logout_response()
overrides rp-initiate logout response
[show source]
# File lib/rodauth/features/oidc_frontchannel_logout.rb 55 def _oidc_logout_response 56 visited_sites = @visited_sites 57 58 return super unless visited_sites 59 60 logout_urls = db[oauth_applications_table] 61 .where(oauth_applications_client_id_column => visited_sites.map(&:first)) 62 .as_hash(oauth_applications_client_id_column, oauth_applications_frontchannel_logout_uri_column) 63 64 return super if logout_urls.empty? 65 66 generate_frontchannel_logout_urls(visited_sites, logout_urls) 67 68 @frontchannel_logout_redirect = oidc_logout_redirect 69 70 set_notice_flash logout_notice_flash 71 return_response frontchannel_logout_view 72 end
_redirect_response_error(redirect_url, params)
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 96 def _redirect_response_error(redirect_url, params) 97 response_mode = param_or_nil("response_mode") || oauth_response_mode 98 99 case response_mode 100 when "form_post" 101 response["Content-Type"] = "text/html" 102 error_body = form_post_error_response_html(redirect_url) do 103 params.map do |name, value| 104 "<input type=\"hidden\" name=\"#{name}\" value=\"#{scope.h(value)}\" />" 105 end.join 106 end 107 response.write(error_body) 108 request.halt 109 else 110 super 111 end 112 end
accepts_json?()
[show source]
# File lib/rodauth/features/oauth_base.rb 188 def accepts_json? 189 return true if only_json? 190 191 (accept = request.env["HTTP_ACCEPT"]) && accept =~ json_request_regexp 192 end
account_from_bearer_assertion_subject(subject)
[show source]
# File lib/rodauth/features/oauth_assertion_base.rb 43 def account_from_bearer_assertion_subject(subject) 44 __insert_or_do_nothing_and_return__( 45 db[accounts_table], 46 account_id_column, 47 [login_column], 48 login_column => subject 49 ) 50 end
account_from_jwt_bearer_assertion(assertion)
[show source]
# File lib/rodauth/features/oauth_jwt_bearer_grant.rb 75 def account_from_jwt_bearer_assertion(assertion) 76 claims = jwt_assertion(assertion) 77 78 return unless claims 79 80 account_from_bearer_assertion_subject(claims["sub"]) 81 end
account_from_saml2_bearer_assertion(assertion)
[show source]
# File lib/rodauth/features/oauth_saml_bearer_grant.rb 64 def account_from_saml2_bearer_assertion(assertion) 65 parse_saml_assertion(assertion) 66 67 return unless @assertion 68 69 account_from_bearer_assertion_subject(@assertion.nameid) 70 end
active_sessions?(session_id)
[show source]
# File lib/rodauth/features/oidc_logout_base.rb 20 def active_sessions?(session_id) 21 !active_sessions_ds.where(active_sessions_session_id_column => session_id).empty? 22 end
allow_cors(request)
[show source]
# File lib/rodauth/features/oidc.rb 839 def allow_cors(request) 840 return unless request.request_method == "OPTIONS" 841 842 response["Access-Control-Allow-Origin"] = "*" 843 response["Access-Control-Allow-Methods"] = "GET, OPTIONS" 844 response["Access-Control-Max-Age"] = "3600" 845 response.status = 200 846 return_response 847 end
assertion_grant_type(grant_type = param("grant_type"))
[show source]
# File lib/rodauth/features/oauth_assertion_base.rb 84 def assertion_grant_type(grant_type = param("grant_type")) 85 grant_type.delete_prefix("urn:ietf:params:oauth:grant-type:").tr("-", "_") 86 end
assertion_grant_type?(grant_type = param("grant_type"))
[show source]
# File lib/rodauth/features/oauth_assertion_base.rb 76 def assertion_grant_type?(grant_type = param("grant_type")) 77 grant_type.start_with?("urn:ietf:params:oauth:grant-type:") 78 end
auth_server_jwks_set()
Resource Server only!
returns the jwks set from the authorization server.
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 88 def auth_server_jwks_set 89 metadata = authorization_server_metadata 90 91 return unless metadata && (jwks_uri = metadata[:jwks_uri]) 92 93 jwks_uri = URI(jwks_uri) 94 95 http_request_with_cache(jwks_uri) 96 end
authorization_encryption_alg_values_supported()
[show source]
# File lib/rodauth/features/oauth_jwt_secured_authorization_response_mode.rb 33 def authorization_encryption_alg_values_supported 34 oauth_jwt_jwe_algorithms_supported 35 end
authorization_encryption_enc_values_supported()
[show source]
# File lib/rodauth/features/oauth_jwt_secured_authorization_response_mode.rb 37 def authorization_encryption_enc_values_supported 38 oauth_jwt_jwe_encryption_methods_supported 39 end
authorization_required()
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 166 def authorization_required 167 if accepts_json? 168 throw_json_response_error(oauth_authorization_required_error_status, "invalid_client") 169 else 170 set_redirect_error_flash(require_authorization_error_flash) 171 redirect(authorize_path) 172 end 173 end
authorization_server_metadata()
Resource server mode
[show source]
# File lib/rodauth/features/oauth_base.rb 873 def authorization_server_metadata 874 auth_url = URI(authorization_server_url).dup 875 auth_url.path = "/.well-known/oauth-authorization-server" 876 877 http_request_with_cache(auth_url) 878 end
authorization_server_url()
[show source]
# File lib/rodauth/features/oauth_base.rb 346 def authorization_server_url 347 base_url 348 end
authorization_signing_alg_values_supported()
[show source]
# File lib/rodauth/features/oauth_jwt_secured_authorization_response_mode.rb 29 def authorization_signing_alg_values_supported 30 oauth_jwt_jws_algorithms_supported 31 end
authorization_token()
[show source]
# File lib/rodauth/features/oauth_base.rb 260 def authorization_token 261 return @authorization_token if defined?(@authorization_token) 262 263 # check if there is a token 264 access_token = fetch_access_token 265 266 return unless access_token 267 268 @authorization_token = oauth_grant_by_token(access_token) 269 end
authorize_response(params, mode)
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 78 def authorize_response(params, mode) 79 redirect_url = URI.parse(redirect_uri) 80 case mode 81 when "query" 82 params = [URI.encode_www_form(params)] 83 params << redirect_url.query if redirect_url.query 84 redirect_url.query = params.join("&") 85 redirect(redirect_url.to_s) 86 when "form_post" 87 inline_html = form_post_response_html(redirect_uri) do 88 params.map do |name, value| 89 "<input type=\"hidden\" name=\"#{scope.h(name)}\" value=\"#{scope.h(value)}\" />" 90 end.join 91 end 92 scope.view layout: false, inline: inline_html 93 end 94 end
authorize_scopes()
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 70 def authorize_scopes 71 scopes || begin 72 oauth_application[oauth_applications_scopes_column].split(oauth_scope_separator) 73 end 74 end
before_authorize_route()
[show source]
# File lib/rodauth/features/oidc.rb 239 def before_authorize_route 240 if (ui_locales = param_or_nil("ui_locales")) 241 ui_locales = ui_locales.split(" ").map(&:to_sym) 242 ui_locales &= ::I18n.available_locales 243 244 ::I18n.locale = ui_locales.first unless ui_locales.empty? 245 end 246 247 super 248 end
before_introspection_request(request)
[show source]
# File lib/rodauth/features/oauth_resource_server.rb 57 def before_introspection_request(request); end
check_csrf?()
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 61 def check_csrf? 62 case request.path 63 when authorize_path 64 only_json? ? false : super 65 else 66 super 67 end 68 end
check_valid_access_type?()
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 113 def check_valid_access_type? 114 return true unless use_oauth_access_type? 115 116 access_type = param_or_nil("access_type") 117 !access_type || OAUTH_ACCESS_TYPES.include?(access_type) 118 end
check_valid_approval_prompt?()
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 120 def check_valid_approval_prompt? 121 return true unless use_oauth_access_type? 122 123 approval_prompt = param_or_nil("approval_prompt") 124 !approval_prompt || OAUTH_APPROVAL_PROMPTS.include?(approval_prompt) 125 end
check_valid_grant_challenge?(grant, verifier)
[show source]
# File lib/rodauth/features/oauth_pkce.rb 72 def check_valid_grant_challenge?(grant, verifier) 73 challenge = grant[oauth_grants_code_challenge_column] 74 75 case grant[oauth_grants_code_challenge_method_column] 76 when "plain" 77 challenge == verifier 78 when "S256" 79 generated_challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), padding: false) 80 81 challenge == generated_challenge 82 else 83 redirect_response_error("unsupported_transform_algorithm") 84 end 85 end
check_valid_no_fragment_uri?(uri)
[show source]
# File lib/rodauth/features/oauth_base.rb 867 def check_valid_no_fragment_uri?(uri) 868 check_valid_uri?(uri) && URI.parse(uri).fragment.nil? 869 end
check_valid_response_type?()
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 153 def check_valid_response_type? 154 response_type = param_or_nil("response_type") 155 156 response_type == "code" || response_type == "none" || super 157 end
check_valid_scopes?(scp = scopes)
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 105 def check_valid_scopes?(scp = scopes) 106 super(scp - %w[offline_access]) 107 end
check_valid_uri?(uri)
[show source]
# File lib/rodauth/features/oauth_base.rb 863 def check_valid_uri?(uri) 864 URI::DEFAULT_PARSER.make_regexp(oauth_valid_uri_schemes).match?(uri) 865 end
clear_session()
[show source]
# File lib/rodauth/features/oidc_session_management.rb 30 def clear_session 31 super 32 33 # update user agent state in the process 34 # TODO: dangerous if this gets overidden by the user 35 36 user_agent_state_cookie_opts = Hash[oauth_oidc_user_agent_state_cookie_options] 37 user_agent_state_cookie_opts[:value] = oauth_unique_id_generator 38 user_agent_state_cookie_opts[:secure] = true 39 if oauth_oidc_user_agent_state_cookie_expires_in 40 user_agent_state_cookie_opts[:expires] = convert_timestamp(Time.now + oauth_oidc_user_agent_state_cookie_expires_in) 41 end 42 ::Rack::Utils.set_cookie_header!(response.headers, oauth_oidc_user_agent_state_cookie_key, user_agent_state_cookie_opts) 43 end
client_assertion_type(assertion_type = param("client_assertion_type"))
[show source]
# File lib/rodauth/features/oauth_assertion_base.rb 88 def client_assertion_type(assertion_type = param("client_assertion_type")) 89 assertion_type.delete_prefix("urn:ietf:params:oauth:client-assertion-type:").tr("-", "_") 90 end
client_assertion_type?(client_assertion_type = param("client_assertion_type"))
[show source]
# File lib/rodauth/features/oauth_assertion_base.rb 80 def client_assertion_type?(client_assertion_type = param("client_assertion_type")) 81 client_assertion_type.start_with?("urn:ietf:params:oauth:client-assertion-type:") 82 end
client_certificate()
[show source]
# File lib/rodauth/features/oauth_tls_client_auth.rb 126 def client_certificate 127 return @client_certificate if defined?(@client_certificate) 128 129 unless (pem_cert = request.env["SSL_CLIENT_CERT"] || request.env["HTTP_SSL_CLIENT_CERT"] || request.env["HTTP_X_SSL_CLIENT_CERT"]) 130 return 131 end 132 133 return if pem_cert.empty? 134 135 @certificate = OpenSSL::X509::Certificate.new(pem_cert) 136 end
client_certificate_sans()
[show source]
# File lib/rodauth/features/oauth_tls_client_auth.rb 138 def client_certificate_sans 139 return @client_certificate_sans if defined?(@client_certificate_sans) 140 141 @client_certificate_sans = begin 142 return [] unless client_certificate 143 144 san = client_certificate.extensions.find { |ext| ext.oid == "subjectAltName" } 145 146 return [] unless san 147 148 ostr = OpenSSL::ASN1.decode(san.to_der).value.last 149 150 sans = OpenSSL::ASN1.decode(ostr.value) 151 152 return [] unless sans 153 154 sans.value 155 end 156 end
convert_to_boolean(key, value)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 389 def convert_to_boolean(key, value) 390 case value 391 when true, false then value 392 when "true" then true 393 when "false" then false 394 else 395 register_throw_json_response_error( 396 "invalid_client_metadata", 397 register_invalid_param_message(key) 398 ) 399 end 400 end
create_oauth_application()
[show source]
# File lib/rodauth/features/oauth_application_management.rb 184 def create_oauth_application 185 create_params = { 186 oauth_applications_account_id_column => account_id, 187 oauth_applications_name_column => oauth_application_params[oauth_application_name_param], 188 oauth_applications_description_column => oauth_application_params[oauth_application_description_param], 189 oauth_applications_scopes_column => oauth_application_params[oauth_application_scopes_param], 190 oauth_applications_homepage_url_column => oauth_application_params[oauth_application_homepage_url_param] 191 } 192 193 redirect_uris = oauth_application_params[oauth_application_redirect_uri_param] 194 redirect_uris = redirect_uris.to_a.reject(&:empty?).join(" ") if redirect_uris.respond_to?(:each) 195 create_params[oauth_applications_redirect_uri_column] = redirect_uris unless redirect_uris.empty? 196 197 # set client ID/secret pairs 198 set_client_secret(create_params, oauth_application_params[oauth_application_client_secret_param]) 199 200 if create_params[oauth_applications_scopes_column] 201 create_params[oauth_applications_scopes_column] = create_params[oauth_applications_scopes_column].join(oauth_scope_separator) 202 end 203 204 rescue_from_uniqueness_error do 205 create_params[oauth_applications_client_id_column] = oauth_unique_id_generator 206 db[oauth_applications_table].insert(create_params) 207 end 208 end
create_oauth_grant(create_params = {})
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 188 def create_oauth_grant(create_params = {}) 189 create_params[oauth_grants_oauth_application_id_column] ||= oauth_application[oauth_applications_id_column] 190 create_params[oauth_grants_redirect_uri_column] ||= redirect_uri 191 create_params[oauth_grants_expires_in_column] ||= Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in) 192 create_params[oauth_grants_scopes_column] ||= scopes.join(oauth_scope_separator) 193 194 if use_oauth_access_type? && (access_type = param_or_nil("access_type")) 195 create_params[oauth_grants_access_type_column] = access_type 196 end 197 198 ds = db[oauth_grants_table] 199 200 create_params[oauth_grants_code_column] = oauth_unique_id_generator 201 202 if oauth_reuse_access_token 203 unique_conds = Hash[oauth_grants_unique_columns.map { |column| [column, create_params[column]] }] 204 valid_grant = valid_oauth_grant_ds(unique_conds).select(oauth_grants_id_column).first 205 if valid_grant 206 create_params[oauth_grants_id_column] = valid_grant[oauth_grants_id_column] 207 rescue_from_uniqueness_error do 208 __insert_or_update_and_return__( 209 ds, 210 oauth_grants_id_column, 211 [oauth_grants_id_column], 212 create_params 213 ) 214 end 215 return create_params[oauth_grants_code_column] 216 end 217 end 218 219 rescue_from_uniqueness_error do 220 if __one_oauth_token_per_account 221 __insert_or_update_and_return__( 222 ds, 223 oauth_grants_id_column, 224 oauth_grants_unique_columns, 225 create_params, 226 nil, 227 { 228 oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in), 229 oauth_grants_revoked_at_column => nil 230 } 231 ) 232 else 233 __insert_and_return__(ds, oauth_grants_id_column, create_params) 234 end 235 end 236 create_params[oauth_grants_code_column] 237 end
create_oauth_grant_with_token(create_params = {})
[show source]
# File lib/rodauth/features/oidc.rb 487 def create_oauth_grant_with_token(create_params = {}) 488 create_params.merge!(resource_owner_params) 489 create_params[oauth_grants_type_column] = "hybrid" 490 create_params[oauth_grants_expires_in_column] = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_access_token_expires_in) 491 authorization_code = create_oauth_grant(create_params) 492 access_token = if oauth_jwt_access_tokens 493 _generate_jwt_access_token(create_params) 494 else 495 oauth_grant = valid_oauth_grant_ds.where(oauth_grants_code_column => authorization_code).first 496 _generate_access_token(oauth_grant) 497 end 498 499 json_access_token_payload(oauth_grants_token_column => access_token).merge("code" => authorization_code) 500 end
create_token(grant_type)
[show source]
# File lib/rodauth/features/oauth_assertion_base.rb 52 def create_token(grant_type) 53 return super unless assertion_grant_type?(grant_type) && supported_grant_type?(grant_type) 54 55 account = __send__(:"account_from_#{assertion_grant_type}_assertion", param("assertion")) 56 57 redirect_response_error("invalid_grant") unless account 58 59 grant_scopes = if param_or_nil("scope") 60 redirect_response_error("invalid_scope") unless check_valid_scopes? 61 scopes 62 else 63 @oauth_application[oauth_applications_scopes_column] 64 end 65 66 grant_params = { 67 oauth_grants_type_column => grant_type, 68 oauth_grants_account_id_column => account[account_id_column], 69 oauth_grants_oauth_application_id_column => @oauth_application[oauth_applications_id_column], 70 oauth_grants_scopes_column => grant_scopes 71 } 72 73 generate_token(grant_params, false) 74 end
create_token_from_authorization_code(grant_params, should_generate_refresh_token = !use_oauth_access_type?, oauth_grant: nil)
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 179 def create_token_from_authorization_code(grant_params, should_generate_refresh_token = !use_oauth_access_type?, oauth_grant: nil) 180 # fetch oauth grant 181 oauth_grant ||= valid_locked_oauth_grant(grant_params) 182 183 should_generate_refresh_token ||= oauth_grant[oauth_grants_access_type_column] == "offline" 184 185 generate_token(oauth_grant, should_generate_refresh_token) 186 end
create_token_from_token(oauth_grant, update_params)
[show source]
# File lib/rodauth/features/oauth_base.rb 695 def create_token_from_token(oauth_grant, update_params) 696 redirect_response_error("invalid_grant") unless grant_from_application?(oauth_grant, oauth_application) 697 698 rescue_from_uniqueness_error do 699 oauth_grants_ds = db[oauth_grants_table].where(oauth_grants_id_column => oauth_grant[oauth_grants_id_column]) 700 access_token = _generate_access_token(update_params) 701 oauth_grant = __update_and_return__(oauth_grants_ds, update_params) 702 703 oauth_grant[oauth_grants_token_column] = access_token 704 oauth_grant 705 end 706 end
current_oauth_account()
[show source]
# File lib/rodauth/features/oauth_base.rb 176 def current_oauth_account 177 account_id = authorization_token[oauth_grants_account_id_column] 178 179 return unless account_id 180 181 oauth_account_ds(account_id).first 182 end
current_oauth_application()
[show source]
# File lib/rodauth/features/oauth_base.rb 184 def current_oauth_application 185 oauth_application_ds(authorization_token[oauth_grants_oauth_application_id_column]).first 186 end
decode_access_token(access_token = fetch_access_token)
[show source]
# File lib/rodauth/features/oauth_jwt.rb 63 def decode_access_token(access_token = fetch_access_token) 64 return unless access_token 65 66 jwt_claims = jwt_decode(access_token, verify_headers: method(:verify_access_token_headers)) 67 68 return unless jwt_claims 69 70 return unless jwt_claims["sub"] 71 72 return unless jwt_claims["aud"] 73 74 jwt_claims 75 end
decode_request_object(request_object)
[show source]
# File lib/rodauth/features/oauth_jwt_secured_authorization_request.rb 103 def decode_request_object(request_object) 104 request_sig_enc_opts = { 105 jws_algorithm: oauth_application[oauth_applications_request_object_signing_alg_column], 106 jws_encryption_algorithm: oauth_application[oauth_applications_request_object_encryption_alg_column], 107 jws_encryption_method: oauth_application[oauth_applications_request_object_encryption_enc_column] 108 }.compact 109 110 request_sig_enc_opts[:jws_algorithm] ||= "none" if oauth_request_object_signing_alg_allow_none 111 112 if request_sig_enc_opts[:jws_algorithm] == "none" 113 redirect_response_error("invalid_request_object") if require_signed_request_object? 114 115 jwks = nil 116 elsif (jwks = oauth_application_jwks(oauth_application)) 117 jwks = JSON.parse(jwks, symbolize_names: true) if jwks.is_a?(String) 118 else 119 redirect_response_error("invalid_request_object") 120 end 121 122 claims = jwt_decode(request_object, 123 jwks: jwks, 124 verify_jti: false, 125 verify_iss: false, 126 verify_aud: false, 127 **request_sig_enc_opts) 128 129 redirect_response_error("invalid_request_object") unless claims 130 131 claims 132 end
distinguished_name_match?(sub1, sub2)
[show source]
# File lib/rodauth/features/oauth_tls_client_auth.rb 158 def distinguished_name_match?(sub1, sub2) 159 sub1 = OpenSSL::X509::Name.parse(sub1) if sub1.is_a?(String) 160 sub2 = OpenSSL::X509::Name.parse(sub2) if sub2.is_a?(String) 161 # OpenSSL::X509::Name#cp calls X509_NAME_cmp via openssl. 162 # https://www.openssl.org/docs/manmaster/man3/X509_NAME_cmp.html 163 # This procedure adheres to the matching rules for Distinguished Names (DN) given in 164 # RFC 4517 section 4.2.15 and RFC 5280 section 7.1. 165 sub1.cmp(sub2).zero? 166 end
do_authorize(response_params = {}, response_mode = param_or_nil("response_mode"))
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 50 def do_authorize(response_params = {}, response_mode = param_or_nil("response_mode")) 51 response_mode ||= oauth_response_mode 52 53 redirect_response_error("invalid_request") unless response_mode.nil? || supported_response_mode?(response_mode) 54 55 response_type = param_or_nil("response_type") 56 57 redirect_response_error("invalid_request") unless response_type.nil? || supported_response_type?(response_type) 58 59 case response_type 60 when "code", nil 61 response_params.replace(_do_authorize_code) 62 end 63 64 response_params["state"] = param("state") if param_or_nil("state") 65 66 [response_params, response_mode] 67 end
do_register(return_params = request.params.dup)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 283 def do_register(return_params = request.params.dup) 284 applications_ds = db[oauth_applications_table] 285 application_columns = applications_ds.columns 286 287 # set defaults 288 create_params = @oauth_application_params 289 290 # If omitted, an authorization server MAY register a client with a default set of scopes 291 create_params[oauth_applications_scopes_column] ||= return_params["scopes"] = oauth_application_scopes.join(" ") 292 293 # https://datatracker.ietf.org/doc/html/rfc7591#section-2 294 if create_params[oauth_applications_grant_types_column] ||= begin 295 # If omitted, the default behavior is that the client will use only the "authorization_code" Grant Type. 296 return_params["grant_types"] = %w[authorization_code] # rubocop:disable Lint/AssignmentInCondition 297 "authorization_code" 298 end 299 create_params[oauth_applications_token_endpoint_auth_method_column] ||= begin 300 # If unspecified or omitted, the default is "client_secret_basic", denoting the HTTP Basic 301 # authentication scheme as specified in Section 2.3.1 of OAuth 2.0. 302 return_params["token_endpoint_auth_method"] = 303 "client_secret_basic" 304 "client_secret_basic" 305 end 306 end 307 create_params[oauth_applications_response_types_column] ||= begin 308 # If omitted, the default is that the client will use only the "code" response type. 309 return_params["response_types"] = %w[code] 310 "code" 311 end 312 rescue_from_uniqueness_error do 313 initialize_register_params(create_params, return_params) 314 create_params.delete_if { |k, _| !application_columns.include?(k) } 315 applications_ds.insert(create_params) 316 end 317 318 return_params 319 end
dpop_bound_access_tokens_required?()
[show source]
# File lib/rodauth/features/oauth_dpop.rb 305 def dpop_bound_access_tokens_required? 306 oauth_dpop_bound_access_tokens || (oauth_application && oauth_application[oauth_applications_dpop_bound_access_tokens_column]) 307 end
dpop_decode(dpop)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 153 def dpop_decode(dpop) 154 # decode first without verifying! 155 _, headers = jwt_decode_no_key(dpop) 156 157 redirect_response_error("invalid_dpop_proof") unless verify_dpop_jwt_headers(headers) 158 159 dpop_jwk = headers["jwk"] 160 161 jwt_decode( 162 dpop, 163 jws_key: jwk_key(dpop_jwk), 164 jws_algorithm: headers["alg"], 165 verify_iss: false, 166 verify_aud: false, 167 verify_jti: false 168 ) 169 end
dpop_nonce_required(dpop_claims)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 322 def dpop_nonce_required(dpop_claims) 323 response["DPoP-Nonce"] = generate_dpop_nonce(dpop_claims) 324 325 if @dpop_access_token 326 # protected resource access 327 throw_json_response_error(401, "use_dpop_nonce") 328 else 329 redirect_response_error("use_dpop_nonce") 330 end 331 end
dpop_use_nonce?()
[show source]
# File lib/rodauth/features/oauth_dpop.rb 309 def dpop_use_nonce? 310 oauth_dpop_use_nonce || (oauth_application && oauth_application[oauth_applications_dpop_bound_access_tokens_column]) 311 end
fetch_access_token()
[show source]
# File lib/rodauth/features/oauth_base.rb 233 def fetch_access_token 234 if (token = request.params["access_token"]) 235 if request.post? && !(request.content_type.start_with?("application/x-www-form-urlencoded") && 236 request.params.size == 1) 237 return 238 end 239 else 240 token = fetch_access_token_from_authorization_header 241 end 242 243 return if token.nil? || token.empty? 244 245 token 246 end
fetch_access_token_from_authorization_header(token_type = oauth_token_type)
[show source]
# File lib/rodauth/features/oauth_base.rb 248 def fetch_access_token_from_authorization_header(token_type = oauth_token_type) 249 value = request.env["HTTP_AUTHORIZATION"] 250 251 return unless value && !value.empty? 252 253 scheme, token = value.split(" ", 2) 254 255 return unless scheme.downcase == token_type 256 257 token 258 end
fetch_dpop_token()
[show source]
# File lib/rodauth/features/oauth_dpop.rb 294 def fetch_dpop_token 295 dpop = request.env["HTTP_DPOP"] 296 297 return if dpop.nil? || dpop.empty? 298 299 # 4.3.1 - There is not more than one DPoP HTTP request header field. 300 redirect_response_error("multiple_dpop_proofs") if dpop.split(";").size > 1 301 302 dpop 303 end
fill_with_account_claims(claims, account, scopes, claims_locales)
aka fill_with_standard_claims
[show source]
# File lib/rodauth/features/oidc.rb 581 def fill_with_account_claims(claims, account, scopes, claims_locales) 582 additional_claims_info = {} 583 584 scopes_by_claim = scopes.each_with_object({}) do |scope, by_oidc| 585 next if scope == "openid" 586 587 if scope.is_a?(Array) 588 # essential claims 589 param, additional_info = scope 590 591 param = param.to_sym 592 593 oidc, = OIDC_SCOPES_MAP.find do |_, oidc_scopes| 594 oidc_scopes.include?(param) 595 end || param.to_s 596 597 param = nil if oidc == param.to_s 598 599 additional_claims_info[param] = additional_info 600 else 601 602 oidc, param = scope.split(".", 2) 603 604 param = param.to_sym if param 605 end 606 607 by_oidc[oidc] ||= [] 608 609 by_oidc[oidc] << param.to_sym if param 610 end 611 612 oidc_scopes, additional_scopes = scopes_by_claim.keys.partition { |key| OIDC_SCOPES_MAP.key?(key) } 613 614 claims_locales = claims_locales.split(" ").map(&:to_sym) if claims_locales 615 616 unless oidc_scopes.empty? 617 if respond_to?(:get_oidc_param) 618 get_oidc_param = proxy_get_param(:get_oidc_param, claims, claims_locales, additional_claims_info) 619 620 oidc_scopes.each do |scope| 621 scope_claims = claims 622 params = scopes_by_claim[scope] 623 params = params.empty? ? OIDC_SCOPES_MAP[scope] : (OIDC_SCOPES_MAP[scope] & params) 624 625 scope_claims = (claims["address"] = {}) if scope == "address" 626 627 params.each do |param| 628 get_oidc_param[account, param, scope_claims] 629 end 630 end 631 else 632 warn "`get_oidc_param(account, claim)` must be implemented to use oidc scopes." 633 end 634 end 635 636 return if additional_scopes.empty? 637 638 if respond_to?(:get_additional_param) 639 get_additional_param = proxy_get_param(:get_additional_param, claims, claims_locales, additional_claims_info) 640 641 additional_scopes.each do |scope| 642 get_additional_param[account, scope.to_sym] 643 end 644 else 645 warn "`get_additional_param(account, claim)` must be implemented to use oidc scopes." 646 end 647 end
form_post_error_response_html(url)
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 128 def form_post_error_response_html(url) 129 <<-FORM 130 <html> 131 <head><title></title></head> 132 <body onload="javascript:document.forms[0].submit()"> 133 <form method="post" action="#{url}"> 134 #{yield} 135 </form> 136 </body> 137 </html> 138 FORM 139 end
form_post_response_html(url)
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 114 def form_post_response_html(url) 115 <<-FORM 116 <html> 117 <head><title>Authorized</title></head> 118 <body onload="javascript:document.forms[0].submit()"> 119 <form method="post" action="#{url}"> 120 #{yield} 121 <input type="submit" class="btn btn-outline-primary" value="#{scope.h(oauth_authorize_post_button)}"/> 122 </form> 123 </body> 124 </html> 125 FORM 126 end
generate_dpop_nonce(dpop_claims)
Nonce
[show source]
# File lib/rodauth/features/oauth_dpop.rb 357 def generate_dpop_nonce(dpop_claims) 358 issued_at = Time.now.to_i 359 360 aud = "#{dpop_claims['htm']}:#{dpop_claims['htu']}" 361 362 nonce_claims = { 363 iss: oauth_jwt_issuer, 364 iat: issued_at, 365 exp: issued_at + oauth_dpop_nonce_expires_in, 366 aud: aud 367 } 368 369 jwt_encode(nonce_claims) 370 end
generate_frontchannel_logout_urls(visited_sites, logout_urls)
[show source]
# File lib/rodauth/features/oidc_frontchannel_logout.rb 76 def generate_frontchannel_logout_urls(visited_sites, logout_urls) 77 @frontchannel_logout_urls = logout_urls.flat_map do |client_id, logout_url| 78 next unless logout_url 79 80 sids = visited_sites.select { |cid, _| cid == client_id }.map(&:last) 81 82 sids.map do |sid| 83 logout_url = URI(logout_url) 84 85 if sid 86 query = logout_url.query 87 query = if query 88 URI.decode_www_form(query) 89 else 90 [] 91 end 92 query << ["iss", oauth_jwt_issuer] 93 query << ["sid", sid] 94 logout_url.query = URI.encode_www_form(query) 95 end 96 97 logout_url 98 end 99 end.compact 100 end
generate_id_token(oauth_grant, include_claims = false)
[show source]
# File lib/rodauth/features/oidc.rb 508 def generate_id_token(oauth_grant, include_claims = false) 509 oauth_scopes = oauth_grant[oauth_grants_scopes_column].split(oauth_scope_separator) 510 511 return unless oauth_scopes.include?("openid") 512 513 signing_algorithm = oauth_application[oauth_applications_id_token_signed_response_alg_column] || 514 oauth_jwt_keys.keys.first 515 516 id_claims = id_token_claims(oauth_grant, signing_algorithm) 517 518 account = db[accounts_table].where(account_id_column => oauth_grant[oauth_grants_account_id_column]).first 519 520 # this should never happen! 521 # a newly minted oauth token from a grant should have been assigned to an account 522 # who just authorized its generation. 523 return unless account 524 525 if (claims = oauth_grant[oauth_grants_claims_column]) 526 claims = JSON.parse(claims) 527 if (id_token_essential_claims = claims["id_token"]) 528 oauth_scopes |= id_token_essential_claims.to_a 529 530 include_claims = true 531 end 532 end 533 534 # OpenID Connect Core 1.0's 5.4 Requesting Claims using Scope Values: 535 # If standard claims (profile, email, etc) are requested as scope values in the Authorization Request, 536 # include in the response. 537 include_claims ||= (OIDC_SCOPES_MAP.keys & oauth_scopes).any? 538 539 # However, when no Access Token is issued (which is the case for the response_type value id_token), 540 # the resulting Claims are returned in the ID Token. 541 fill_with_account_claims(id_claims, account, oauth_scopes, param_or_nil("claims_locales")) if include_claims 542 543 params = { 544 jwks: oauth_application_jwks(oauth_application), 545 signing_algorithm: signing_algorithm, 546 encryption_algorithm: oauth_application[oauth_applications_id_token_encrypted_response_alg_column], 547 encryption_method: oauth_application[oauth_applications_id_token_encrypted_response_enc_column], 548 549 # Not officially part of the spec, but some providers follow this convention. 550 # This is useful for distinguishing between ID Tokens and JWT Access Tokens. 551 headers: { typ: "id_token+jwt" } 552 }.compact 553 554 oauth_grant[:id_token] = jwt_encode(id_claims, **params) 555 end
generate_jti(payload)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 98 def generate_jti(payload) 99 # Use the key and iat to create a unique key per request to prevent replay attacks 100 jti_raw = [ 101 payload[:aud] || payload["aud"], 102 payload[:iat] || payload["iat"] 103 ].join(":").to_s 104 Digest::SHA256.hexdigest(jti_raw) 105 end
generate_logout_token(oauth_application, sid)
[show source]
# File lib/rodauth/features/oidc_backchannel_logout.rb 50 def generate_logout_token(oauth_application, sid) 51 issued_at = Time.now.to_i 52 53 logout_claims = { 54 iss: oauth_jwt_issuer, # issuer 55 iat: issued_at, # issued at 56 exp: issued_at + oauth_logout_token_expires_in, 57 aud: oauth_application[oauth_applications_client_id_column], 58 events: { 59 "http://schemas.openid.net/event/backchannel-logout": {} 60 } 61 } 62 63 logout_claims[:sid] = sid if sid 64 65 signing_algorithm = oauth_application[oauth_applications_id_token_signed_response_alg_column] || 66 oauth_jwt_keys.keys.first 67 68 params = { 69 jwks: oauth_application_jwks(oauth_application), 70 headers: { typ: "logout+jwt" }, 71 signing_algorithm: signing_algorithm, 72 encryption_algorithm: oauth_application[oauth_applications_id_token_encrypted_response_alg_column], 73 encryption_method: oauth_application[oauth_applications_id_token_encrypted_response_enc_column] 74 }.compact 75 76 jwt_encode(logout_claims, **params) 77 end
generate_saml_settings(saml_settings)
[show source]
# File lib/rodauth/features/oauth_saml_bearer_grant.rb 72 def generate_saml_settings(saml_settings) 73 settings = OneLogin::RubySaml::Settings.new 74 75 # issuer 76 settings.idp_entity_id = saml_settings[oauth_saml_settings_issuer_column] 77 78 # audience 79 settings.sp_entity_id = saml_settings[oauth_saml_settings_audience_column] || token_url 80 81 # recipient 82 settings.assertion_consumer_service_url = token_url 83 84 settings.idp_cert = saml_settings[oauth_saml_settings_idp_cert_column] 85 settings.idp_cert_fingerprint = saml_settings[oauth_saml_settings_idp_cert_fingerprint_column] 86 settings.idp_cert_fingerprint_algorithm = saml_settings[oauth_saml_settings_idp_cert_fingerprint_algorithm_column] 87 88 if settings.idp_cert 89 check_idp_cert_expiration = saml_settings[oauth_saml_settings_idp_cert_check_expiration_column] 90 check_idp_cert_expiration = oauth_saml_idp_cert_check_expiration if check_idp_cert_expiration.nil? 91 settings.security[:check_idp_cert_expiration] = check_idp_cert_expiration 92 end 93 settings.security[:strict_audience_validation] = true 94 settings.security[:want_name_id] = true 95 96 settings.name_identifier_format = saml_settings[oauth_saml_settings_name_identifier_format_column] || 97 oauth_saml_name_identifier_format 98 settings 99 end
generate_session_state()
[show source]
# File lib/rodauth/features/oidc_session_management.rb 64 def generate_session_state 65 salt = oauth_oidc_session_management_salt 66 67 uri = URI(redirect_uri) 68 origin = if uri.respond_to?(:origin) 69 uri.origin 70 else 71 # TODO: remove when not supporting uri < 0.11 72 "#{uri.scheme}://#{uri.host}#{":#{uri.port}" if uri.port != uri.default_port}" 73 end 74 session_id = "#{oauth_application[oauth_applications_client_id_column]} " \ 75 "#{origin} " \ 76 "#{request.cookies[oauth_oidc_user_agent_state_cookie_key]} #{salt}" 77 78 "#{Digest::SHA256.hexdigest(session_id)}.#{salt}" 79 end
generate_token(grant_params = {}, should_generate_refresh_token = true)
[show source]
# File lib/rodauth/features/oauth_base.rb 464 def generate_token(grant_params = {}, should_generate_refresh_token = true) 465 if grant_params[oauth_grants_id_column] && oauth_reuse_access_token && 466 ( 467 if oauth_grants_token_hash_column 468 grant_params[oauth_grants_token_hash_column] 469 else 470 grant_params[oauth_grants_token_column] 471 end 472 ) 473 return grant_params 474 end 475 476 update_params = { 477 oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_access_token_expires_in), 478 oauth_grants_code_column => nil 479 } 480 481 rescue_from_uniqueness_error do 482 access_token = _generate_access_token(update_params) 483 refresh_token = _generate_refresh_token(update_params) if should_generate_refresh_token 484 oauth_grant = store_token(grant_params, update_params) 485 486 return unless oauth_grant 487 488 oauth_grant[oauth_grants_token_column] = access_token 489 oauth_grant[oauth_grants_refresh_token_column] = refresh_token if refresh_token 490 oauth_grant 491 end 492 end
generate_token_hash(token)
[show source]
# File lib/rodauth/features/oauth_base.rb 450 def generate_token_hash(token) 451 Base64.urlsafe_encode64(Digest::SHA256.digest(token)) 452 end
generate_user_code()
[show source]
# File lib/rodauth/features/oauth_device_code_grant.rb 120 def generate_user_code 121 user_code_size = oauth_device_code_grant_user_code_size 122 SecureRandom.random_number(36**user_code_size) 123 .to_s(36) # 0 to 9, a to z 124 .upcase 125 .rjust(user_code_size, "0") 126 end
get_oidc_account_last_login_at(account_id)
[show source]
# File lib/rodauth/features/oidc.rb 350 def get_oidc_account_last_login_at(account_id) 351 return get_activity_timestamp(account_id, account_activity_last_activity_column) if features.include?(:account_expiration) 352 353 # active sessions based 354 ds = db[active_sessions_table].where(active_sessions_account_id_column => account_id) 355 356 ds = ds.order(Sequel.desc(active_sessions_created_at_column)) 357 358 convert_timestamp(ds.get(active_sessions_created_at_column)) 359 end
grant_from_application?(oauth_grant, oauth_application)
[show source]
# File lib/rodauth/features/oauth_base.rb 454 def grant_from_application?(oauth_grant, oauth_application) 455 oauth_grant[oauth_grants_oauth_application_id_column] == oauth_application[oauth_applications_id_column] 456 end
id_token_claims(oauth_grant, signing_algorithm)
[show source]
# File lib/rodauth/features/oidc.rb 557 def id_token_claims(oauth_grant, signing_algorithm) 558 claims = jwt_claims(oauth_grant) 559 560 claims[:nonce] = oauth_grant[oauth_grants_nonce_column] if oauth_grant[oauth_grants_nonce_column] 561 562 claims[:acr] = oauth_grant[oauth_grants_acr_column] if oauth_grant[oauth_grants_acr_column] 563 564 # Time when the End-User authentication occurred. 565 claims[:auth_time] = get_oidc_account_last_login_at(oauth_grant[oauth_grants_account_id_column]).to_i 566 567 # Access Token hash value. 568 if (access_token = oauth_grant[oauth_grants_token_column]) 569 claims[:at_hash] = id_token_hash(access_token, signing_algorithm) 570 end 571 572 # code hash value. 573 if (code = oauth_grant[oauth_grants_code_column]) 574 claims[:c_hash] = id_token_hash(code, signing_algorithm) 575 end 576 577 claims 578 end
id_token_hash(hash, algo)
[show source]
# File lib/rodauth/features/oidc.rb 863 def id_token_hash(hash, algo) 864 digest = case algo 865 when /256/ then Digest::SHA256 866 when /384/ then Digest::SHA384 867 when /512/ then Digest::SHA512 868 end 869 870 return unless digest 871 872 hash = digest.digest(hash) 873 hash = hash[0...(hash.size / 2)] 874 Base64.urlsafe_encode64(hash).tr("=", "") 875 end
initialize_register_params(create_params, return_params)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 321 def initialize_register_params(create_params, return_params) 322 client_id = oauth_unique_id_generator 323 create_params[oauth_applications_client_id_column] = client_id 324 return_params["client_id"] = client_id 325 return_params["client_id_issued_at"] = Time.now.utc.iso8601 326 327 registration_access_token = oauth_unique_id_generator 328 create_params[oauth_applications_registration_access_token_column] = secret_hash(registration_access_token) 329 return_params["registration_access_token"] = registration_access_token 330 return_params["registration_client_uri"] = "#{base_url}/#{registration_client_uri_route}/#{return_params['client_id']}" 331 332 if create_params.key?(oauth_applications_client_secret_column) 333 set_client_secret(create_params, create_params[oauth_applications_client_secret_column]) 334 return_params.delete("client_secret") 335 else 336 client_secret = oauth_unique_id_generator 337 set_client_secret(create_params, client_secret) 338 return_params["client_secret"] = client_secret 339 return_params["client_secret_expires_at"] = 0 340 341 end 342 end
introspection_request(token_type_hint, token)
[show source]
# File lib/rodauth/features/oauth_resource_server.rb 47 def introspection_request(token_type_hint, token) 48 introspect_url = URI("#{authorization_server_url}#{introspect_path}") 49 50 response = http_request(introspect_url, { "token_type_hint" => token_type_hint, "token" => token }) do |request| 51 before_introspection_request(request) 52 end 53 54 JSON.parse(response.body) 55 end
json_access_token_payload(oauth_grant)
[show source]
# File lib/rodauth/features/oauth_base.rb 644 def json_access_token_payload(oauth_grant) 645 payload = { 646 "access_token" => oauth_grant[oauth_grants_token_column], 647 "token_type" => oauth_token_type, 648 "expires_in" => oauth_access_token_expires_in 649 } 650 payload["refresh_token"] = oauth_grant[oauth_grants_refresh_token_column] if oauth_grant[oauth_grants_refresh_token_column] 651 payload 652 end
json_request?()
copied from the jwt feature
[show source]
# File lib/rodauth/features/oauth_base.rb 195 def json_request? 196 return super if features.include?(:jsonn) 197 return @json_request if defined?(@json_request) 198 199 @json_request = request.content_type =~ json_request_regexp 200 end
json_response_oauth_application(oauth_application)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 402 def json_response_oauth_application(oauth_application) 403 params = methods.map { |k| k.to_s[/\Aoauth_applications_(\w+)_column\z/, 1] }.compact 404 405 body = params.each_with_object({}) do |k, hash| 406 next if %w[id account_id client_id client_secret cliennt_secret_hash].include?(k) 407 408 value = oauth_application[__send__(:"oauth_applications_#{k}_column")] 409 410 next unless value 411 412 case k 413 when "redirect_uri" 414 hash["redirect_uris"] = value.split(" ") 415 when "token_endpoint_auth_method", "grant_types", "response_types", "request_uris", "post_logout_redirect_uris" 416 hash[k] = value.split(" ") 417 when "scopes" 418 hash["scope"] = value 419 when "jwks" 420 hash[k] = value.is_a?(String) ? JSON.parse(value) : value 421 when "homepage_url" 422 hash["client_uri"] = value 423 when "name" 424 hash["client_name"] = value 425 else 426 hash[k] = value 427 end 428 end 429 430 response.status = 200 431 response["Content-Type"] ||= json_response_content_type 432 response["Cache-Control"] = "no-store" 433 response["Pragma"] = "no-cache" 434 json_payload = _json_response_body(body) 435 return_response(json_payload) 436 end
json_response_success(body, cache = false)
[show source]
# File lib/rodauth/features/oauth_base.rb 808 def json_response_success(body, cache = false) 809 response.status = 200 810 response["Content-Type"] ||= json_response_content_type 811 if cache 812 # defaulting to 1-day for everyone, for now at least 813 max_age = 60 * 60 * 24 814 response["Cache-Control"] = "private, max-age=#{max_age}" 815 else 816 response["Cache-Control"] = "no-store" 817 response["Pragma"] = "no-cache" 818 end 819 json_payload = _json_response_body(body) 820 return_response(json_payload) 821 end
json_token_introspect_payload(grant_or_claims)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 390 def json_token_introspect_payload(grant_or_claims) 391 claims = super 392 393 return claims unless grant_or_claims 394 395 if (jkt = grant_or_claims.dig("cnf", "jkt")) 396 (claims[:cnf] ||= {})[:jkt] = jkt 397 claims[:token_type] = "DPoP" 398 end 399 400 claims 401 end
json_webfinger_payload()
Webfinger
[show source]
# File lib/rodauth/features/oidc.rb 781 def json_webfinger_payload 782 JSON.dump({ 783 subject: param("resource"), 784 links: [{ 785 rel: "http://openid.net/specs/connect/1.0/issuer", 786 href: authorization_server_url 787 }] 788 }) 789 end
jwk_export(key)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 152 def jwk_export(key) 153 key_to_jwk(key) 154 end
jwk_import(jwk)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 156 def jwk_import(jwk) 157 JSON::JWK.new(jwk) 158 end
jwk_key(jwk)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 160 def jwk_key(jwk) 161 jwk = jwk_import(jwk) unless jwk.is_a?(JSON::JWK) 162 jwk.to_key 163 end
jwk_thumbprint(jwk)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 165 def jwk_thumbprint(jwk) 166 jwk = jwk_import(jwk) if jwk.is_a?(Hash) 167 jwk.thumbprint 168 end
jwks_set()
[show source]
# File lib/rodauth/features/oauth_jwt_jwks.rb 28 def jwks_set 29 @jwks_set ||= [ 30 *( 31 unless oauth_jwt_public_keys.empty? 32 oauth_jwt_public_keys.flat_map { |algo, pkeys| Array(pkeys).map { |pkey| jwk_export(pkey).merge(use: "sig", alg: algo) } } 33 end 34 ), 35 *( 36 unless oauth_jwt_jwe_public_keys.empty? 37 oauth_jwt_jwe_public_keys.flat_map do |(algo, _enc), pkeys| 38 Array(pkeys).map do |pkey| 39 jwk_export(pkey).merge(use: "enc", alg: algo) 40 end 41 end 42 end 43 ) 44 ].compact 45 end
jwt_assertion(assertion, **kwargs)
[show source]
# File lib/rodauth/features/oauth_jwt_bearer_grant.rb 83 def jwt_assertion(assertion, **kwargs) 84 claims = jwt_decode(assertion, verify_iss: false, verify_aud: false, verify_jti: false, **kwargs) 85 86 return unless claims && verify_aud(request.url, claims["aud"]) 87 88 claims 89 end
jwt_claims(oauth_grant)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 234 def jwt_claims(oauth_grant) 235 claims = super 236 if @dpop_thumbprint 237 # the authorization server associates the issued access token with the 238 # public key from the DPoP proof 239 claims[:cnf] = { jkt: @dpop_thumbprint } 240 end 241 claims 242 end
jwt_decode( token, jwks: nil, jws_algorithm: oauth_jwt_public_keys.keys.first || oauth_jwt_keys.keys.first, jws_key: oauth_jwt_keys[jws_algorithm] || _jwt_key, jws_encryption_algorithm: oauth_jwt_jwe_keys.keys.dig(0, 0), jws_encryption_method: oauth_jwt_jwe_keys.keys.dig(0, 1), jwe_key: oauth_jwt_jwe_keys[[jws_encryption_algorithm, jws_encryption_method]] || oauth_jwt_jwe_keys.values.first, verify_claims: true, verify_jti: true, verify_iss: true, verify_aud: true, verify_headers: nil, ** )
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 217 def jwt_decode( 218 token, 219 jwks: nil, 220 jws_algorithm: oauth_jwt_public_keys.keys.first || oauth_jwt_keys.keys.first, 221 jws_key: oauth_jwt_keys[jws_algorithm] || _jwt_key, 222 jws_encryption_algorithm: oauth_jwt_jwe_keys.keys.dig(0, 0), 223 jws_encryption_method: oauth_jwt_jwe_keys.keys.dig(0, 1), 224 jwe_key: oauth_jwt_jwe_keys[[jws_encryption_algorithm, jws_encryption_method]] || oauth_jwt_jwe_keys.values.first, 225 verify_claims: true, 226 verify_jti: true, 227 verify_iss: true, 228 verify_aud: true, 229 verify_headers: nil, 230 ** 231 ) 232 jws_key = jws_key.first if jws_key.is_a?(Array) 233 234 if jwe_key 235 jwe_key = jwe_key.first if jwe_key.is_a?(Array) 236 token = JSON::JWT.decode(token, jwe_key).plain_text 237 end 238 239 claims = if is_authorization_server? 240 if jwks 241 jwks = jwks[:keys] if jwks.is_a?(Hash) 242 243 enc_algs = [jws_encryption_algorithm].compact 244 enc_meths = [jws_encryption_method].compact 245 246 sig_algs = jws_algorithm ? [jws_algorithm] : jwks.select { |k| k[:use] == "sig" }.map { |k| k[:alg] } 247 sig_algs = sig_algs.compact.map(&:to_sym) 248 249 # JWKs may be set up without a KID, when there's a single one 250 if jwks.size == 1 && !jwks[0][:kid] 251 key = jwks[0] 252 jwk_key = JSON::JWK.new(key) 253 jws = JSON::JWT.decode(token, jwk_key) 254 else 255 jws = JSON::JWT.decode(token, JSON::JWK::Set.new({ keys: jwks }), enc_algs + sig_algs, enc_meths) 256 jws = JSON::JWT.decode(jws.plain_text, JSON::JWK::Set.new({ keys: jwks }), sig_algs) if jws.is_a?(JSON::JWE) 257 end 258 jws 259 elsif jws_key 260 JSON::JWT.decode(token, jws_key) 261 else 262 JSON::JWT.decode(token, nil, jws_algorithm) 263 end 264 elsif (jwks = auth_server_jwks_set) 265 JSON::JWT.decode(token, JSON::JWK::Set.new(jwks)) 266 end 267 268 now = Time.now 269 if verify_claims && 270 (!claims[:exp] || Time.at(claims[:exp]) < now) && 271 claims[:nbf] && Time.at(claims[:nbf]) < now && 272 claims[:iat] && Time.at(claims[:iat]) < now && 273 verify_iss && claims[:iss] != oauth_jwt_issuer && 274 verify_aud && !verify_aud(claims[:aud], claims[:client_id]) && 275 verify_jti && !verify_jti(claims[:jti], claims) 276 277 return 278 end 279 280 return if verify_headers && !verify_headers.call(claims.header) 281 282 claims 283 rescue JSON::JWT::Exception 284 nil 285 end
jwt_decode_no_key(token)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 287 def jwt_decode_no_key(token) 288 jws = JSON::JWT.decode(token, :skip_verification) 289 [jws.to_h, jws.header] 290 end
jwt_decode_with_jwe( token, jwks: nil, jws_encryption_algorithm: oauth_jwt_jwe_keys.keys.dig(0, 0), jws_encryption_method: oauth_jwt_jwe_keys.keys.dig(0, 1), jwe_key: oauth_jwt_jwe_keys[[jws_encryption_algorithm, jws_encryption_method]] || oauth_jwt_jwe_keys.values.first, **args )
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 469 def jwt_decode_with_jwe( 470 token, 471 jwks: nil, 472 jws_encryption_algorithm: oauth_jwt_jwe_keys.keys.dig(0, 0), 473 jws_encryption_method: oauth_jwt_jwe_keys.keys.dig(0, 1), 474 jwe_key: oauth_jwt_jwe_keys[[jws_encryption_algorithm, jws_encryption_method]] || oauth_jwt_jwe_keys.values.first, 475 **args 476 ) 477 token = if jwks && jwks.any? { |k| k[:use] == "enc" } 478 JWE.__rodauth_oauth_decrypt_from_jwks(token, jwks, alg: jws_encryption_algorithm, enc: jws_encryption_method) 479 elsif jwe_key 480 jwe_key = jwe_key.first if jwe_key.is_a?(Array) 481 JWE.decrypt(token, jwe_key) 482 else 483 token 484 end 485 486 jwt_decode_without_jwe(token, jwks: jwks, **args) 487 rescue JWE::DecodeError => e 488 jwt_decode_without_jwe(token, jwks: jwks, **args) if e.message.include?("Not enough or too many segments") 489 end
jwt_encode(payload, jwks: nil, headers: {}, encryption_algorithm: oauth_jwt_jwe_keys.keys.dig(0, 0), encryption_method: oauth_jwt_jwe_keys.keys.dig(0, 1), jwe_key: oauth_jwt_jwe_keys[[encryption_algorithm, encryption_method]], signing_algorithm: oauth_jwt_keys.keys.first)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 174 def jwt_encode(payload, 175 jwks: nil, 176 headers: {}, 177 encryption_algorithm: oauth_jwt_jwe_keys.keys.dig(0, 0), 178 encryption_method: oauth_jwt_jwe_keys.keys.dig(0, 1), 179 jwe_key: oauth_jwt_jwe_keys[[encryption_algorithm, 180 encryption_method]], 181 signing_algorithm: oauth_jwt_keys.keys.first) 182 payload[:jti] = generate_jti(payload) 183 jwt = JSON::JWT.new(payload) 184 185 key = oauth_jwt_keys[signing_algorithm] || _jwt_key 186 key = key.first if key.is_a?(Array) 187 188 jwk = JSON::JWK.new(key || "") 189 190 # update headers 191 headers.each_key do |k| 192 if jwt.respond_to?(:"#{k}=") 193 jwt.send(:"#{k}=", headers[k]) 194 headers.delete(k) 195 end 196 end 197 jwt.header.merge(headers) unless headers.empty? 198 199 jwt = jwt.sign(jwk, signing_algorithm) 200 201 return jwt.to_s unless encryption_algorithm && encryption_method 202 203 if jwks && (jwk = jwks.find { |k| k[:use] == "enc" && k[:alg] == encryption_algorithm && k[:enc] == encryption_method }) 204 jwk = JSON::JWK.new(jwk) 205 jwe = jwt.encrypt(jwk, encryption_algorithm.to_sym, encryption_method.to_sym) 206 jwe.to_s 207 elsif jwe_key 208 jwe_key = jwe_key.first if jwe_key.is_a?(Array) 209 algorithm = encryption_algorithm.to_sym 210 meth = encryption_method.to_sym 211 jwt.encrypt(jwe_key, algorithm, meth) 212 else 213 jwt.to_s 214 end 215 end
jwt_encode_authorization_response_mode(params)
[show source]
# File lib/rodauth/features/oauth_jwt_secured_authorization_response_mode.rb 99 def jwt_encode_authorization_response_mode(params) 100 now = Time.now.to_i 101 claims = { 102 iss: oauth_jwt_issuer, 103 aud: oauth_application[oauth_applications_client_id_column], 104 exp: now + oauth_authorization_response_mode_expires_in, 105 iat: now 106 }.merge(params) 107 108 encode_params = { 109 jwks: oauth_application_jwks(oauth_application), 110 signing_algorithm: oauth_application[oauth_applications_authorization_signed_response_alg_column], 111 encryption_algorithm: oauth_application[oauth_applications_authorization_encrypted_response_alg_column], 112 encryption_method: oauth_application[oauth_applications_authorization_encrypted_response_enc_column] 113 }.compact 114 115 jwt_encode(claims, **encode_params) 116 end
jwt_encode_with_jwe( payload, jwks: nil, encryption_algorithm: oauth_jwt_jwe_keys.keys.dig(0, 0), encryption_method: oauth_jwt_jwe_keys.keys.dig(0, 1), jwe_key: oauth_jwt_jwe_keys[[encryption_algorithm, encryption_method]], **args )
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 367 def jwt_encode_with_jwe( 368 payload, 369 jwks: nil, 370 encryption_algorithm: oauth_jwt_jwe_keys.keys.dig(0, 0), 371 encryption_method: oauth_jwt_jwe_keys.keys.dig(0, 1), 372 jwe_key: oauth_jwt_jwe_keys[[encryption_algorithm, encryption_method]], 373 **args 374 ) 375 token = jwt_encode_without_jwe(payload, **args) 376 377 return token unless encryption_algorithm && encryption_method 378 379 if jwks && jwks.any? { |k| k[:use] == "enc" } 380 JWE.__rodauth_oauth_encrypt_from_jwks(token, jwks, alg: encryption_algorithm, enc: encryption_method) 381 elsif jwe_key 382 jwe_key = jwe_key.first if jwe_key.is_a?(Array) 383 params = { 384 zip: "DEF", 385 copyright: oauth_jwt_jwe_copyright 386 } 387 params[:enc] = encryption_method if encryption_method 388 params[:alg] = encryption_algorithm if encryption_algorithm 389 JWE.encrypt(token, jwe_key, **params) 390 else 391 token 392 end 393 end
jwt_response_success(jwt, cache = false)
[show source]
# File lib/rodauth/features/oidc.rb 849 def jwt_response_success(jwt, cache = false) 850 response.status = 200 851 response["Content-Type"] ||= "application/jwt" 852 if cache 853 # defaulting to 1-day for everyone, for now at least 854 max_age = 60 * 60 * 24 855 response["Cache-Control"] = "private, max-age=#{max_age}" 856 else 857 response["Cache-Control"] = "no-store" 858 response["Pragma"] = "no-cache" 859 end 860 return_response(jwt) 861 end
jwt_subject(account_unique_id, client_application = oauth_application)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 66 def jwt_subject(account_unique_id, client_application = oauth_application) 67 (account_unique_id || client_application[oauth_applications_client_id_column]).to_s 68 end
key_to_jwk(key)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 148 def key_to_jwk(key) 149 JSON::JWK.new(key) 150 end
load_oauth_application_management_routes()
/oauth-applications routes
[show source]
# File lib/rodauth/features/oauth_application_management.rb 73 def load_oauth_application_management_routes 74 request.on(oauth_applications_route) do 75 check_csrf if check_csrf? 76 require_account 77 78 request.get "new" do 79 new_oauth_application_view 80 end 81 82 request.on(oauth_applications_id_pattern) do |id| 83 oauth_application = db[oauth_applications_table] 84 .where(oauth_applications_id_column => id) 85 .where(oauth_applications_account_id_column => account_id) 86 .first 87 next unless oauth_application 88 89 scope.instance_variable_set(:@oauth_application, oauth_application) 90 91 request.is do 92 request.get do 93 oauth_application_view 94 end 95 end 96 97 request.on(oauth_applications_oauth_grants_path) do 98 page = Integer(param_or_nil("page") || 1) 99 per_page = per_page_param(oauth_grants_per_page) 100 oauth_grants = db[oauth_grants_table] 101 .where(oauth_grants_oauth_application_id_column => id) 102 .order(Sequel.desc(oauth_grants_id_column)) 103 scope.instance_variable_set(:@oauth_grants, oauth_grants.paginate(page, per_page)) 104 request.is do 105 request.get do 106 oauth_application_oauth_grants_view 107 end 108 end 109 end 110 end 111 112 request.is do 113 request.get do 114 page = Integer(param_or_nil("page") || 1) 115 per_page = per_page_param(oauth_applications_per_page) 116 scope.instance_variable_set(:@oauth_applications, db[oauth_applications_table] 117 .where(oauth_applications_account_id_column => account_id) 118 .order(Sequel.desc(oauth_applications_id_column)) 119 .paginate(page, per_page)) 120 121 oauth_applications_view 122 end 123 124 request.post do 125 catch_error do 126 validate_oauth_application_params 127 128 transaction do 129 before_create_oauth_application 130 id = create_oauth_application 131 after_create_oauth_application 132 set_notice_flash create_oauth_application_notice_flash 133 redirect "#{request.path}/#{id}" 134 end 135 end 136 set_error_flash create_oauth_application_error_flash 137 new_oauth_application_view 138 end 139 end 140 end 141 end
load_oauth_grant_management_routes()
[show source]
# File lib/rodauth/features/oauth_grant_management.rb 36 def load_oauth_grant_management_routes 37 request.on(oauth_grants_route) do 38 check_csrf if check_csrf? 39 require_account 40 41 request.post(oauth_grants_id_pattern) do |id| 42 db[oauth_grants_table] 43 .where(oauth_grants_id_column => id) 44 .where(oauth_grants_account_id_column => account_id) 45 .update(oauth_grants_revoked_at_column => Sequel::CURRENT_TIMESTAMP) 46 47 set_notice_flash revoke_oauth_grant_notice_flash 48 redirect oauth_grants_path || "/" 49 end 50 51 request.is do 52 request.get do 53 page = Integer(param_or_nil("page") || 1) 54 per_page = per_page_param(oauth_grants_per_page) 55 56 scope.instance_variable_set(:@oauth_grants, db[oauth_grants_table] 57 .select(Sequel[oauth_grants_table].*, Sequel[oauth_applications_table][oauth_applications_name_column]) 58 .join(oauth_applications_table, Sequel[oauth_grants_table][oauth_grants_oauth_application_id_column] => 59 Sequel[oauth_applications_table][oauth_applications_id_column]) 60 .where(Sequel[oauth_grants_table][oauth_grants_account_id_column] => account_id) 61 .where(oauth_grants_revoked_at_column => nil) 62 .order(Sequel.desc(oauth_grants_id_column)) 63 .paginate(page, per_page)) 64 oauth_grants_view 65 end 66 end 67 end 68 end
load_oauth_server_metadata_route(issuer = nil)
[show source]
# File lib/rodauth/features/oauth_base.rb 150 def load_oauth_server_metadata_route(issuer = nil) 151 request.on(".well-known") do 152 request.get("oauth-authorization-server") do 153 json_response_success(oauth_server_metadata_body(issuer), true) 154 end 155 end 156 end
load_openid_configuration_route(alt_issuer = nil)
[show source]
# File lib/rodauth/features/oidc.rb 186 def load_openid_configuration_route(alt_issuer = nil) 187 request.on(".well-known/openid-configuration") do 188 allow_cors(request) 189 190 request.is do 191 request.get do 192 json_response_success(openid_configuration_body(alt_issuer), cache: true) 193 end 194 end 195 end 196 end
load_registration_client_uri_routes()
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 17 def load_registration_client_uri_routes 18 request.on(registration_client_uri_route) do 19 # CLIENT REGISTRATION URI 20 request.on(String) do |client_id| 21 token = (v = request.env["HTTP_AUTHORIZATION"]) && v[/\A *Bearer (.*)\Z/, 1] 22 23 next unless token 24 25 oauth_application = db[oauth_applications_table] 26 .where(oauth_applications_client_id_column => client_id) 27 .first 28 next unless oauth_application 29 30 authorization_required unless password_hash_match?(oauth_application[oauth_applications_registration_access_token_column], token) 31 32 request.is do 33 request.get do 34 json_response_oauth_application(oauth_application) 35 end 36 request.on method: :put do 37 %w[client_id registration_access_token registration_client_uri client_secret_expires_at 38 client_id_issued_at].each do |prohibited_param| 39 if request.params.key?(prohibited_param) 40 register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message(prohibited_param)) 41 end 42 end 43 validate_client_registration_params 44 45 # if the client includes the "client_secret" field in the request, the value of this field MUST match the currently 46 # issued client secret for that client. The client MUST NOT be allowed to overwrite its existing client secret with 47 # its own chosen value. 48 authorization_required if request.params.key?("client_secret") && secret_matches?(oauth_application, 49 request.params["client_secret"]) 50 51 oauth_application = transaction do 52 applications_ds = db[oauth_applications_table] 53 __update_and_return__(applications_ds, @oauth_application_params) 54 end 55 json_response_oauth_application(oauth_application) 56 end 57 58 request.on method: :delete do 59 applications_ds = db[oauth_applications_table] 60 applications_ds.where(oauth_applications_client_id_column => client_id).delete 61 response.status = 204 62 response["Cache-Control"] = "no-store" 63 response["Pragma"] = "no-cache" 64 response.finish 65 end 66 end 67 end 68 end 69 end
load_webfinger_route()
[show source]
# File lib/rodauth/features/oidc.rb 198 def load_webfinger_route 199 request.on(".well-known/webfinger") do 200 request.get do 201 resource = param_or_nil("resource") 202 203 throw_json_response_error(400, "invalid_request") unless resource 204 205 response.status = 200 206 response["Content-Type"] ||= "application/jrd+json" 207 208 return_response(json_webfinger_payload) 209 end 210 end 211 end
logout()
[show source]
# File lib/rodauth/features/oidc_backchannel_logout.rb 18 def logout 19 visited_sites = session[visited_sites_key] 20 21 return super unless visited_sites 22 23 oauth_applications = db[oauth_applications_table].where(oauth_applications_client_id_column => visited_sites.map(&:first)) 24 .as_hash(oauth_applications_id_column) 25 26 logout_params = oauth_applications.flat_map do |_id, oauth_application| 27 logout_url = oauth_application[oauth_applications_backchannel_logout_uri_column] 28 29 next unless logout_url 30 31 client_id = oauth_application[oauth_applications_client_id_column] 32 33 sids = visited_sites.select { |cid, _| cid == client_id }.map(&:last) 34 35 sids.map do |sid| 36 logout_token = generate_logout_token(oauth_application, sid) 37 38 [logout_url, logout_token] 39 end 40 end.compact 41 42 perform_logout_requests(logout_params) unless logout_params.empty? 43 44 # now we can clear the session 45 super 46 end
normalize_redirect_uri_for_comparison(redirect_uri)
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 239 def normalize_redirect_uri_for_comparison(redirect_uri) 240 uri = URI(redirect_uri) 241 242 return redirect_uri unless uri.scheme == "http" && uri.port 243 244 hostname = uri.hostname 245 246 # https://www.rfc-editor.org/rfc/rfc8252#section-7.3 247 # ignore (potentially ephemeral) port number for native clients per RFC8252 248 begin 249 ip = IPAddr.new(hostname) 250 uri.port = nil if ip.loopback? 251 rescue IPAddr::InvalidAddressError 252 # https://www.rfc-editor.org/rfc/rfc8252#section-8.3 253 # Although the use of localhost is NOT RECOMMENDED, it is still allowed. 254 uri.port = nil if hostname == "localhost" 255 end 256 257 uri.to_s 258 end
oauth_account_ds(account_id)
[show source]
# File lib/rodauth/features/oauth_base.rb 314 def oauth_account_ds(account_id) 315 account_ds(account_id) 316 end
oauth_acr_values_supported()
[show source]
# File lib/rodauth/features/oidc.rb 275 def oauth_acr_values_supported 276 acr_values = [] 277 acr_values << "phrh" if features.include?(:webauthn_login) 278 acr_values << "phr" if respond_to?(:require_two_factor_authenticated) 279 acr_values 280 end
oauth_application()
[show source]
# File lib/rodauth/features/oauth_base.rb 221 def oauth_application 222 return @oauth_application if defined?(@oauth_application) 223 224 @oauth_application = begin 225 client_id = param_or_nil("client_id") 226 227 return unless client_id 228 229 db[oauth_applications_table].filter(oauth_applications_client_id_column => client_id).first 230 end 231 end
oauth_application_ds(oauth_application_id)
[show source]
# File lib/rodauth/features/oauth_base.rb 318 def oauth_application_ds(oauth_application_id) 319 db[oauth_applications_table].where(oauth_applications_id_column => oauth_application_id) 320 end
oauth_application_in_visited_sites()
[show source]
# File lib/rodauth/features/oidc_logout_base.rb 43 def oauth_application_in_visited_sites 44 visited_sites = session[visited_sites_key] || [] 45 46 session_id = yield 47 48 visited_site = [oauth_application[oauth_applications_client_id_column], session_id] 49 50 return if visited_sites.include?(visited_site) 51 52 visited_sites << visited_site 53 set_session_value(visited_sites_key, visited_sites) 54 end
oauth_application_jwks(oauth_application)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 115 def oauth_application_jwks(oauth_application) 116 jwks = oauth_application[oauth_applications_jwks_column] 117 118 if jwks 119 jwks = JSON.parse(jwks, symbolize_names: true) if jwks.is_a?(String) 120 return jwks 121 end 122 123 jwks_uri = oauth_application[oauth_applications_jwks_uri_column] 124 125 return unless jwks_uri 126 127 jwks_uri = URI(jwks_uri) 128 129 http_request_with_cache(jwks_uri) 130 end
oauth_application_params()
[show source]
# File lib/rodauth/features/oauth_application_management.rb 145 def oauth_application_params 146 @oauth_application_params ||= oauth_application_required_params.each_with_object({}) do |param, params| 147 value = request.params[__send__(:"oauth_application_#{param}_param")] 148 if value && !value.empty? 149 params[param] = value 150 else 151 set_field_error(param, null_error_message) 152 end 153 end 154 end
oauth_application_path(id)
[show source]
# File lib/rodauth/features/oauth_application_management.rb 68 def oauth_application_path(id) 69 "#{oauth_applications_path}/#{id}" 70 end
oauth_applications_path(opts = {})
[show source]
# File lib/rodauth/features/oauth_application_management.rb 64 def oauth_applications_path(opts = {}) 65 route_path(oauth_applications_route, opts) 66 end
oauth_grant_by_refresh_token(token, **kwargs)
[show source]
# File lib/rodauth/features/oauth_base.rb 640 def oauth_grant_by_refresh_token(token, **kwargs) 641 oauth_grant_by_refresh_token_ds(token, **kwargs).first 642 end
oauth_grant_by_refresh_token_ds(token, revoked: false)
[show source]
# File lib/rodauth/features/oauth_base.rb 619 def oauth_grant_by_refresh_token_ds(token, revoked: false) 620 ds = db[oauth_grants_table].where(oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column]) 621 # 622 # filter expired refresh tokens out. 623 # an expired refresh token is a token whose access token expired for a period longer than the 624 # refresh token expiration period. 625 # 626 ds = ds.where(Sequel.date_add(oauth_grants_expires_in_column, 627 seconds: (oauth_refresh_token_expires_in - oauth_access_token_expires_in)) >= Sequel::CURRENT_TIMESTAMP) 628 629 ds = if oauth_grants_refresh_token_hash_column 630 ds.where(oauth_grants_refresh_token_hash_column => generate_token_hash(token)) 631 else 632 ds.where(oauth_grants_refresh_token_column => token) 633 end 634 635 ds = ds.where(oauth_grants_revoked_at_column => nil) unless revoked 636 637 ds 638 end
oauth_grant_by_token(token)
[show source]
# File lib/rodauth/features/oauth_base.rb 615 def oauth_grant_by_token(token) 616 oauth_grant_by_token_ds(token).first 617 end
oauth_grant_by_token_ds(token)
[show source]
# File lib/rodauth/features/oauth_base.rb 605 def oauth_grant_by_token_ds(token) 606 ds = valid_oauth_grant_ds 607 608 if oauth_grants_token_hash_column 609 ds.where(Sequel[oauth_grants_table][oauth_grants_token_hash_column] => generate_token_hash(token)) 610 else 611 ds.where(Sequel[oauth_grants_table][oauth_grants_token_column] => token) 612 end 613 end
oauth_grant_path(id)
[show source]
# File lib/rodauth/features/oauth_grant_management.rb 32 def oauth_grant_path(id) 33 "#{oauth_grants_path}/#{id}" 34 end
oauth_grant_types_supported()
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 11 def oauth_grant_types_supported 12 super | %w[authorization_code] 13 end
oauth_grants_path(opts = {})
[show source]
# File lib/rodauth/features/oauth_grant_management.rb 28 def oauth_grants_path(opts = {}) 29 route_path(oauth_grants_route, opts) 30 end
oauth_grants_resource_owner_columns()
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 131 def oauth_grants_resource_owner_columns 132 [oauth_grants_account_id_column] 133 end
oauth_grants_unique_columns()
OAuth
Token Unique/Reuse
[show source]
# File lib/rodauth/features/oauth_base.rb 338 def oauth_grants_unique_columns 339 [ 340 oauth_grants_oauth_application_id_column, 341 oauth_grants_account_id_column, 342 oauth_grants_scopes_column 343 ] 344 end
oauth_jwt_audience()
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 39 def oauth_jwt_audience 40 # The JWT MUST contain an "aud" (audience) claim containing a 41 # value that identifies the authorization server as an intended 42 # audience. The token endpoint URL of the authorization server 43 # MAY be used as a value for an "aud" element to identify the 44 # authorization server as an intended audience of the JWT. 45 @oauth_jwt_audience ||= if is_authorization_server? 46 oauth_application[oauth_applications_client_id_column] 47 else 48 metadata = authorization_server_metadata 49 50 return unless metadata 51 52 metadata[:token_endpoint] 53 end 54 end
oauth_jwt_issuer()
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 33 def oauth_jwt_issuer 34 # The JWT MUST contain an "iss" (issuer) claim that contains a 35 # unique identifier for the entity that issued the JWT. 36 @oauth_jwt_issuer ||= authorization_server_url 37 end
oauth_jwt_jwe_algorithms_supported()
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 309 def oauth_jwt_jwe_algorithms_supported 310 JWE::VALID_ALG 311 end
oauth_jwt_jwe_encryption_methods_supported()
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 313 def oauth_jwt_jwe_encryption_methods_supported 314 JWE::VALID_ENC 315 end
oauth_management_pagination_link(page, label: page, current: false, classes: "")
[show source]
# File lib/rodauth/features/oauth_management_base.rb 22 def oauth_management_pagination_link(page, label: page, current: false, classes: "") 23 classes += " disabled" if current || !page 24 classes += " active" if current 25 if page 26 params = URI.encode_www_form(request.GET.merge("page" => page)) 27 28 href = "#{request.path}?#{params}" 29 30 <<-HTML 31 <li class="page-item #{classes}" #{'aria-current="page"' if current}> 32 <a class="page-link" href="#{href}" tabindex="-1" aria-disabled="#{current || !page}"> 33 #{label} 34 </a> 35 </li> 36 HTML 37 else 38 <<-HTML 39 <li class="page-item #{classes}"> 40 <span class="page-link"> 41 #{label} 42 #{'<span class="sr-only">(current)</span>' if current} 43 </span> 44 </li> 45 HTML 46 end 47 end
oauth_management_pagination_links(paginated_ds)
[show source]
# File lib/rodauth/features/oauth_management_base.rb 12 def oauth_management_pagination_links(paginated_ds) 13 html = +'<nav aria-label="Pagination"><ul class="pagination">' 14 html << oauth_management_pagination_link(paginated_ds.prev_page, label: oauth_management_pagination_previous_button) 15 html << oauth_management_pagination_link(paginated_ds.current_page - 1) unless paginated_ds.first_page? 16 html << oauth_management_pagination_link(paginated_ds.current_page, label: paginated_ds.current_page, current: true) 17 html << oauth_management_pagination_link(paginated_ds.current_page + 1) unless paginated_ds.last_page? 18 html << oauth_management_pagination_link(paginated_ds.next_page, label: oauth_management_pagination_next_button) 19 html << "</ul></nav>" 20 end
oauth_oidc_session_management_salt()
[show source]
# File lib/rodauth/features/oidc_session_management.rb 87 def oauth_oidc_session_management_salt 88 oauth_unique_id_generator 89 end
oauth_response_modes_for_code_supported()
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 41 def oauth_response_modes_for_code_supported 42 %w[query form_post] 43 end
oauth_response_modes_for_token_supported()
[show source]
# File lib/rodauth/features/oauth_implicit_grant.rb 37 def oauth_response_modes_for_token_supported 38 %w[fragment] 39 end
oauth_response_modes_supported()
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 19 def oauth_response_modes_supported 20 super | %w[query form_post] 21 end
oauth_response_types_supported()
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 15 def oauth_response_types_supported 16 super | %w[code] 17 end
oauth_server_metadata_body(*)
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 159 def oauth_server_metadata_body(*) 160 super.tap do |data| 161 data[:authorization_endpoint] = authorize_url 162 end 163 end
oauth_token_endpoint_auth_methods_supported()
[show source]
# File lib/rodauth/features/oauth_jwt_bearer_grant.rb 17 def oauth_token_endpoint_auth_methods_supported 18 if oauth_applications_client_secret_hash_column.nil? 19 super | %w[client_secret_jwt private_key_jwt urn:ietf:params:oauth:client-assertion-type:jwt-bearer] 20 else 21 super | %w[private_key_jwt] 22 end 23 end
oauth_token_subject()
[show source]
# File lib/rodauth/features/oauth_base.rb 167 def oauth_token_subject 168 return unless authorization_token 169 170 authorization_token[oauth_grants_account_id_column] || 171 db[oauth_applications_table].where( 172 oauth_applications_id_column => authorization_token[oauth_grants_oauth_application_id_column] 173 ).select_map(oauth_applications_client_id_column).first 174 end
oauth_unique_id_generator()
[show source]
# File lib/rodauth/features/oauth_base.rb 446 def oauth_unique_id_generator 447 SecureRandom.urlsafe_base64(32) 448 end
oidc_authorize_on_prompt_none?(_account)
[show source]
# File lib/rodauth/features/oidc.rb 282 def oidc_authorize_on_prompt_none?(_account) 283 false 284 end
oidc_grant_params()
[show source]
# File lib/rodauth/features/oidc.rb 747 def oidc_grant_params 748 grant_params = { 749 **resource_owner_params, 750 oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column], 751 oauth_grants_scopes_column => scopes.join(oauth_scope_separator), 752 oauth_grants_redirect_uri_column => param_or_nil("redirect_uri") 753 } 754 if (nonce = param_or_nil("nonce")) 755 grant_params[oauth_grants_nonce_column] = nonce 756 end 757 grant_params[oauth_grants_acr_column] = @acr if @acr 758 if (claims_locales = param_or_nil("claims_locales")) 759 grant_params[oauth_grants_claims_locales_column] = claims_locales 760 end 761 if (claims = param_or_nil("claims")) 762 grant_params[oauth_grants_claims_column] = claims 763 end 764 grant_params 765 end
openid_configuration_body(path = nil)
Metadata
[show source]
# File lib/rodauth/features/oidc.rb 793 def openid_configuration_body(path = nil) 794 metadata = oauth_server_metadata_body(path).slice(*VALID_METADATA_KEYS) 795 796 scope_claims = oauth_application_scopes.each_with_object([]) do |scope, claims| 797 oidc, param = scope.split(".", 2) 798 if param 799 claims << param 800 else 801 oidc_claims = OIDC_SCOPES_MAP[oidc] 802 claims.concat(oidc_claims) if oidc_claims 803 end 804 end 805 806 scope_claims.unshift("auth_time") 807 808 metadata.merge( 809 userinfo_endpoint: userinfo_url, 810 subject_types_supported: %w[public pairwise], 811 acr_values_supported: oauth_acr_values_supported, 812 claims_parameter_supported: true, 813 814 id_token_signing_alg_values_supported: oauth_jwt_jws_algorithms_supported, 815 id_token_encryption_alg_values_supported: oauth_jwt_jwe_algorithms_supported, 816 id_token_encryption_enc_values_supported: oauth_jwt_jwe_encryption_methods_supported, 817 818 userinfo_signing_alg_values_supported: oauth_jwt_jws_algorithms_supported, 819 userinfo_encryption_alg_values_supported: oauth_jwt_jwe_algorithms_supported, 820 userinfo_encryption_enc_values_supported: oauth_jwt_jwe_encryption_methods_supported, 821 822 request_object_signing_alg_values_supported: oauth_jwt_jws_algorithms_supported, 823 request_object_encryption_alg_values_supported: oauth_jwt_jwe_algorithms_supported, 824 request_object_encryption_enc_values_supported: oauth_jwt_jwe_encryption_methods_supported, 825 826 # These Claim Types are described in Section 5.6 of OpenID Connect Core 1.0 [OpenID.Core]. 827 # Values defined by this specification are normal, aggregated, and distributed. 828 # If omitted, the implementation supports only normal Claims. 829 claim_types_supported: %w[normal], 830 claims_supported: %w[sub iss iat exp aud] | scope_claims 831 ).reject do |key, val| 832 # Filter null values in optional items 833 (!REQUIRED_METADATA_KEYS.include?(key.to_sym) && val.nil?) || 834 # Claims with zero elements MUST be omitted from the response 835 (val.respond_to?(:empty?) && val.empty?) 836 end 837 end
parse_saml_assertion(assertion)
rubocop:disable Naming/MemoizedInstanceVariableName
[show source]
# File lib/rodauth/features/oauth_saml_bearer_grant.rb 102 def parse_saml_assertion(assertion) 103 return @assertion if defined?(@assertion) 104 105 response = OneLogin::RubySaml::Response.new(assertion) 106 107 # The SAML Assertion XML data MUST be encoded using base64url 108 redirect_response_error("invalid_grant", oauth_saml_assertion_not_base64_message) unless response.send(:base64_encoded?, assertion) 109 110 # 1. The Assertion's <Issuer> element MUST contain a unique identifier 111 # for the entity that issued the Assertion. 112 redirect_response_error("invalid_grant", oauth_saml_assertion_single_issuer_message) unless response.issuers.size == 1 113 114 @saml_settings = db[oauth_saml_settings_table].where( 115 oauth_saml_settings_issuer_column => response.issuers.first 116 ).first 117 118 redirect_response_error("invalid_grant", oauth_saml_settings_not_found_message) unless @saml_settings 119 120 response.settings = generate_saml_settings(@saml_settings) 121 122 # 2. The Assertion MUST contain a <Conditions> element ... 123 # 3. he Assertion MUST have an expiry that limits the time window ... 124 # 4. The Assertion MUST have an expiry that limits the time window ... 125 # 5. The <Subject> element MUST contain at least one ... 126 # 6. The authorization server MUST reject the entire Assertion if the ... 127 # 7. If the Assertion issuer directly authenticated the subject, ... 128 redirect_response_error("invalid_grant", response.errors.join("; ")) unless response.is_valid? 129 130 @assertion = response 131 end
password_hash(password)
[show source]
# File lib/rodauth/features/oauth_base.rb 458 def password_hash(password) 459 return super if features.include?(:login_password_requirements_base) 460 461 BCrypt::Password.create(password, cost: BCrypt::Engine::DEFAULT_COST) 462 end
per_page_param(default_per_page)
[show source]
# File lib/rodauth/features/oauth_management_base.rb 60 def per_page_param(default_per_page) 61 per_page = param_or_nil("per_page") 62 63 return default_per_page unless per_page 64 65 per_page = per_page.to_i 66 67 return default_per_page if per_page <= 0 68 69 [per_page, default_per_page].min 70 end
perform_logout_requests(logout_params)
[show source]
# File lib/rodauth/features/oidc_backchannel_logout.rb 79 def perform_logout_requests(logout_params) 80 # performs logout requests sequentially 81 logout_params.each do |logout_url, logout_token| 82 http_request(logout_url, { "logout_token" => logout_token }) 83 rescue StandardError 84 warn "failed to perform backchannel logout on #{logout_url}" 85 end 86 end
post_configure()
[show source]
# File lib/rodauth/features/oauth_base.rb 292 def post_configure 293 super 294 295 i18n_register(File.expand_path(File.join(__dir__, "..", "..", "..", "locales"))) if features.include?(:i18n) 296 297 # all of the extensions below involve DB changes. Resource server mode doesn't use 298 # database functions for OAuth though. 299 return unless is_authorization_server? 300 301 self.class.__send__(:include, Rodauth::OAuth::ExtendDatabase(db)) 302 303 # Check whether we can reutilize db entries for the same account / application pair 304 one_oauth_token_per_account = db.indexes(oauth_grants_table).values.any? do |definition| 305 definition[:unique] && 306 definition[:columns] == oauth_grants_unique_columns 307 end 308 309 self.class.send(:define_method, :__one_oauth_token_per_account) { one_oauth_token_per_account } 310 end
private_jwk?(jwk)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 170 def private_jwk?(jwk) 171 %w[d p q dp dq qi].any?(&jwk.method(:key?)) 172 end
proxy_get_param(get_param_func, claims, claims_locales, additional_claims_info)
[show source]
# File lib/rodauth/features/oidc.rb 649 def proxy_get_param(get_param_func, claims, claims_locales, additional_claims_info) 650 meth = method(get_param_func) 651 if meth.arity == 2 652 lambda do |account, param, cl = claims| 653 additional_info = additional_claims_info[param] || EMPTY_HASH 654 value = additional_info["value"] || meth[account, param] 655 value = nil if additional_info["values"] && additional_info["values"].include?(value) 656 cl[param] = value unless value.nil? 657 end 658 elsif claims_locales.nil? 659 lambda do |account, param, cl = claims| 660 additional_info = additional_claims_info[param] || EMPTY_HASH 661 value = additional_info["value"] || meth[account, param, nil] 662 value = nil if additional_info["values"] && additional_info["values"].include?(value) 663 cl[param] = value unless value.nil? 664 end 665 else 666 lambda do |account, param, cl = claims| 667 claims_values = claims_locales.map do |locale| 668 additional_info = additional_claims_info[param] || EMPTY_HASH 669 value = additional_info["value"] || meth[account, param, locale] 670 value = nil if additional_info["values"] && additional_info["values"].include?(value) 671 value 672 end.compact 673 674 if claims_values.uniq.size == 1 675 cl[param] = claims_values.first 676 else 677 claims_locales.zip(claims_values).each do |locale, value| 678 cl["#{param}##{locale}"] = value if value 679 end 680 end 681 end 682 end 683 end
redirect_authorize_error(parameter, referer = request.referer || default_redirect)
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 151 def redirect_authorize_error(parameter, referer = request.referer || default_redirect) 152 error_message = oauth_authorize_parameter_required(parameter: parameter) 153 154 if accepts_json? 155 status_code = oauth_invalid_response_status 156 157 throw_json_response_error(status_code, "invalid_request", error_message) 158 else 159 scope.instance_variable_set(:@error, error_message) 160 scope.instance_variable_set(:@back_url, referer) 161 162 return_response(authorize_error_view) 163 end 164 end
redirect_logout_with_error(error_message = oauth_invalid_client_message)
[show source]
# File lib/rodauth/features/oidc_rp_initiated_logout.rb 129 def redirect_logout_with_error(error_message = oauth_invalid_client_message) 130 set_notice_flash(error_message) 131 redirect(logout_redirect) 132 end
redirect_response_error(error_code, message = nil)
[show source]
# File lib/rodauth/features/oauth_base.rb 765 def redirect_response_error(error_code, message = nil) 766 if accepts_json? 767 status_code = if respond_to?(:"oauth_#{error_code}_response_status") 768 send(:"oauth_#{error_code}_response_status") 769 else 770 oauth_invalid_response_status 771 end 772 773 throw_json_response_error(status_code, error_code, message) 774 else 775 redirect_url = redirect_uri || request.referer || default_redirect 776 redirect_url = URI.parse(redirect_url) 777 params = response_error_params(error_code, message) 778 state = param_or_nil("state") 779 params["state"] = state if state 780 _redirect_response_error(redirect_url, params) 781 end 782 end
redirect_uri()
[show source]
# File lib/rodauth/features/oauth_base.rb 212 def redirect_uri 213 param_or_nil("redirect_uri") || begin 214 return unless oauth_application 215 216 redirect_uris = oauth_application[oauth_applications_redirect_uri_column].split(" ") 217 redirect_uris.size == 1 ? redirect_uris.first : nil 218 end 219 end
register_invalid_application_type_message(application_type)
[show source]
# File lib/rodauth/features/oidc_dynamic_client_registration.rb 286 def register_invalid_application_type_message(application_type) 287 "The application type '#{application_type}' is not allowed." 288 end
register_invalid_client_metadata_message(key, value)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 356 def register_invalid_client_metadata_message(key, value) 357 "The value '#{value}' is not supported by this server for param '#{key}'." 358 end
register_invalid_contacts_message(contacts)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 360 def register_invalid_contacts_message(contacts) 361 "The contacts '#{contacts}' are not allowed by this server." 362 end
register_invalid_jwks_param_message(key1, key2)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 368 def register_invalid_jwks_param_message(key1, key2) 369 "The param '#{key1}' cannot be accepted together with param '#{key2}'." 370 end
register_invalid_param_message(key)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 352 def register_invalid_param_message(key) 353 "The param '#{key}' is not supported by this server." 354 end
register_invalid_response_type_for_grant_type_message(response_type, grant_type)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 384 def register_invalid_response_type_for_grant_type_message(response_type, grant_type) 385 "The grant type '#{grant_type}' must be registered for the response " \ 386 "type '#{response_type}' to be allowed." 387 end
register_invalid_response_type_message(response_type)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 380 def register_invalid_response_type_message(response_type) 381 "The response type #{response_type} is not allowed by this server." 382 end
register_invalid_scopes_message(scopes)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 372 def register_invalid_scopes_message(scopes) 373 "The given scopes (#{scopes}) are not allowed by this server." 374 end
register_invalid_uri_message(uri)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 364 def register_invalid_uri_message(uri) 365 "The '#{uri}' URL is not allowed by this server." 366 end
register_oauth_invalid_grant_type_message(grant_type)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 376 def register_oauth_invalid_grant_type_message(grant_type) 377 "The grant type #{grant_type} is not allowed by this server." 378 end
register_required_param_message(key)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 348 def register_required_param_message(key) 349 "The param '#{key}' is required by this server." 350 end
register_throw_json_response_error(code, message)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 344 def register_throw_json_response_error(code, message) 345 throw_json_response_error(oauth_invalid_response_status, code, message) 346 end
request_object_encryption_alg_values_supported()
[show source]
# File lib/rodauth/features/oidc.rb 267 def request_object_encryption_alg_values_supported 268 oauth_jwt_jwe_algorithms_supported 269 end
request_object_encryption_enc_values_supported()
[show source]
# File lib/rodauth/features/oidc.rb 271 def request_object_encryption_enc_values_supported 272 oauth_jwt_jwe_encryption_methods_supported 273 end
request_object_signing_alg_values_supported()
[show source]
# File lib/rodauth/features/oidc.rb 263 def request_object_signing_alg_values_supported 264 oauth_jwt_jws_algorithms_supported 265 end
require_acr_value(_acr)
[show source]
# File lib/rodauth/features/oidc.rb 478 def require_acr_value(_acr) 479 true 480 end
require_acr_value_phr()
[show source]
# File lib/rodauth/features/oidc.rb 465 def require_acr_value_phr 466 return false unless respond_to?(:require_two_factor_authenticated) 467 468 require_two_factor_authenticated 469 true 470 end
require_acr_value_phrh()
[show source]
# File lib/rodauth/features/oidc.rb 472 def require_acr_value_phrh 473 return false unless features.include?(:webauthn_login) 474 475 require_acr_value_phr && two_factor_login_type_match?("webauthn") 476 end
require_authorizable_account()
[show source]
# File lib/rodauth/features/oauth_base.rb 322 def require_authorizable_account 323 require_account 324 end
require_oauth_application()
[show source]
# File lib/rodauth/features/oauth_assertion_base.rb 24 def require_oauth_application 25 if assertion_grant_type? 26 @oauth_application = __send__(:"require_oauth_application_from_#{assertion_grant_type}_assertion_issuer", param("assertion")) 27 elsif client_assertion_type? 28 @oauth_application = __send__(:"require_oauth_application_from_#{client_assertion_type}_assertion_subject", 29 param("client_assertion")) 30 31 if (client_id = param_or_nil("client_id")) && 32 client_id != @oauth_application[oauth_applications_client_id_column] 33 # If present, the value of the 34 # "client_id" parameter MUST identify the same client as is 35 # identified by the client assertion. 36 redirect_response_error("invalid_grant") 37 end 38 else 39 super 40 end 41 end
require_oauth_application_for_introspect()
[show source]
# File lib/rodauth/features/oauth_token_introspection.rb 101 def require_oauth_application_for_introspect 102 token = (v = request.env["HTTP_AUTHORIZATION"]) && v[/\A *Bearer (.*)\Z/, 1] 103 104 return require_oauth_application unless token 105 106 oauth_application = current_oauth_application 107 108 authorization_required unless oauth_application 109 110 @oauth_application = oauth_application 111 end
require_oauth_application_from_account()
[show source]
# File lib/rodauth/features/oauth_base.rb 412 def require_oauth_application_from_account 413 ds = db[oauth_applications_table] 414 .join(oauth_grants_table, Sequel[oauth_grants_table][oauth_grants_oauth_application_id_column] => 415 Sequel[oauth_applications_table][oauth_applications_id_column]) 416 .where(oauth_grant_by_token_ds(param("token")).opts.fetch(:where, true)) 417 .where(Sequel[oauth_applications_table][oauth_applications_account_id_column] => account_id) 418 419 @oauth_application = ds.qualify.first 420 return if @oauth_application 421 422 set_redirect_error_flash revoke_unauthorized_account_error_flash 423 redirect request.referer || "/" 424 end
require_oauth_application_from_client_secret_basic(token)
[show source]
# File lib/rodauth/features/oauth_base.rb 378 def require_oauth_application_from_client_secret_basic(token) 379 client_id, client_secret = Base64.decode64(token).split(":", 2) 380 authorization_required unless client_id 381 oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => client_id).first 382 authorization_required unless supports_auth_method?(oauth_application, 383 "client_secret_basic") && secret_matches?(oauth_application, client_secret) 384 oauth_application 385 end
require_oauth_application_from_client_secret_jwt(client_id, assertion, alg)
[show source]
# File lib/rodauth/features/oauth_jwt_bearer_grant.rb 57 def require_oauth_application_from_client_secret_jwt(client_id, assertion, alg) 58 oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => client_id).first 59 authorization_required unless oauth_application && supports_auth_method?(oauth_application, "client_secret_jwt") 60 client_secret = oauth_application[oauth_applications_client_secret_column] 61 claims = jwt_assertion(assertion, jws_key: client_secret, jws_algorithm: alg) 62 authorization_required unless claims && claims["iss"] == client_id 63 oauth_application 64 end
require_oauth_application_from_client_secret_post(client_id, client_secret)
[show source]
# File lib/rodauth/features/oauth_base.rb 387 def require_oauth_application_from_client_secret_post(client_id, client_secret) 388 oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => client_id).first 389 authorization_required unless supports_auth_method?(oauth_application, 390 "client_secret_post") && secret_matches?(oauth_application, client_secret) 391 oauth_application 392 end
require_oauth_application_from_jwt_bearer_assertion_issuer(assertion)
[show source]
# File lib/rodauth/features/oauth_jwt_bearer_grant.rb 31 def require_oauth_application_from_jwt_bearer_assertion_issuer(assertion) 32 claims = jwt_assertion(assertion) 33 34 return unless claims 35 36 db[oauth_applications_table].where( 37 oauth_applications_client_id_column => claims["iss"] 38 ).first 39 end
require_oauth_application_from_jwt_bearer_assertion_subject(assertion)
[show source]
# File lib/rodauth/features/oauth_jwt_bearer_grant.rb 41 def require_oauth_application_from_jwt_bearer_assertion_subject(assertion) 42 claims, header = jwt_decode_no_key(assertion) 43 44 client_id = claims["sub"] 45 46 case header["alg"] 47 when "none" 48 # do not accept jwts with no alg set 49 authorization_required 50 when /\AHS/ 51 require_oauth_application_from_client_secret_jwt(client_id, assertion, header["alg"]) 52 else 53 require_oauth_application_from_private_key_jwt(client_id, assertion) 54 end 55 end
require_oauth_application_from_none(client_id)
[show source]
# File lib/rodauth/features/oauth_base.rb 394 def require_oauth_application_from_none(client_id) 395 oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => client_id).first 396 authorization_required unless supports_auth_method?(oauth_application, "none") 397 oauth_application 398 end
require_oauth_application_from_private_key_jwt(client_id, assertion)
[show source]
# File lib/rodauth/features/oauth_jwt_bearer_grant.rb 66 def require_oauth_application_from_private_key_jwt(client_id, assertion) 67 oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => client_id).first 68 authorization_required unless oauth_application && supports_auth_method?(oauth_application, "private_key_jwt") 69 jwks = oauth_application_jwks(oauth_application) 70 claims = jwt_assertion(assertion, jwks: jwks) 71 authorization_required unless claims 72 oauth_application 73 end
require_oauth_application_from_saml2_bearer_assertion_issuer(assertion)
[show source]
# File lib/rodauth/features/oauth_saml_bearer_grant.rb 43 def require_oauth_application_from_saml2_bearer_assertion_issuer(assertion) 44 parse_saml_assertion(assertion) 45 46 return unless @saml_settings 47 48 db[oauth_applications_table].where( 49 oauth_applications_id_column => @saml_settings[oauth_saml_settings_oauth_application_id_column] 50 ).first 51 end
require_oauth_application_from_saml2_bearer_assertion_subject(assertion)
[show source]
# File lib/rodauth/features/oauth_saml_bearer_grant.rb 53 def require_oauth_application_from_saml2_bearer_assertion_subject(assertion) 54 parse_saml_assertion(assertion) 55 56 return unless @assertion 57 58 # 3.3.8 - For client authentication, the Subject MUST be the "client_id" of the OAuth client. 59 db[oauth_applications_table].where( 60 oauth_applications_client_id_column => @assertion.nameid 61 ).first 62 end
require_oauth_authorization(*scopes)
[show source]
# File lib/rodauth/features/oauth_base.rb 271 def require_oauth_authorization(*scopes) 272 authorization_required unless authorization_token 273 274 token_scopes = authorization_token[oauth_grants_scopes_column].split(oauth_scope_separator) 275 276 authorization_required unless scopes.any? { |scope| token_scopes.include?(scope) } 277 end
require_signed_request_object?()
[show source]
# File lib/rodauth/features/oauth_jwt_secured_authorization_request.rb 95 def require_signed_request_object? 96 return @require_signed_request_object if defined?(@require_signed_request_object) 97 98 @require_signed_request_object = (oauth_application[oauth_applications_require_signed_request_object_column] if oauth_application) 99 @require_signed_request_object = oauth_require_signed_request_object if @require_signed_request_object.nil? 100 @require_signed_request_object 101 end
requires_backchannel_logout_session?(oauth_application)
[show source]
# File lib/rodauth/features/oidc_backchannel_logout.rb 106 def requires_backchannel_logout_session?(oauth_application) 107 ( 108 oauth_application && 109 oauth_application[oauth_applications_backchannel_logout_session_required_column] 110 ) || backchannel_logout_session_supported 111 end
requires_frontchannel_logout_session?(oauth_application)
[show source]
# File lib/rodauth/features/oidc_frontchannel_logout.rb 120 def requires_frontchannel_logout_session?(oauth_application) 121 ( 122 oauth_application && 123 oauth_application[oauth_applications_frontchannel_logout_session_required_column] 124 ) || frontchannel_logout_session_supported 125 end
rescue_from_uniqueness_error(&block)
[show source]
# File lib/rodauth/features/oauth_base.rb 326 def rescue_from_uniqueness_error(&block) 327 retries = oauth_unique_id_generation_retries 328 begin 329 transaction(savepoint: :only, &block) 330 rescue Sequel::UniqueConstraintViolation 331 redirect_response_error("already_in_use") if retries.zero? 332 retries -= 1 333 retry 334 end 335 end
resource_indicators()
[show source]
# File lib/rodauth/features/oauth_resource_indicators.rb 11 def resource_indicators 12 return @resource_indicators if defined?(@resource_indicators) 13 14 resources = param_or_nil("resource") 15 16 return unless resources 17 18 if json_request? || param_or_nil("request") # signed request 19 resources = Array(resources) 20 else 21 query = if request.form_data? 22 request.body.rewind 23 request.body.read 24 else 25 request.query_string 26 end 27 # resource query param does not conform to rack parsing rules 28 resources = URI.decode_www_form(query).each_with_object([]) do |(k, v), memo| 29 memo << v if k == "resource" 30 end 31 end 32 33 @resource_indicators = resources 34 end
resource_owner_identifier(grant_or_claims)
[show source]
# File lib/rodauth/features/oauth_token_introspection.rb 120 def resource_owner_identifier(grant_or_claims) 121 if (account_id = grant_or_claims[oauth_grants_account_id_column]) 122 account_ds(account_id).select(login_column).first[login_column] 123 elsif (app_id = grant_or_claims[oauth_grants_oauth_application_id_column]) 124 db[oauth_applications_table].where(oauth_applications_id_column => app_id) 125 .select(oauth_applications_name_column) 126 .first[oauth_applications_name_column] 127 elsif (subject = grant_or_claims["sub"]) 128 # JWT 129 if subject == grant_or_claims["client_id"] 130 db[oauth_applications_table].where(oauth_applications_client_id_column => subject) 131 .select(oauth_applications_name_column) 132 .first[oauth_applications_name_column] 133 else 134 account_ds(subject).select(login_column).first[login_column] 135 end 136 end 137 end
resource_owner_params()
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 127 def resource_owner_params 128 { oauth_grants_account_id_column => account_id } 129 end
resource_owner_params_from_jwt_claims(claims)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 70 def resource_owner_params_from_jwt_claims(claims) 71 { oauth_grants_account_id_column => claims["sub"] } 72 end
response_error_params(error_code, message = nil)
[show source]
# File lib/rodauth/features/oauth_base.rb 794 def response_error_params(error_code, message = nil) 795 code = if respond_to?(:"oauth_#{error_code}_error_code") 796 send(:"oauth_#{error_code}_error_code") 797 else 798 error_code 799 end 800 payload = { "error" => code } 801 error_description = message 802 error_description ||= send(:"oauth_#{error_code}_message") if respond_to?(:"oauth_#{error_code}_message") 803 payload["error_description"] = error_description if error_description 804 805 payload 806 end
return_response(body = nil)
[show source]
# File lib/rodauth/features/oauth_base.rb 847 def return_response(body = nil) 848 response.write(body) if body 849 request.halt 850 end
revoke_oauth_grant()
[show source]
# File lib/rodauth/features/oauth_token_revocation.rb 82 def revoke_oauth_grant 83 token = param("token") 84 85 if param("token_type_hint") == "refresh_token" 86 oauth_grant = oauth_grant_by_refresh_token(token) 87 token_column = oauth_grants_refresh_token_column 88 else 89 oauth_grant = oauth_grant_by_token_ds(token).where( 90 oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column] 91 ).first 92 token_column = oauth_grants_token_column 93 end 94 95 redirect_response_error("invalid_request") unless oauth_grant 96 97 redirect_response_error("invalid_request") unless grant_from_application?(oauth_grant, oauth_application) 98 99 update_params = { oauth_grants_revoked_at_column => Sequel::CURRENT_TIMESTAMP } 100 101 ds = db[oauth_grants_table].where(oauth_grants_id_column => oauth_grant[oauth_grants_id_column]) 102 103 oauth_grant = __update_and_return__(ds, update_params) 104 105 oauth_grant[token_column] = token 106 oauth_grant 107 108 # If the particular 109 # token is a refresh token and the authorization server supports the 110 # revocation of access tokens, then the authorization server SHOULD 111 # also invalidate all access tokens based on the same authorization 112 # grant 113 # 114 # we don't need to do anything here, as we revalidate existing tokens 115 end
scopes()
[show source]
# File lib/rodauth/features/oauth_base.rb 202 def scopes 203 scope = request.params["scope"] 204 case scope 205 when Array 206 scope 207 when String 208 scope.split(" ") 209 end 210 end
secret_hash(secret)
[show source]
# File lib/rodauth/features/oauth_base.rb 442 def secret_hash(secret) 443 password_hash(secret) 444 end
secret_matches?(oauth_application, secret)
[show source]
# File lib/rodauth/features/oauth_base.rb 426 def secret_matches?(oauth_application, secret) 427 if oauth_applications_client_secret_hash_column 428 BCrypt::Password.new(oauth_application[oauth_applications_client_secret_hash_column]) == secret 429 else 430 oauth_application[oauth_applications_client_secret_column] == secret 431 end 432 end
session_id_in_claims(oauth_grant, claims)
[show source]
# File lib/rodauth/features/oidc_logout_base.rb 24 def session_id_in_claims(oauth_grant, claims) 25 oauth_application_in_visited_sites do 26 if should_set_sid_in_visited_sites?(oauth_application) 27 # id_token or token response types 28 session_id = if (sess = session[session_id_session_key]) 29 compute_hmac(sess) 30 else 31 # code response type 32 ds = db[active_sessions_table] 33 ds = ds.where(active_sessions_account_id_column => oauth_grant[oauth_grants_account_id_column]) 34 ds = ds.order(Sequel.desc(active_sessions_last_use_column)) 35 ds.get(active_sessions_session_id_column) 36 end 37 38 claims[:sid] = session_id 39 end 40 end 41 end
set_client_secret(params, secret)
[show source]
# File lib/rodauth/features/oauth_base.rb 434 def set_client_secret(params, secret) 435 if oauth_applications_client_secret_hash_column 436 params[oauth_applications_client_secret_hash_column] = secret_hash(secret) 437 else 438 params[oauth_applications_client_secret_column] = secret 439 end 440 end
should_set_oauth_application_in_visited_sites?()
[show source]
# File lib/rodauth/features/oidc_backchannel_logout.rb 98 def should_set_oauth_application_in_visited_sites? 99 true 100 end
should_set_sid_in_visited_sites?(oauth_application)
[show source]
# File lib/rodauth/features/oidc_backchannel_logout.rb 102 def should_set_sid_in_visited_sites?(oauth_application) 103 super || requires_backchannel_logout_session?(oauth_application) 104 end
sid_in_visited_sites()
[show source]
# File lib/rodauth/features/oidc_logout_base.rb 56 def sid_in_visited_sites 57 return unless should_set_oauth_application_in_visited_sites? 58 59 oauth_application_in_visited_sites do 60 if should_set_sid_in_visited_sites?(oauth_application) 61 ds = active_sessions_ds.order(Sequel.desc(active_sessions_last_use_column)) 62 63 ds.get(active_sessions_session_id_column) 64 end 65 end 66 end
store_token(grant_params, update_params = {})
[show source]
# File lib/rodauth/features/oauth_base.rb 526 def store_token(grant_params, update_params = {}) 527 ds = db[oauth_grants_table] 528 529 if __one_oauth_token_per_account 530 531 to_update_if_null = [ 532 oauth_grants_token_column, 533 oauth_grants_token_hash_column, 534 oauth_grants_refresh_token_column, 535 oauth_grants_refresh_token_hash_column 536 ].compact.map do |attribute| 537 [ 538 attribute, 539 ( 540 if ds.respond_to?(:supports_insert_conflict?) && ds.supports_insert_conflict? 541 Sequel.function(:coalesce, Sequel[oauth_grants_table][attribute], Sequel[:excluded][attribute]) 542 else 543 Sequel.function(:coalesce, Sequel[oauth_grants_table][attribute], update_params[attribute]) 544 end 545 ) 546 ] 547 end 548 549 token = __insert_or_update_and_return__( 550 ds, 551 oauth_grants_id_column, 552 oauth_grants_unique_columns, 553 grant_params.merge(update_params), 554 Sequel.expr(Sequel[oauth_grants_table][oauth_grants_expires_in_column]) > Sequel::CURRENT_TIMESTAMP, 555 Hash[to_update_if_null] 556 ) 557 558 # if the previous operation didn't return a row, it means that the conditions 559 # invalidated the update, and the existing token is still valid. 560 token || ds.where( 561 oauth_grants_account_id_column => update_params[oauth_grants_account_id_column], 562 oauth_grants_oauth_application_id_column => update_params[oauth_grants_oauth_application_id_column] 563 ).first 564 else 565 566 if oauth_reuse_access_token 567 unique_conds = Hash[oauth_grants_unique_columns.map { |column| [column, update_params[column]] }] 568 valid_token_ds = valid_oauth_grant_ds(unique_conds) 569 if oauth_grants_token_hash_column 570 valid_token_ds.exclude(oauth_grants_token_hash_column => nil) 571 else 572 valid_token_ds.exclude(oauth_grants_token_column => nil) 573 end 574 575 valid_token = valid_token_ds.first 576 577 return valid_token if valid_token 578 end 579 580 if grant_params[oauth_grants_id_column] 581 __update_and_return__(ds.where(oauth_grants_id_column => grant_params[oauth_grants_id_column]), update_params) 582 else 583 __insert_and_return__(ds, oauth_grants_id_column, grant_params.merge(update_params)) 584 end 585 end 586 end
supported_grant_type?(grant_type, expected_grant_type = grant_type)
[show source]
# File lib/rodauth/features/oauth_base.rb 708 def supported_grant_type?(grant_type, expected_grant_type = grant_type) 709 return false unless grant_type == expected_grant_type 710 711 grant_types_supported = if oauth_application[oauth_applications_grant_types_column] 712 oauth_application[oauth_applications_grant_types_column].split(/ +/) 713 else 714 oauth_grant_types_supported 715 end 716 717 grant_types_supported.include?(grant_type) 718 end
supported_request_uri?(request_uri, oauth_application)
[show source]
# File lib/rodauth/features/oauth_jwt_secured_authorization_request.rb 87 def supported_request_uri?(request_uri, oauth_application) 88 return false unless check_valid_uri?(request_uri) 89 90 request_uris = oauth_application[oauth_applications_request_uris_column] 91 92 request_uris.nil? || request_uris.split(oauth_scope_separator).one? { |uri| request_uri.start_with?(uri) } 93 end
supported_response_mode?(response_mode, expected_response_mode = response_mode)
[show source]
# File lib/rodauth/features/oauth_base.rb 734 def supported_response_mode?(response_mode, expected_response_mode = response_mode) 735 return false unless response_mode == expected_response_mode 736 737 response_modes_supported = if oauth_application[oauth_applications_response_modes_column] 738 oauth_application[oauth_applications_response_modes_column].split(/ +/) 739 else 740 oauth_response_modes_supported 741 end 742 743 response_modes_supported.include?(response_mode) 744 end
supported_response_type?(response_type, expected_response_type = response_type)
[show source]
# File lib/rodauth/features/oauth_base.rb 720 def supported_response_type?(response_type, expected_response_type = response_type) 721 return false unless response_type == expected_response_type 722 723 response_types_supported = if oauth_application[oauth_applications_response_types_column] 724 oauth_application[oauth_applications_response_types_column].split(/ +/) 725 else 726 oauth_response_types_supported 727 end 728 729 response_types = response_type.split(/ +/) 730 731 (response_types - response_types_supported).empty? 732 end
supports_auth_method?(oauth_application, auth_method)
[show source]
# File lib/rodauth/features/oauth_base.rb 400 def supports_auth_method?(oauth_application, auth_method) 401 return false unless oauth_application 402 403 supported_auth_methods = if oauth_application[oauth_applications_token_endpoint_auth_method_column] 404 oauth_application[oauth_applications_token_endpoint_auth_method_column].split(/ +/) 405 else 406 oauth_token_endpoint_auth_methods_supported 407 end 408 409 supported_auth_methods.include?(auth_method) 410 end
template_path(page)
[show source]
# File lib/rodauth/features/oauth_base.rb 350 def template_path(page) 351 path = File.join(File.dirname(__FILE__), "../../../templates", "#{page}.str") 352 return super unless File.exist?(path) 353 354 path 355 end
throw_json_response_error(status, error_code, message = nil)
[show source]
# File lib/rodauth/features/oauth_base.rb 823 def throw_json_response_error(status, error_code, message = nil) 824 set_response_error_status(status) 825 payload = response_error_params(error_code, message) 826 json_payload = _json_response_body(payload) 827 response["Content-Type"] ||= json_response_content_type 828 response["WWW-Authenticate"] = www_authenticate_header(payload) if status == 401 829 return_response(json_payload) 830 end
translate(key, default, args = EMPTY_HASH)
override
[show source]
# File lib/rodauth/features/oauth_base.rb 284 def translate(key, default, args = EMPTY_HASH) 285 return i18n_translate(key, default, **args) if features.include?(:i18n) 286 # do not attempt to translate by default 287 return default if args.nil? 288 289 default % args 290 end
try_acr_values()
[show source]
# File lib/rodauth/features/oidc.rb 446 def try_acr_values 447 return unless (acr_values = param_or_nil("acr_values")) 448 449 acr_values.split(" ").each do |acr_value| 450 next unless oauth_acr_values_supported.include?(acr_value) 451 452 case acr_value 453 when "phr" 454 return acr_value if require_acr_value_phr 455 when "phrh" 456 return acr_value if require_acr_value_phrh 457 else 458 return acr_value if require_acr_value(acr_value) 459 end 460 end 461 462 nil 463 end
try_approval_prompt()
[show source]
# File lib/rodauth/features/oauth_authorize_base.rb 135 def try_approval_prompt 136 approval_prompt = param_or_nil("approval_prompt") 137 138 return unless approval_prompt && approval_prompt == "auto" 139 140 return if db[oauth_grants_table].where(resource_owner_params).where( 141 oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column], 142 oauth_grants_redirect_uri_column => redirect_uri, 143 oauth_grants_scopes_column => scopes.join(oauth_scope_separator), 144 oauth_grants_access_type_column => "online" 145 ).none? 146 147 # if there's a previous oauth grant for the params combo, it means that this user has approved before. 148 request.env["REQUEST_METHOD"] = "POST" 149 end
try_prompt()
this executes before checking for a logged in account
[show source]
# File lib/rodauth/features/oidc.rb 393 def try_prompt 394 return unless (prompt = param_or_nil("prompt")) 395 396 case prompt 397 when "none" 398 return unless request.get? 399 400 redirect_response_error("login_required") unless logged_in? 401 402 require_account 403 404 redirect_response_error("interaction_required") unless oidc_authorize_on_prompt_none?(account_from_session) 405 406 request.env["REQUEST_METHOD"] = "POST" 407 when "login" 408 return unless request.get? 409 410 if logged_in? && request.cookies[oauth_prompt_login_cookie_key] == "login" 411 ::Rack::Utils.delete_cookie_header!(response.headers, oauth_prompt_login_cookie_key, oauth_prompt_login_cookie_options) 412 return 413 end 414 415 # logging out 416 clear_session 417 set_session_value(login_redirect_session_key, request.fullpath) 418 419 login_cookie_opts = Hash[oauth_prompt_login_cookie_options] 420 login_cookie_opts[:value] = "login" 421 if oauth_prompt_login_interval 422 login_cookie_opts[:expires] = convert_timestamp(Time.now + oauth_prompt_login_interval) # 15 minutes 423 end 424 ::Rack::Utils.set_cookie_header!(response.headers, oauth_prompt_login_cookie_key, login_cookie_opts) 425 426 redirect require_login_redirect 427 when "consent" 428 return unless request.post? 429 430 require_account 431 432 sc = scopes || [] 433 434 redirect_response_error("consent_required") if sc.empty? 435 436 when "select-account" 437 return unless request.get? 438 439 # only works if select_account plugin is available 440 require_select_account if respond_to?(:require_select_account) 441 else 442 redirect_response_error("invalid_request") 443 end 444 end
use_date_arithmetic?()
[show source]
# File lib/rodauth/features/oauth_base.rb 279 def use_date_arithmetic? 280 true 281 end
userinfo_encryption_alg_values_supported()
[show source]
# File lib/rodauth/features/oidc.rb 255 def userinfo_encryption_alg_values_supported 256 oauth_jwt_jwe_algorithms_supported 257 end
userinfo_encryption_enc_values_supported()
[show source]
# File lib/rodauth/features/oidc.rb 259 def userinfo_encryption_enc_values_supported 260 oauth_jwt_jwe_encryption_methods_supported 261 end
userinfo_signing_alg_values_supported()
[show source]
# File lib/rodauth/features/oidc.rb 251 def userinfo_signing_alg_values_supported 252 oauth_jwt_jws_algorithms_supported 253 end
valid_dpop_nonce?(nonce)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 372 def valid_dpop_nonce?(nonce) 373 nonce_claims = jwt_decode(nonce, verify_aud: false, verify_jti: false) 374 375 return false unless nonce_claims 376 377 jti = nonce_claims["jti"] 378 379 return false unless jti 380 381 return false unless jti == Digest::SHA256.hexdigest("#{request.request_method}:#{request.url}:#{nonce_claims['iat']}") 382 383 return false unless nonce_claims.key?("aud") 384 385 htm, htu = nonce_claims["aud"].split(":", 2) 386 387 htm == request.request_method && htu == request.url 388 end
valid_dpop_proof_required(error_code = "invalid_dpop_proof")
[show source]
# File lib/rodauth/features/oauth_dpop.rb 313 def valid_dpop_proof_required(error_code = "invalid_dpop_proof") 314 if @dpop_access_token 315 # protected resource access 316 throw_json_response_error(401, error_code) 317 else 318 redirect_response_error(error_code) 319 end 320 end
valid_locked_oauth_grant(grant_params = nil)
[show source]
# File lib/rodauth/features/oauth_base.rb 588 def valid_locked_oauth_grant(grant_params = nil) 589 oauth_grant = valid_oauth_grant_ds(grant_params).for_update.first 590 591 redirect_response_error("invalid_grant") unless oauth_grant 592 593 oauth_grant 594 end
valid_oauth_grant_ds(grant_params = nil)
[show source]
# File lib/rodauth/features/oauth_base.rb 596 def valid_oauth_grant_ds(grant_params = nil) 597 ds = db[oauth_grants_table] 598 .where(Sequel[oauth_grants_table][oauth_grants_revoked_at_column] => nil) 599 .where(Sequel.expr(Sequel[oauth_grants_table][oauth_grants_expires_in_column]) >= Sequel::CURRENT_TIMESTAMP) 600 ds = ds.where(grant_params) if grant_params 601 602 ds 603 end
validate_ath(claims, access_token)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 210 def validate_ath(claims, access_token) 211 # When the DPoP proof is used in conjunction with the presentation of an access token in protected resource access 212 # the DPoP proof MUST also contain the following claim 213 ath = claims["ath"] 214 215 redirect_response_error("invalid_token") unless ath 216 217 # The value MUST be the result of a base64url encoding of the SHA-256 hash of the ASCII encoding of 218 # the associated access token's value. 219 redirect_response_error("invalid_token") unless ath == Base64.urlsafe_encode64(Digest::SHA256.digest(access_token), padding: false) 220 end
validate_authorize_params()
[show source]
# File lib/rodauth/features/oauth_authorization_code_grant.rb 25 def validate_authorize_params 26 super 27 28 response_mode = param_or_nil("response_mode") 29 30 return unless response_mode 31 32 redirect_response_error("invalid_request") unless oauth_response_modes_supported.include?(response_mode) 33 34 response_type = param_or_nil("response_type") 35 36 return unless response_type.nil? || response_type == "code" 37 38 redirect_response_error("invalid_request") unless oauth_response_modes_for_code_supported.include?(response_mode) 39 end
validate_client_registration_params(request_params = request.params)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 121 def validate_client_registration_params(request_params = request.params) 122 @oauth_application_params = request_params.each_with_object({}) do |(key, value), params| 123 case key 124 when "redirect_uris" 125 if value.is_a?(Array) 126 value = value.each do |uri| 127 unless check_valid_no_fragment_uri?(uri) 128 register_throw_json_response_error("invalid_redirect_uri", 129 register_invalid_uri_message(uri)) 130 end 131 end.join(" ") 132 else 133 register_throw_json_response_error("invalid_redirect_uri", register_invalid_uri_message(value)) 134 end 135 key = oauth_applications_redirect_uri_column 136 when "token_endpoint_auth_method" 137 unless oauth_token_endpoint_auth_methods_supported.include?(value) 138 register_throw_json_response_error("invalid_client_metadata", register_invalid_client_metadata_message(key, value)) 139 end 140 # verify if in range 141 key = oauth_applications_token_endpoint_auth_method_column 142 when "grant_types" 143 if value.is_a?(Array) 144 value = value.each do |grant_type| 145 unless oauth_grant_types_supported.include?(grant_type) 146 register_throw_json_response_error("invalid_client_metadata", register_invalid_client_metadata_message(grant_type, value)) 147 end 148 end.join(" ") 149 else 150 register_throw_json_response_error("invalid_client_metadata", register_invalid_client_metadata_message(key, value)) 151 end 152 key = oauth_applications_grant_types_column 153 when "response_types" 154 if value.is_a?(Array) 155 grant_types = request_params["grant_types"] || %w[authorization_code] 156 value = value.each do |response_type| 157 unless oauth_response_types_supported.include?(response_type) 158 register_throw_json_response_error("invalid_client_metadata", 159 register_invalid_response_type_message(response_type)) 160 end 161 162 validate_client_registration_response_type(response_type, grant_types) 163 end.join(" ") 164 else 165 register_throw_json_response_error("invalid_client_metadata", register_invalid_client_metadata_message(key, value)) 166 end 167 key = oauth_applications_response_types_column 168 # verify if in range and match grant type 169 when "client_uri", "logo_uri", "tos_uri", "policy_uri", "jwks_uri" 170 register_throw_json_response_error("invalid_client_metadata", register_invalid_uri_message(value)) unless check_valid_uri?(value) 171 case key 172 when "client_uri" 173 key = oauth_applications_homepage_url_column 174 when "jwks_uri" 175 if request_params.key?("jwks") 176 register_throw_json_response_error("invalid_client_metadata", 177 register_invalid_jwks_param_message(key, "jwks")) 178 end 179 end 180 key = __send__(:"oauth_applications_#{key}_column") 181 when "jwks" 182 register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message(value)) unless value.is_a?(Hash) 183 if request_params.key?("jwks_uri") 184 register_throw_json_response_error("invalid_client_metadata", 185 register_invalid_jwks_param_message(key, "jwks_uri")) 186 end 187 188 key = oauth_applications_jwks_column 189 value = JSON.dump(value) 190 when "scope" 191 register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message(value)) unless value.is_a?(String) 192 scopes = value.split(" ") - oauth_application_scopes 193 register_throw_json_response_error("invalid_client_metadata", register_invalid_scopes_message(value)) unless scopes.empty? 194 key = oauth_applications_scopes_column 195 # verify if in range 196 when "contacts" 197 register_throw_json_response_error("invalid_client_metadata", register_invalid_contacts_message(value)) unless value.is_a?(Array) 198 value = value.join(" ") 199 key = oauth_applications_contacts_column 200 when "client_name" 201 register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message(value)) unless value.is_a?(String) 202 key = oauth_applications_name_column 203 when "dpop_bound_access_tokens" 204 unless respond_to?(:oauth_applications_dpop_bound_access_tokens_column) 205 register_throw_json_response_error("invalid_client_metadata", 206 register_invalid_param_message(key)) 207 end 208 request_params[key] = value = convert_to_boolean(key, value) 209 210 key = oauth_applications_dpop_bound_access_tokens_column 211 when "require_signed_request_object" 212 unless respond_to?(:oauth_applications_require_signed_request_object_column) 213 register_throw_json_response_error("invalid_client_metadata", 214 register_invalid_param_message(key)) 215 end 216 request_params[key] = value = convert_to_boolean(key, value) 217 218 key = oauth_applications_require_signed_request_object_column 219 when "require_pushed_authorization_requests" 220 unless respond_to?(:oauth_applications_require_pushed_authorization_requests_column) 221 register_throw_json_response_error("invalid_client_metadata", 222 register_invalid_param_message(key)) 223 end 224 request_params[key] = value = convert_to_boolean(key, value) 225 226 key = oauth_applications_require_pushed_authorization_requests_column 227 when "tls_client_certificate_bound_access_tokens" 228 property = :oauth_applications_tls_client_certificate_bound_access_tokens_column 229 register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message(key)) unless respond_to?(property) 230 231 request_params[key] = value = convert_to_boolean(key, value) 232 233 key = oauth_applications_tls_client_certificate_bound_access_tokens_column 234 when /\Atls_client_auth_/ 235 unless respond_to?(:"oauth_applications_#{key}_column") 236 register_throw_json_response_error("invalid_client_metadata", 237 register_invalid_param_message(key)) 238 end 239 240 # client using the tls_client_auth authentication method MUST use exactly one of the below metadata 241 # parameters to indicate the certificate subject value that the authorization server is to expect when 242 # authenticating the respective client. 243 if params.any? { |k, _| k.to_s.start_with?("tls_client_auth_") } 244 register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message(key)) 245 end 246 247 key = __send__(:"oauth_applications_#{key}_column") 248 else 249 if respond_to?(:"oauth_applications_#{key}_column") 250 if PROTECTED_APPLICATION_ATTRIBUTES.include?(key) 251 register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message(key)) 252 end 253 property = :"oauth_applications_#{key}_column" 254 key = __send__(property) 255 elsif !db[oauth_applications_table].columns.include?(key.to_sym) 256 register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message(key)) 257 end 258 end 259 params[key] = value 260 end 261 end
validate_client_registration_response_type(response_type, grant_types)
[show source]
# File lib/rodauth/features/oauth_dynamic_client_registration.rb 263 def validate_client_registration_response_type(response_type, grant_types) 264 case response_type 265 when "code" 266 unless grant_types.include?("authorization_code") 267 register_throw_json_response_error("invalid_client_metadata", 268 register_invalid_response_type_for_grant_type_message(response_type, 269 "authorization_code")) 270 end 271 when "token" 272 unless grant_types.include?("implicit") 273 register_throw_json_response_error("invalid_client_metadata", 274 register_invalid_response_type_for_grant_type_message(response_type, "implicit")) 275 end 276 when "none" 277 if grant_types.include?("implicit") || grant_types.include?("authorization_code") 278 register_throw_json_response_error("invalid_client_metadata", register_invalid_response_type_message(response_type)) 279 end 280 end 281 end
validate_dpop_jwt_claims(claims)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 192 def validate_dpop_jwt_claims(claims) 193 jti = claims["jti"] 194 195 unless jti && jti == Digest::SHA256.hexdigest("#{request.request_method}:#{request.url}:#{claims['iat']}") 196 redirect_response_error("invalid_dpop_jti") 197 end 198 199 htm = claims["htm"] 200 201 # 4.3.8 - Check if htm matches the request method 202 redirect_response_error("invalid_dpop_htm") unless htm && htm == request.request_method 203 204 htu = claims["htu"] 205 206 # 4.3.9 - Check if htu matches the request URL 207 redirect_response_error("invalid_dpop_htu") unless htu && htu == request.url 208 end
validate_dpop_proof_usage(claims)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 137 def validate_dpop_proof_usage(claims) 138 jti = claims["jti"] 139 140 dpop_proof = __insert_or_do_nothing_and_return__( 141 db[oauth_dpop_proofs_table], 142 oauth_dpop_proofs_jti_column, 143 [oauth_dpop_proofs_jti_column], 144 oauth_dpop_proofs_jti_column => Digest::SHA256.hexdigest(jti), 145 oauth_dpop_proofs_first_use_column => Sequel::CURRENT_TIMESTAMP 146 ) 147 148 return unless (Time.now - dpop_proof[oauth_dpop_proofs_first_use_column]) > oauth_dpop_proof_expires_in 149 150 redirect_response_error("invalid_dpop_proof") 151 end
validate_dpop_token(dpop)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 118 def validate_dpop_token(dpop) 119 # 4.3.2 120 @dpop_claims = dpop_decode(dpop) 121 redirect_response_error("invalid_dpop_proof") unless @dpop_claims 122 123 validate_dpop_jwt_claims(@dpop_claims) 124 125 # 4.3.10 126 validate_nonce(@dpop_claims) 127 128 # 11.1 129 # To prevent multiple uses of the same DPoP proof, servers can store, in the 130 # context of the target URI, the jti value of each DPoP proof for the time window 131 # in which the respective DPoP proof JWT would be accepted. 132 validate_dpop_proof_usage(@dpop_claims) 133 134 @dpop_claims 135 end
validate_introspect_params(token_hint_types = %w[access_token refresh_token].freeze)
Token introspect
[show source]
# File lib/rodauth/features/oauth_token_introspection.rb 50 def validate_introspect_params(token_hint_types = %w[access_token refresh_token].freeze) 51 # check if valid token hint type 52 if param_or_nil("token_type_hint") && !token_hint_types.include?(param("token_type_hint")) 53 redirect_response_error("unsupported_token_type") 54 end 55 56 redirect_response_error("invalid_request") unless param_or_nil("token") 57 end
validate_nonce(claims)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 222 def validate_nonce(claims) 223 nonce = claims["nonce"] 224 225 unless nonce 226 dpop_nonce_required(claims) if dpop_use_nonce? 227 228 return 229 end 230 231 dpop_nonce_required(claims) unless valid_dpop_nonce?(nonce) 232 end
validate_oauth_application_params()
[show source]
# File lib/rodauth/features/oauth_application_management.rb 156 def validate_oauth_application_params 157 oauth_application_params.each do |key, value| 158 if key == oauth_application_homepage_url_param 159 160 set_field_error(key, invalid_url_message) unless check_valid_uri?(value) 161 162 elsif key == oauth_application_redirect_uri_param 163 164 if value.respond_to?(:each) 165 value.each do |uri| 166 next if uri.empty? 167 168 set_field_error(key, invalid_url_message) unless check_valid_no_fragment_uri?(uri) 169 end 170 else 171 set_field_error(key, invalid_url_message) unless check_valid_no_fragment_uri?(value) 172 end 173 elsif key == oauth_application_scopes_param 174 175 value.each do |scope| 176 set_field_error(key, oauth_invalid_scope_message) unless oauth_application_scopes.include?(scope) 177 end 178 end 179 end 180 181 throw :rodauth_error if @field_errors && !@field_errors.empty? 182 end
validate_oidc_logout_params()
Logout
[show source]
# File lib/rodauth/features/oidc_rp_initiated_logout.rb 120 def validate_oidc_logout_params 121 # check if valid token hint type 122 return unless (redirect_uri = param_or_nil("post_logout_redirect_uri")) 123 124 return if check_valid_no_fragment_uri?(redirect_uri) 125 126 redirect_logout_with_error(oauth_invalid_client_message) 127 end
validate_par_params()
[show source]
# File lib/rodauth/features/oauth_dpop.rb 104 def validate_par_params 105 super 106 107 return unless (dpop = fetch_dpop_token) 108 109 validate_dpop_token(dpop) 110 111 if (dpop_jkt = param_or_nil("dpop_jkt")) 112 redirect_response_error("invalid_request") if dpop_jkt != @dpop_thumbprint 113 else 114 request.params["dpop_jkt"] = @dpop_thumbprint 115 end 116 end
validate_pkce_challenge_params()
[show source]
# File lib/rodauth/features/oauth_pkce.rb 60 def validate_pkce_challenge_params 61 if param_or_nil("code_challenge") 62 63 challenge_method = param_or_nil("code_challenge_method") 64 redirect_response_error("code_challenge_required") unless oauth_pkce_challenge_method == challenge_method 65 else 66 return unless oauth_require_pkce 67 68 redirect_response_error("code_challenge_required") 69 end 70 end
validate_revoke_params(token_hint_types = %w[access_token refresh_token].freeze)
[show source]
# File lib/rodauth/features/oauth_token_revocation.rb 57 def validate_revoke_params(token_hint_types = %w[access_token refresh_token].freeze) 58 token_hint = param_or_nil("token_type_hint") 59 60 if features.include?(:oauth_jwt) && oauth_jwt_access_tokens && (!token_hint || token_hint == "access_token") 61 # JWT access tokens can't be revoked 62 throw(:rodauth_error) 63 end 64 65 # check if valid token hint type 66 redirect_response_error("unsupported_token_type") if token_hint && !token_hint_types.include?(token_hint) 67 68 redirect_response_error("invalid_request") unless param_or_nil("token") 69 end
validate_token_params()
[show source]
# File lib/rodauth/features/oauth_assertion_base.rb 18 def validate_token_params 19 return super unless assertion_grant_type? 20 21 redirect_response_error("invalid_grant") unless param_or_nil("assertion") 22 end
verify_access_token_headers(headers)
[show source]
# File lib/rodauth/features/oauth_jwt.rb 59 def verify_access_token_headers(headers) 60 headers["typ"] == "at+jwt" 61 end
verify_aud(expected_aud, aud)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 111 def verify_aud(expected_aud, aud) 112 expected_aud == aud 113 end
verify_dpop_jwt_headers(headers)
[show source]
# File lib/rodauth/features/oauth_dpop.rb 171 def verify_dpop_jwt_headers(headers) 172 # 4.3.4 - A field with the value dpop+jwt 173 return false unless headers["typ"] == "dpop+jwt" 174 175 # 4.3.5 - It MUST NOT be none or an identifier for a symmetric algorithm 176 alg = headers["alg"] 177 return false unless alg && oauth_dpop_signing_alg_values_supported.include?(alg) 178 179 dpop_jwk = headers["jwk"] 180 181 return false unless dpop_jwk 182 183 # 4.3.7 - It MUST NOT contain a private key. 184 return false if private_jwk?(dpop_jwk) 185 186 # store thumbprint for future assertions 187 @dpop_thumbprint = jwk_thumbprint(dpop_jwk) 188 189 true 190 end
verify_jti(jti, claims)
[show source]
# File lib/rodauth/features/oauth_jwt_base.rb 107 def verify_jti(jti, claims) 108 generate_jti(claims) == jti 109 end
www_authenticate_header(*)
[show source]
# File lib/rodauth/features/oauth_base.rb 832 def www_authenticate_header(*) 833 oauth_token_type.capitalize 834 end