lib/rodauth/features/oauth_authorize_base.rb in rodauth-oauth-0.10.4 vs lib/rodauth/features/oauth_authorize_base.rb in rodauth-oauth-1.0.0.pre.beta1

- old
+ new

@@ -1,7 +1,9 @@ # frozen_string_literal: true +require "rodauth/oauth" + module Rodauth Feature.define(:oauth_authorize_base, :OauthAuthorizeBase) do depends :oauth_base before "authorize" @@ -10,21 +12,24 @@ view "authorize", "Authorize", "authorize" button "Authorize", "oauth_authorize" button "Back to Client Application", "oauth_authorize_post" - translatable_method :oauth_tokens_scopes_label, "Scopes" + auth_value_method :use_oauth_access_type?, false + + auth_value_method :oauth_grants_access_type_column, :access_type + + translatable_method :authorize_page_lead, "The application %<name>s would like to access your data" + translatable_method :oauth_grants_scopes_label, "Scopes" translatable_method :oauth_applications_contacts_label, "Contacts" translatable_method :oauth_applications_tos_uri_label, "Terms of service URL" translatable_method :oauth_applications_policy_uri_label, "Policy URL" # /authorize - route(:authorize) do |r| - next unless is_authorization_server? - - before_authorize_route + auth_server_route(:authorize) do |r| require_authorizable_account + before_authorize_route validate_authorize_params r.get do authorize_view @@ -47,63 +52,141 @@ else super end end + def authorize_scopes + scopes || begin + oauth_application[oauth_applications_scopes_column].split(oauth_scope_separator) + end + end + private def validate_authorize_params redirect_response_error("invalid_request", request.referer || default_redirect) unless oauth_application && check_valid_redirect_uri? redirect_response_error("invalid_request") unless check_valid_response_type? - redirect_response_error("invalid_scope") unless check_valid_scopes? + redirect_response_error("invalid_request") unless check_valid_access_type? && check_valid_approval_prompt? + + try_approval_prompt if use_oauth_access_type? && request.get? + + redirect_response_error("invalid_scope") if (request.post? || param_or_nil("scope")) && !check_valid_scopes? end def check_valid_response_type? false end def check_valid_redirect_uri? oauth_application[oauth_applications_redirect_uri_column].split(" ").include?(redirect_uri) end + ACCESS_TYPES = %w[offline online].freeze + + def check_valid_access_type? + return true unless use_oauth_access_type? + + access_type = param_or_nil("access_type") + !access_type || ACCESS_TYPES.include?(access_type) + end + + APPROVAL_PROMPTS = %w[force auto].freeze + + def check_valid_approval_prompt? + return true unless use_oauth_access_type? + + approval_prompt = param_or_nil("approval_prompt") + !approval_prompt || APPROVAL_PROMPTS.include?(approval_prompt) + end + + def try_approval_prompt + approval_prompt = param_or_nil("approval_prompt") + + return unless approval_prompt && approval_prompt == "auto" + + return if db[oauth_grants_table].where( + oauth_grants_account_id_column => account_id, + oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column], + oauth_grants_redirect_uri_column => redirect_uri, + oauth_grants_scopes_column => scopes.join(oauth_scope_separator), + oauth_grants_access_type_column => "online" + ).count.zero? + + # if there's a previous oauth grant for the params combo, it means that this user has approved before. + request.env["REQUEST_METHOD"] = "POST" + end + def authorization_required if accepts_json? - throw_json_response_error(authorization_required_error_status, "invalid_client") + throw_json_response_error(oauth_authorization_required_error_status, "invalid_client") else set_redirect_error_flash(require_authorization_error_flash) redirect(authorize_path) end end def do_authorize(*args); end def authorize_response(params, mode); end - def create_oauth_token_from_authorization_code(oauth_grant, create_params, should_generate_refresh_token = false) - # revoke oauth grant - db[oauth_grants_table].where(oauth_grants_id_column => oauth_grant[oauth_grants_id_column]) - .update(oauth_grants_revoked_at_column => Sequel::CURRENT_TIMESTAMP) + def create_token_from_authorization_code(grant_params, should_generate_refresh_token = !use_oauth_access_type?, oauth_grant: nil) + # fetch oauth grant + oauth_grant ||= valid_locked_oauth_grant(grant_params) should_generate_refresh_token ||= oauth_grant[oauth_grants_access_type_column] == "offline" - generate_oauth_token(create_params, should_generate_refresh_token) + generate_token(oauth_grant, should_generate_refresh_token) end def create_oauth_grant(create_params = {}) - create_params.merge!( - oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column], - oauth_grants_redirect_uri_column => redirect_uri, - oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in), - oauth_grants_scopes_column => scopes.join(oauth_scope_separator) - ) + create_params[oauth_grants_oauth_application_id_column] = oauth_application[oauth_applications_id_column] + create_params[oauth_grants_redirect_uri_column] = redirect_uri + create_params[oauth_grants_expires_in_column] = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in) + create_params[oauth_grants_scopes_column] = scopes.join(oauth_scope_separator) + if use_oauth_access_type? && (access_type = param_or_nil("access_type")) + create_params[oauth_grants_access_type_column] = access_type + end + ds = db[oauth_grants_table] + create_params[oauth_grants_code_column] = oauth_unique_id_generator + + if oauth_reuse_access_token + unique_conds = Hash[oauth_grants_unique_columns.map { |column| [column, create_params[column]] }] + valid_grant = valid_oauth_grant_ds(unique_conds).select(oauth_grants_id_column).first + if valid_grant + create_params[oauth_grants_id_column] = valid_grant[oauth_grants_id_column] + rescue_from_uniqueness_error do + __insert_or_update_and_return__( + ds, + oauth_grants_id_column, + [oauth_grants_id_column], + create_params + ) + end + return create_params[oauth_grants_code_column] + end + end + rescue_from_uniqueness_error do - create_params[oauth_grants_code_column] = oauth_unique_id_generator - __insert_and_return__(ds, oauth_grants_id_column, create_params) + if __one_oauth_token_per_account + __insert_or_update_and_return__( + ds, + oauth_grants_id_column, + oauth_grants_unique_columns, + create_params, + nil, + { + oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in), + oauth_grants_revoked_at_column => nil + } + ) + else + __insert_and_return__(ds, oauth_grants_id_column, create_params) + end end create_params[oauth_grants_code_column] end end end