From 3d71a28fb3614d711b36dc6bf572292c6763f646 Mon Sep 17 00:00:00 2001 From: MILLER/F Date: Mon, 16 Oct 2023 15:47:35 +0200 Subject: [PATCH] [memberships] Remove token from url (#33561) * Remove sending token as GET param and refactor * Create add-memberships-remove-token-from-url * Amend $email_confirmation_pending to retrieve the data from the token Fix issue when subscription is pending and do not rely on subscribe=success * Fix for tests --- .../add-memberships-remove-token-from-url | 4 +++ .../class-token-subscription-service.php | 32 +++++-------------- .../extensions/blocks/premium-content/view.js | 26 +-------------- .../blocks/subscriptions/subscriptions.php | 13 ++++++-- .../extensions/blocks/subscriptions/view.js | 31 ++---------------- .../jetpack/extensions/shared/memberships.js | 22 +++++++++---- 6 files changed, 42 insertions(+), 86 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/add-memberships-remove-token-from-url diff --git a/projects/plugins/jetpack/changelog/add-memberships-remove-token-from-url b/projects/plugins/jetpack/changelog/add-memberships-remove-token-from-url new file mode 100644 index 0000000000000..bef6316bcf5c2 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-memberships-remove-token-from-url @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Subscriptions: Do not display token in URL diff --git a/projects/plugins/jetpack/extensions/blocks/premium-content/_inc/subscription-service/class-token-subscription-service.php b/projects/plugins/jetpack/extensions/blocks/premium-content/_inc/subscription-service/class-token-subscription-service.php index a723d08a6bdd4..d9df92343db71 100644 --- a/projects/plugins/jetpack/extensions/blocks/premium-content/_inc/subscription-service/class-token-subscription-service.php +++ b/projects/plugins/jetpack/extensions/blocks/premium-content/_inc/subscription-service/class-token-subscription-service.php @@ -46,12 +46,12 @@ public function initialize() { public function get_and_set_token_from_request() { // URL token always has a precedence, so it can overwrite the cookie when new data available. $token = $this->token_from_request(); - if ( null !== $token ) { $this->set_token_cookie( $token ); + return $token; } - return $token; + return $this->token_from_cookie(); } /** @@ -74,30 +74,14 @@ public function visitor_can_view_content( $valid_plan_ids, $access_level ) { global $current_user; $old_user = $current_user; // backup the current user so we can set the current user to the token user for paywall purposes - // URL token always has a precedence, so it can overwrite the cookie when new data available. - $token = $this->token_from_request(); - if ( $token ) { - $this->set_token_cookie( $token ); - } else { - $token = $this->token_from_cookie(); - } - - $is_valid_token = true; - - if ( empty( $token ) ) { - // no token, no access. - $is_valid_token = false; - } else { - $payload = $this->decode_token( $token ); - if ( empty( $payload ) ) { - $is_valid_token = false; - } + $token = $this->get_and_set_token_from_request(); + $payload = $this->decode_token( $token ); + $is_valid_token = ! empty( $payload ); + if ( $is_valid_token && isset( $payload['user_id'] ) ) { // set the current user to the payload's user id - if ( isset( $payload['user_id'] ) ) { - // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited - $current_user = get_user_by( 'id', $payload['user_id'] ); - } + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $current_user = get_user_by( 'id', $payload['user_id'] ); } $is_blog_subscriber = false; diff --git a/projects/plugins/jetpack/extensions/blocks/premium-content/view.js b/projects/plugins/jetpack/extensions/blocks/premium-content/view.js index 48722494e4ff8..9e81a24695dea 100644 --- a/projects/plugins/jetpack/extensions/blocks/premium-content/view.js +++ b/projects/plugins/jetpack/extensions/blocks/premium-content/view.js @@ -1,31 +1,7 @@ import './view.scss'; -import { - setPurchaseResultCookie, - reloadPageWithPremiumContentQueryString, -} from '../../../extensions/shared/memberships'; +import { handleIframeResult } from '../../../extensions/shared/memberships'; document.addEventListener( 'DOMContentLoaded', function () { - let premiumContentJWTToken = ''; - - /** - * @typedef globalThis - * @param {globalThis.Event} eventFromIframe - message event that gets emitted in the checkout iframe. - * @listens window#message - */ - function handleIframeResult( eventFromIframe ) { - if ( eventFromIframe.origin === 'https://subscribe.wordpress.com' && eventFromIframe.data ) { - const data = JSON.parse( eventFromIframe.data ); - if ( data && data.result && data.result.jwt_token ) { - // We save the token for now, doing nothing. - premiumContentJWTToken = data.result.jwt_token; - setPurchaseResultCookie( premiumContentJWTToken ); - } - if ( data && data.action === 'close' && premiumContentJWTToken ) { - reloadPageWithPremiumContentQueryString( premiumContentJWTToken ); - } - } - } - if ( typeof window !== 'undefined' ) { window.addEventListener( 'message', handleIframeResult, false ); } diff --git a/projects/plugins/jetpack/extensions/blocks/subscriptions/subscriptions.php b/projects/plugins/jetpack/extensions/blocks/subscriptions/subscriptions.php index 878020ca18062..5b5b0d9d55a5e 100644 --- a/projects/plugins/jetpack/extensions/blocks/subscriptions/subscriptions.php +++ b/projects/plugins/jetpack/extensions/blocks/subscriptions/subscriptions.php @@ -9,7 +9,9 @@ use Automattic\Jetpack\Blocks; use Automattic\Jetpack\Connection\Manager as Connection_Manager; +use Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Jetpack_Token_Subscription_Service; use Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Token_Subscription_Service; +use Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\WPCOM_Token_Subscription_Service; use Automattic\Jetpack\Status; use Automattic\Jetpack\Status\Host; use Jetpack; @@ -926,7 +928,14 @@ function add_paywall( $the_content ) { $post_access_level = Jetpack_Memberships::get_post_access_level(); - $paywalled_content = get_paywall_content( $post_access_level, isset( $_GET['subscribe'] ) && 'success' === $_GET['subscribe'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended + require_once JETPACK__PLUGIN_DIR . 'extensions/blocks/premium-content/_inc/subscription-service/include.php'; + $token_service = is_wpcom() ? new WPCOM_Token_Subscription_Service() : new Jetpack_Token_Subscription_Service(); + $token = $token_service->get_and_set_token_from_request(); + $payload = $token_service->decode_token( $token ); + $is_valid_token = ! empty( $payload ); + $email_confirmation_pending = $is_valid_token && isset( $payload['blog_sub'] ) && $payload['blog_sub'] === 'pending'; + + $paywalled_content = get_paywall_content( $post_access_level, $email_confirmation_pending ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( has_block( \Automattic\Jetpack\Extensions\Paywall\BLOCK_NAME ) ) { if ( strpos( $the_content, \Automattic\Jetpack\Extensions\Paywall\BLOCK_HTML ) ) { @@ -1101,7 +1110,7 @@ function get_paywall_blocks( $newsletter_access_level ) { * @return string */ function get_paywall_blocks_subscribe_pending() { - $access_heading = esc_html__( 'Verify your email and continue reading', 'jetpack' ); + $access_heading = esc_html__( 'Verify your email to continue reading', 'jetpack' ); $subscribe_text = esc_html__( 'Please check your inbox to confirm your subscription.', 'jetpack' ); diff --git a/projects/plugins/jetpack/extensions/blocks/subscriptions/view.js b/projects/plugins/jetpack/extensions/blocks/subscriptions/view.js index 04cd583958a4d..f611dc8e4e39e 100644 --- a/projects/plugins/jetpack/extensions/blocks/subscriptions/view.js +++ b/projects/plugins/jetpack/extensions/blocks/subscriptions/view.js @@ -1,20 +1,16 @@ -/* global tb_show, tb_remove */ +/* global tb_show */ import './view.scss'; import '../../shared/memberships.scss'; import domReady from '@wordpress/dom-ready'; -import { - setPurchaseResultCookie, - reloadPageWithPremiumContentQueryString, -} from '../../../extensions/shared/memberships'; +import { handleIframeResult } from '../../../extensions/shared/memberships'; domReady( function () { const form = document.querySelector( '.wp-block-jetpack-subscriptions__container form' ); if ( ! form ) { return; } - let premiumContentJWTToken = ''; if ( ! form.payments_attached ) { form.payments_attached = true; form.addEventListener( 'submit', function ( event ) { @@ -46,29 +42,6 @@ domReady( function () { window.scrollTo( 0, 0 ); tb_show( null, url + '&TB_iframe=true', null ); - //TODO: Unify this code across premium content, subscribe and payment button. - const handleIframeResult = function ( eventFromIframe ) { - if ( - eventFromIframe.origin === 'https://subscribe.wordpress.com' && - eventFromIframe.data - ) { - const data = JSON.parse( eventFromIframe.data ); - if ( data && data.result && data.result.jwt_token ) { - // We save the token for now, doing nothing. - premiumContentJWTToken = data.result.jwt_token; - setPurchaseResultCookie( premiumContentJWTToken ); - } else if ( data && data.action === 'close' && premiumContentJWTToken ) { - // The token was set during the purchase flow, we want to relead the whole page with token in query string so it recognizes that the user is logged in. - reloadPageWithPremiumContentQueryString( premiumContentJWTToken, { - subscribe: 'success', - } ); - } else if ( data && data.action === 'close' ) { - // User just aborted. - window.removeEventListener( 'message', handleIframeResult ); - tb_remove(); - } - } - }; window.addEventListener( 'message', handleIframeResult, false ); const tbWindow = document.querySelector( '#TB_window' ); tbWindow.classList.add( 'jetpack-memberships-modal' ); diff --git a/projects/plugins/jetpack/extensions/shared/memberships.js b/projects/plugins/jetpack/extensions/shared/memberships.js index d1337d4c9e3b7..bb5687dbab862 100644 --- a/projects/plugins/jetpack/extensions/shared/memberships.js +++ b/projects/plugins/jetpack/extensions/shared/memberships.js @@ -1,17 +1,27 @@ /* global tb_show, tb_remove */ +let premiumContentJWTTokenForCookie = ''; + /** - * Since "close" button is inside our checkout iframe, in order to close it, it has to pass a message to higher scope to close the modal. - * - * @param {event} eventFromIframe - message event that gets emmited in the checkout iframe. + * @typedef globalThis + * @param {globalThis.Event} eventFromIframe - message event that gets emitted in the checkout iframe. * @listens window#message */ -function handleIframeResult( eventFromIframe ) { +export function handleIframeResult( eventFromIframe ) { if ( eventFromIframe.origin === 'https://subscribe.wordpress.com' && eventFromIframe.data ) { const data = JSON.parse( eventFromIframe.data ); - if ( data && data.action === 'close' ) { + if ( data && data.result && data.result.jwt_token ) { + // We save the token for now, doing nothing. + premiumContentJWTTokenForCookie = data.result.jwt_token; + setPurchaseResultCookie( premiumContentJWTTokenForCookie ); + } + if ( data && data.action === 'close' && premiumContentJWTTokenForCookie ) { + // The token was set during the purchase flow, we want to reload the whole page so the content is displayed + window.location.reload(); + } else if ( data && data.action === 'close' ) { + // User just aborted. window.removeEventListener( 'message', handleIframeResult ); - tb_remove(); + tb_remove && tb_remove(); } } }