Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions includes/class-magic-link.php
Original file line number Diff line number Diff line change
Expand Up @@ -516,11 +516,27 @@ public static function send_email( $user, $redirect_to = '', $use_otp = true ) {
'value' => $token_data['otp']['code'],
];
}
return Emails::send_email(

/**
* Filters the email config name for magic link/OTP authentication emails.
* Allows overriding the email template used based on the current context.
*
* @param string $email_config_name The email config name (e.g., 'reader-activation-otp-authentication').
* @param string $email_type The email type key (e.g., 'OTP_AUTH', 'MAGIC_LINK').
* @param \WP_User $user The user receiving the email.
* @param string $redirect_to The redirect URL after authentication.
* @param array $token_data The token data including OTP information.
*/
$email_config_name = \apply_filters(
'newspack_magic_link_email_config',
Reader_Activation_Emails::EMAIL_TYPES[ $email_type ],
$user->user_email,
$email_placeholders
$email_type,
$user,
$redirect_to,
$token_data
);

return Emails::send_email( $email_config_name, $user->user_email, $email_placeholders );
}

/**
Expand Down
2 changes: 1 addition & 1 deletion includes/reader-activation/class-reader-activation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2712,7 +2712,7 @@ public static function login_after_password_reset( $user ) {
* @param string $url The URL to check.
* @return bool True if the URL path contains an OAuth route.
*/
private static function is_oauth_redirect( $url ) {
public static function is_oauth_redirect( $url ) {
$url_path = \wp_parse_url( $url, PHP_URL_PATH ) ?? '';

/**
Expand Down
23 changes: 19 additions & 4 deletions src/reader-activation-auth/auth-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ window.newspackRAS.push( function ( readerActivation ) {
const resendCodeButton = container.querySelector( '[data-resend-code]' );
const messageContentElement = container.querySelector( '.response' );

/**
* Check if the current URL has a redirect parameter.
* Used to determine if we should pass the full URL to the backend during OAuth flows.
*
* @return {boolean} True if URL has a 'redirect' query parameter.
*/
const hasRedirectInUrl = () => {
const urlParams = new URLSearchParams( window.location.search );
return urlParams.has( 'redirect' );
};

/**
* Set action listener on the given item.
*/
Expand Down Expand Up @@ -216,9 +227,11 @@ window.newspackRAS.push( function ( readerActivation ) {
body.set( 'npe', emailInput.value );
body.set( 'action', 'link' );
const pendingCheckout = getPendingCheckout();
if ( pendingCheckout ) {
if ( pendingCheckout || hasRedirectInUrl() ) {
const url = new URL( window.location.href );
url.searchParams.set( 'checkout', 1 );
if ( pendingCheckout ) {
url.searchParams.set( 'checkout', 1 );
}
body.set( 'redirect_url', url.toString() );
}
fetch( form.getAttribute( 'action' ) || window.location.pathname, {
Expand Down Expand Up @@ -410,9 +423,11 @@ window.newspackRAS.push( function ( readerActivation ) {
return form.endLoginFlow( newspack_reader_activation_labels.invalid_email, 400 );
}
const pendingCheckout = getPendingCheckout();
if ( pendingCheckout ) {
if ( pendingCheckout || hasRedirectInUrl() ) {
const url = new URL( window.location.href );
url.searchParams.set( 'checkout', 1 );
if ( pendingCheckout ) {
url.searchParams.set( 'checkout', 1 );
}
body.set( 'redirect_url', url.toString() );
}

Expand Down
36 changes: 36 additions & 0 deletions tests/unit-tests/magic-link.php
Original file line number Diff line number Diff line change
Expand Up @@ -315,4 +315,40 @@ public function test_expired_token() {
$this->assertTrue( is_wp_error( $validation ) );
$this->assertEquals( 'invalid_token', $validation->get_error_code() );
}

/**
* Test that newspack_magic_link_email_config filter can override the email config.
*/
public function test_email_config_filter() {
$filter_called = false;
$received_params = [];

// Add a filter to capture the parameters and modify the config name.
$filter_callback = function ( $email_config_name, $email_type, $user, $redirect_to, $token_data ) use ( &$filter_called, &$received_params ) {
$filter_called = true;
$received_params = [
'email_config_name' => $email_config_name,
'email_type' => $email_type,
'user' => $user,
'redirect_to' => $redirect_to,
'token_data' => $token_data,
];
// Return a custom config name.
return 'custom-email-config';
};

add_filter( 'newspack_magic_link_email_config', $filter_callback, 10, 5 );

// Trigger send_email (it will fail to send since we don't have email setup, but filter should still fire).
$user = get_user_by( 'id', self::$user_id );
Magic_Link::send_email( $user, 'https://example.com/redirect' );

remove_filter( 'newspack_magic_link_email_config', $filter_callback, 10 );

$this->assertTrue( $filter_called, 'The newspack_magic_link_email_config filter was called.' );
$this->assertEquals( 'OTP_AUTH', $received_params['email_type'], 'Filter received the correct email type.' );
$this->assertEquals( $user->ID, $received_params['user']->ID, 'Filter received the correct user.' );
$this->assertEquals( 'https://example.com/redirect', $received_params['redirect_to'], 'Filter received the correct redirect_to.' );
$this->assertIsArray( $received_params['token_data'], 'Filter received token data as array.' );
}
}
29 changes: 29 additions & 0 deletions tests/unit-tests/reader-activation.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,33 @@ public function test_restricted_roles() {
$this->assertFalse( Reader_Activation::is_user_reader( $user ) );
wp_delete_user( $reader_id ); // Clean up.
}

/**
* Test is_oauth_redirect detection.
*/
public function test_is_oauth_redirect() {
$this->assertTrue( Reader_Activation::is_oauth_redirect( 'https://example.com/oauth/authorize?client_id=123' ) );
$this->assertFalse( Reader_Activation::is_oauth_redirect( 'https://example.com/my-account/' ) );
$this->assertFalse( Reader_Activation::is_oauth_redirect( 'https://example.com/' ) );
$this->assertFalse( Reader_Activation::is_oauth_redirect( '' ) );
}

/**
* Test is_oauth_redirect filter can extend routes.
*/
public function test_is_oauth_redirect_filter() {
// Add a custom OAuth route via filter.
add_filter(
'newspack_ras_oauth_redirect_routes',
function ( $routes ) {
$routes[] = '/custom-oauth/';
return $routes;
}
);

$this->assertTrue(
Reader_Activation::is_oauth_redirect( 'https://example.com/custom-oauth/?param=value' ),
'Custom OAuth route should be detected after adding via filter.'
);
}
}