diff --git a/src/class-convertkit-api-v4.php b/src/class-convertkit-api-v4.php index ad3762a..fcc26b9 100644 --- a/src/class-convertkit-api-v4.php +++ b/src/class-convertkit-api-v4.php @@ -89,6 +89,7 @@ class ConvertKit_API_V4 { */ protected $api_endpoints_oauth = array( 'token', + 'revoke', ); /** @@ -464,6 +465,89 @@ public function refresh_token() { } + /** + * Revokes the current access and refresh tokens. + * + * @since 2.1.4 + */ + public function revoke_tokens() { + + // Revoke the access token. + $result = $this->post( + 'revoke', + array( + 'client_id' => $this->client_id, + 'token' => $this->access_token, + ) + ); + + // If an error occured, log and return it now. + if ( is_wp_error( $result ) ) { + $this->log( 'API: Error: ' . $result->get_error_message() ); + + /** + * Perform any actions when revoking an access token fails. + * + * @since 2.1.4 + * + * @param WP_Error $result Error from API. + * @param string $client_id OAuth Client ID. + */ + do_action( 'convertkit_api_revoke_tokens_access_token_error', $result, $this->client_id ); + + return $result; + } + + // Revoke the refresh token. + $result = $this->post( + 'revoke', + array( + 'client_id' => $this->client_id, + 'token' => $this->refresh_token, + ) + ); + + // If an error occured, log and return it now. + if ( is_wp_error( $result ) ) { + $this->log( 'API: Error: ' . $result->get_error_message() ); + + /** + * Perform any actions when revoking a refresh token fails. + * + * @since 2.1.4 + * + * @param WP_Error $result Error from API. + * @param string $client_id OAuth Client ID. + */ + do_action( 'convertkit_api_revoke_tokens_refresh_token_error', $result, $this->client_id ); + + return $result; + } + + // Store existing access and refresh tokens. + $previous_access_token = $this->access_token; + $previous_refresh_token = $this->refresh_token; + + // Remove access and refresh tokens from this class. + $this->access_token = ''; + $this->refresh_token = ''; + + /** + * Perform any actions when the tokens are revoked, such as deleting them from the database. + * + * @since 2.1.4 + * + * @param string $client_id OAuth Client ID. + * @param string $previous_access_token Existing Access Token. + * @param string $previous_refresh_token Existing Refresh Token. + */ + do_action( 'convertkit_api_revoke_tokens', $this->client_id, $previous_access_token, $previous_refresh_token ); + + // Return. + return $result; + + } + /** * Exchanges the given API Key for an Access Token. * diff --git a/tests/Integration/APITest.php b/tests/Integration/APITest.php index ff9e405..ad34182 100644 --- a/tests/Integration/APITest.php +++ b/tests/Integration/APITest.php @@ -528,6 +528,57 @@ public function testRefreshTokenWithInvalidToken() $this->assertEquals($result->get_error_code(), 'convertkit_api_error'); } + /** + * Test that the access token and refresh token are revoked when revoke_tokens() is called. + * + * @since 2.1.4 + */ + public function testRevokeTokens() + { + // Initialize the API without an access token or refresh token. + $api = new \ConvertKit_API_V4( + $_ENV['CONVERTKIT_OAUTH_CLIENT_ID'], + $_ENV['CONVERTKIT_OAUTH_REDIRECT_URI'] + ); + + // Generate an access token by API key and secret. + $result = $api->get_access_token_by_api_key_and_secret( + $_ENV['CONVERTKIT_API_KEY'], + $_ENV['CONVERTKIT_API_SECRET'], + wp_generate_password( 10, false ) // Random tenant name to produce a token for this request only. + ); + + // Initialize the API with the access token and refresh token. + $api = new \ConvertKit_API_V4( + $_ENV['CONVERTKIT_OAUTH_CLIENT_ID'], + $_ENV['CONVERTKIT_OAUTH_REDIRECT_URI'], + $result['oauth']['access_token'], + $result['oauth']['refresh_token'] + ); + + // Confirm the token works when making an authenticated request. + $this->assertNotInstanceOf( 'WP_Error', $api->get_account() ); + + // Revoke the access and refresh tokens. + $api->revoke_tokens(); + + // Initialize the API with the (now revoked) access token and refresh token. + // revoke_tokens() will have removed the access token and refresh token from the API class, so we need to provide them again + // to test they're revoked. + $api = new \ConvertKit_API_V4( + $_ENV['CONVERTKIT_OAUTH_CLIENT_ID'], + $_ENV['CONVERTKIT_OAUTH_REDIRECT_URI'], + $result['oauth']['access_token'], + $result['oauth']['refresh_token'] + ); + + // Confirm attempting to use the revoked access token no longer works. + $this->assertInstanceOf( 'WP_Error', $api->get_account() ); + + // Confirm attempting to use the revoked refresh token no longer works. + $this->assertInstanceOf( 'WP_Error', $api->refresh_token() ); + } + /** * Test that supplying no API credentials to the API class returns a WP_Error. *