From e29b06373251a0388e0e3105f8aa8b8b0b900f8a Mon Sep 17 00:00:00 2001 From: JeremyC-za Date: Fri, 19 May 2023 13:42:44 +0200 Subject: [PATCH] Added config option `revoke_previous_authorization_code_token`. --- lib/doorkeeper/config.rb | 11 ++++++ .../oauth/authorization_code_request.rb | 8 +++++ .../doorkeeper/templates/initializer.rb | 6 ++++ .../oauth/authorization_code_request_spec.rb | 36 +++++++++++++++++++ 4 files changed, 61 insertions(+) diff --git a/lib/doorkeeper/config.rb b/lib/doorkeeper/config.rb index eae3aa70d..e6abd3b2e 100644 --- a/lib/doorkeeper/config.rb +++ b/lib/doorkeeper/config.rb @@ -106,6 +106,13 @@ def revoke_previous_client_credentials_token @config.instance_variable_set(:@revoke_previous_client_credentials_token, true) end + # Only allow one valid access token obtained via authorization code + # per client. If a new access token is obtained before the old one + # expired, the old one gets revoked (disabled by default) + def revoke_previous_authorization_code_token + @config.instance_variable_set(:@revoke_previous_authorization_code_token, true) + end + # Use an API mode for applications generated with --api argument # It will skip applications controller, disable forgery protection def api_only @@ -481,6 +488,10 @@ def revoke_previous_client_credentials_token? option_set? :revoke_previous_client_credentials_token end + def revoke_previous_authorization_code_token? + option_set? :revoke_previous_authorization_code_token + end + def enforce_configured_scopes? option_set? :enforce_configured_scopes end diff --git a/lib/doorkeeper/oauth/authorization_code_request.rb b/lib/doorkeeper/oauth/authorization_code_request.rb index 43ac24508..849e6745c 100644 --- a/lib/doorkeeper/oauth/authorization_code_request.rb +++ b/lib/doorkeeper/oauth/authorization_code_request.rb @@ -29,6 +29,10 @@ def before_successful_response grant.lock! raise Errors::InvalidGrantReuse if grant.revoked? + if Doorkeeper.config.revoke_previous_authorization_code_token? + revoke_previous_tokens(grant.application, resource_owner) + end + grant.revoke find_or_create_access_token( @@ -109,6 +113,10 @@ def custom_token_attributes_with_data .slice(*Doorkeeper.config.custom_access_token_attributes) .symbolize_keys end + + def revoke_previous_tokens(application, resource_owner) + Doorkeeper.config.access_token_model.revoke_all_for(application.id, resource_owner) + end end end end diff --git a/lib/generators/doorkeeper/templates/initializer.rb b/lib/generators/doorkeeper/templates/initializer.rb index f1f48309b..601c6da58 100644 --- a/lib/generators/doorkeeper/templates/initializer.rb +++ b/lib/generators/doorkeeper/templates/initializer.rb @@ -164,6 +164,12 @@ # # revoke_previous_client_credentials_token + # Only allow one valid access token obtained via authorization code + # per client. If a new access token is obtained before the old one + # expired, the old one gets revoked (disabled by default) + # + # revoke_previous_authorization_code_token + # Hash access and refresh tokens before persisting them. # This will disable the possibility to use +reuse_access_token+ # since plain values can no longer be retrieved. diff --git a/spec/lib/oauth/authorization_code_request_spec.rb b/spec/lib/oauth/authorization_code_request_spec.rb index fe452a29a..a5914f74e 100644 --- a/spec/lib/oauth/authorization_code_request_spec.rb +++ b/spec/lib/oauth/authorization_code_request_spec.rb @@ -220,4 +220,40 @@ end end end + + context "when revoke_previous_authorization_code_token is false" do + before do + allow(Doorkeeper.config).to receive(:revoke_previous_authorization_code_token?).and_return(false) + end + + it "does not revoke the previous token" do + previous_token = FactoryBot.create( + :access_token, + application_id: client.id, + resource_owner_id: grant.resource_owner_id, + resource_owner_type: grant.resource_owner_type, + scopes: grant.scopes.to_s, + ) + + expect { request.authorize }.not_to(change { previous_token.reload.revoked_at }) + end + end + + context "when revoke_previous_authorization_code_token is true" do + before do + allow(Doorkeeper.config).to receive(:revoke_previous_authorization_code_token?).and_return(true) + end + + it "revokes the previous token" do + previous_token = FactoryBot.create( + :access_token, + application_id: client.id, + resource_owner_id: grant.resource_owner_id, + resource_owner_type: grant.resource_owner_type, + scopes: grant.scopes.to_s, + ) + + expect { request.authorize }.to(change { previous_token.reload.revoked_at }) + end + end end