lib/rodauth/features/oauth_base.rb in rodauth-oauth-0.8.0 vs lib/rodauth/features/oauth_base.rb in rodauth-oauth-0.9.0

- old
+ new

@@ -27,10 +27,11 @@ auth_value_method :oauth_token_expires_in, 60 * 60 # 60 minutes auth_value_method :oauth_refresh_token_expires_in, 60 * 60 * 24 * 360 # 1 year auth_value_method :oauth_unique_id_generation_retries, 3 auth_value_method :oauth_response_mode, "query" + auth_value_method :oauth_auth_methods_supported, %w[client_secret_basic client_secret_post] auth_value_method :oauth_scope_separator, " " auth_value_method :oauth_tokens_table, :oauth_tokens auth_value_method :oauth_tokens_id_column, :id @@ -56,10 +57,13 @@ %i[ account_id name description scopes client_id client_secret homepage_url redirect_uri + token_endpoint_auth_method grant_types response_types + logo_uri tos_uri policy_uri jwks jwks_uri + contacts software_id software_version ].each do |column| auth_value_method :"oauth_applications_#{column}_column", column end auth_value_method :authorization_required_error_status, 401 @@ -329,32 +333,51 @@ # fetch an authorization basic header # parse client id and secret # def require_oauth_application # get client credentials + auth_method = nil client_id = client_secret = nil if (token = ((v = request.env["HTTP_AUTHORIZATION"]) && v[/\A *Basic (.*)\Z/, 1])) # client_secret_basic client_id, client_secret = Base64.decode64(token).split(/:/, 2) + auth_method = "client_secret_basic" else # client_secret_post client_id = param_or_nil("client_id") client_secret = param_or_nil("client_secret") + auth_method = "client_secret_post" if client_secret end authorization_required unless client_id @oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => client_id).first - authorization_required unless authorized_oauth_application?(@oauth_application, client_secret) + authorization_required unless @oauth_application + + authorization_required unless authorized_oauth_application?(@oauth_application, client_secret, auth_method) end - def authorized_oauth_application?(oauth_application, client_secret) - oauth_application && secret_matches?(oauth_application, client_secret) + def authorized_oauth_application?(oauth_application, client_secret, auth_method) + supported_auth_methods = if oauth_application[oauth_applications_token_endpoint_auth_method_column] + oauth_application[oauth_applications_token_endpoint_auth_method_column].split(/ +/) + else + oauth_auth_methods_supported + end + + if auth_method + supported_auth_methods.include?(auth_method) && secret_matches?(oauth_application, client_secret) + else + supported_auth_methods.include?("none") + end end + def no_auth_oauth_application?(_oauth_application) + supported_auth_methods.include?("none") + end + def require_oauth_application_from_account ds = db[oauth_applications_table] .join(oauth_tokens_table, Sequel[oauth_tokens_table][oauth_tokens_oauth_application_id_column] => Sequel[oauth_applications_table][oauth_applications_id_column]) .where(oauth_token_by_token_ds(param("token")).opts.fetch(:where, true)) @@ -513,12 +536,11 @@ redirect_response_error("invalid_request") if grant_type == "refresh_token" && !param_or_nil("refresh_token") end def create_oauth_token(grant_type) - case grant_type - when "refresh_token" + if supported_grant_type?(grant_type, "refresh_token") # fetch potentially revoked oauth token oauth_token = oauth_token_by_refresh_token(param("refresh_token"), revoked: true) if !oauth_token redirect_response_error("invalid_grant") @@ -592,22 +614,32 @@ oauth_token[oauth_tokens_refresh_token_column] = refresh_token if refresh_token oauth_token end end - def oauth_server_metadata_body(path) + def supported_grant_type?(grant_type, expected_grant_type = grant_type) + return false unless grant_type == expected_grant_type + + return true unless (grant_types_supported = oauth_application[oauth_applications_grant_types_column]) + + grant_types_supported = grant_types_supported.split(/ +/) + + grant_types_supported.include?(grant_type) + end + + def oauth_server_metadata_body(path = nil) issuer = base_url issuer += "/#{path}" if path { issuer: issuer, token_endpoint: token_url, scopes_supported: oauth_application_scopes, response_types_supported: [], response_modes_supported: [], - grant_types_supported: [], - token_endpoint_auth_methods_supported: %w[client_secret_basic client_secret_post], + grant_types_supported: %w[refresh_token], + token_endpoint_auth_methods_supported: oauth_auth_methods_supported, service_documentation: oauth_metadata_service_documentation, ui_locales_supported: oauth_metadata_ui_locales_supported, op_policy_uri: oauth_metadata_op_policy_uri, op_tos_uri: oauth_metadata_op_tos_uri } @@ -657,19 +689,19 @@ json_payload = _json_response_body(body) response.write(json_payload) request.halt end - def throw_json_response_error(status, error_code) + def throw_json_response_error(status, error_code, message = nil) set_response_error_status(status) code = if respond_to?(:"#{error_code}_error_code") send(:"#{error_code}_error_code") else error_code end payload = { "error" => code } - payload["error_description"] = send(:"#{error_code}_message") if respond_to?(:"#{error_code}_message") + payload["error_description"] = message || (send(:"#{error_code}_message") if respond_to?(:"#{error_code}_message")) json_payload = _json_response_body(payload) response["Content-Type"] ||= json_response_content_type response["WWW-Authenticate"] = oauth_token_type.upcase if status == 401 response.write(json_payload) request.halt @@ -696,9 +728,13 @@ def check_valid_scopes? return false unless scopes (scopes - oauth_application[oauth_applications_scopes_column].split(oauth_scope_separator)).empty? + end + + def check_valid_uri?(uri) + URI::DEFAULT_PARSER.make_regexp(oauth_valid_uri_schemes).match?(uri) end # Resource server mode SERVER_METADATA = OAuth::TtlStore.new