Plugin Directory

Changeset 3744682


Ignore:
Timestamp:
10/13/2026 06:30:07 PM (3 days ago)
Author:
shivaat
Message:

Tagsmith 2.1.1: the sign-in return no longer passes through wp-admin

Location:
tagsmith
Files:
12 edited
1 copied

Legend:

Unmodified
Added
Removed
  • tagsmith/tags/2.1.1/package-lock.json

    r3744485 r3744682  
    11{
    22  "name": "tagsmith",
    3   "version": "2.1.0",
     3  "version": "2.1.1",
    44  "lockfileVersion": 3,
    55  "requires": true,
    … …  
    77    "": {
    88      "name": "tagsmith",
    9       "version": "2.1.0",
     9      "version": "2.1.1",
    1010      "devDependencies": {
    1111        "@playwright/test": "^1.63.0",
  • tagsmith/tags/2.1.1/package.json

    r3744485 r3744682  
    22  "name": "tagsmith",
    33  "private": true,
    4   "version": "2.1.0",
     4  "version": "2.1.1",
    55  "scripts": {
    66    "build": "wp-scripts build --webpack-src-dir=assets/src",
  • tagsmith/tags/2.1.1/readme.txt

    r3744485 r3744682  
    55Tested up to: 7.1
    66Requires PHP: 8.1
    7 Stable tag: 2.1.0
     7Stable tag: 2.1.1
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    146146== Changelog ==
    147147
     148= 2.1.1 =
     149* Fixed: the Google sign-in return no longer passes through wp-admin, so sites that send logged-out wp-admin requests to a login or account page (membership and LMS plugins) can connect.
     150
    148151= 2.1.0 =
    149152* New: ad pixels for Meta, Google Ads, TikTok, LinkedIn, Microsoft Advertising, Pinterest, Snap and X, set up in the new Ads & pixels tab and off until you switch one on.
  • tagsmith/tags/2.1.1/src/Admin/OAuthReturn.php

    r3744428 r3744682  
    1616/**
    1717 * Wires OAuthFlow::receive() and OAuthFlow::finalize() into WordPress.
     18 * The relay's return reaches receive() through maybe_receive() (front end);
     19 * the admin-post actions remain for the BYO redirect and older in-flight returns.
    1820 *
    1921 * CSRF note: these handlers do not use a nonce. The relay's handoff is a
    … …  
    7375         */
    7476        public function register(): void {
     77                add_action( 'init', array( $this, 'maybe_receive' ), 1 );
    7578                add_action( 'admin_post_tagsmith_oauth_return', array( $this, 'receive' ) );
    7679                add_action( 'admin_post_nopriv_tagsmith_oauth_return', array( $this, 'receive' ) );
    7780                add_action( 'admin_init', array( $this, 'maybe_finalize' ) );
     81        }
     82
     83        /**
     84         * Phase 1 on a front-end URL: takes the relay's POST to
     85         * home_url( '/?tagsmith_oauth_return=1' ), which does not pass through
     86         * wp-admin (plugins that redirect logged-out wp-admin requests would
     87         * swallow an admin-post.php return). Anything but a POST with the marker
     88         * is left alone. Same protection as the admin-post path: the single-use
     89         * random state checked in receive().
     90         */
     91        public function maybe_receive(): void {
     92                $method = isset( $_SERVER['REQUEST_METHOD'] ) && is_string( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) : '';
     93
     94                if ( 'POST' !== $method ) {
     95                        return;
     96                }
     97
     98                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- CSRF protection is the single-use random state checked in receive() (see class doc).
     99                if ( ! isset( $_GET['tagsmith_oauth_return'] ) || ! is_string( $_GET['tagsmith_oauth_return'] ) ) {
     100                        return;
     101                }
     102
     103                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- see above.
     104                if ( '1' !== sanitize_text_field( wp_unslash( $_GET['tagsmith_oauth_return'] ) ) ) {
     105                        return;
     106                }
     107
     108                $this->receive();
    78109        }
    79110
  • tagsmith/tags/2.1.1/src/Auth/OAuthFlow.php

    r3744485 r3744682  
    163163
    164164        /**
    165          * The OAuth return endpoint (also the BYO redirect URI to register).
     165         * The BYO redirect URI users register with Google. Google's return here is
     166         * a logged-in GET, so wp-admin is fine. The relay does not use this URL:
     167         * see relay_return_url().
    166168         */
    167169        public static function return_url(): string {
    … …  
    170172
    171173        /**
     174         * Where the relay hands the browser back: the WordPress address (not
     175         * wp-admin).
     176         *
     177         * The handoff is a cross-site POST without cookies, so WordPress sees a
     178         * logged-out request. Membership, LMS and "hide wp-admin" plugins redirect
     179         * logged-out wp-admin requests to a login or account page before this
     180         * plugin can run, while this URL reaches OAuthReturn::maybe_receive() on
     181         * init. It is built from site_url(), not home_url(): the site address can
     182         * be a headless front end or another origin, whereas site_url() is where
     183         * WordPress runs and shares its origin and path with admin_url(). The relay
     184         * accepts this shape from 2.1.1 on.
     185         */
     186        public static function relay_return_url(): string {
     187                return add_query_arg( 'tagsmith_oauth_return', '1', site_url( '/' ) );
     188        }
     189
     190        /**
    172191         * Starts a connection: creates a single-use state bound to the user and
    173192         * returns the URL to send the browser to (the relay, or Google in BYO mode).
    … …  
    175194         * @param int      $user_id  The user starting the flow.
    176195         * @param string[] $features Subset of ['analytics','searchconsole'].
    177          * @throws ApiError 'https_required' when wp-admin is served over plain
    178          *                  http on a non-local host: the relay and Google both
    179          *                  reject a non-https return URL.
     196         * @throws ApiError 'https_required' when wp-admin (or, for the relay, the
     197         *                  WordPress address) is served over plain http on a
     198         *                  non-local host: the relay and Google both reject a
     199         *                  non-https return URL.
    180200         */
    181201        public function start_url( int $user_id, array $features ): string {
    182                 if ( ! self::secure_return_url() ) {
    183                         throw new ApiError( 'https_required', 'Google sign-in needs an https admin URL.' );
     202                $byo = $this->usable_byo_credentials();
     203
     204                // Google gets return_url(); the relay gets relay_return_url(). Both reject plain http off local hosts.
     205                if ( ! self::secure_url( self::return_url() ) || ( null === $byo && ! self::secure_url( self::relay_return_url() ) ) ) {
     206                        throw new ApiError( 'https_required', 'Google sign-in needs an https admin and WordPress address.' );
    184207                }
    185208
    … …  
    191214                );
    192215
    193                 $byo = $this->usable_byo_credentials();
    194 
    195216                if ( null === $byo ) {
    196217                        set_transient( self::START_PREFIX . $state, $start, 600 );
    … …  
    198219                        $query = array(
    199220                                'state'      => $state,
    200                                 'return_url' => self::return_url(),
     221                                'return_url' => self::relay_return_url(),
    201222                                'scopes'     => implode( ',', $features ),
    202223                                'site'       => home_url(),
    … …  
    684705
    685706        /**
    686          * Whether the OAuth return URL is acceptable to the relay and Google:
     707         * Whether an OAuth return URL is acceptable to the relay and Google:
    687708         * https, or plain http only on a local development host.
    688          */
    689         private static function secure_return_url(): bool {
    690                 $url    = self::return_url();
     709         *
     710         * @param string $url Return URL.
     711         */
     712        private static function secure_url( string $url ): bool {
    691713                $scheme = strtolower( (string) wp_parse_url( $url, PHP_URL_SCHEME ) );
    692714
  • tagsmith/tags/2.1.1/tagsmith.php

    r3744485 r3744682  
    44 * Plugin URI:        https://shivaatripathi.com/tagsmith/
    55 * Description:       See Google Analytics 4 reports inside WordPress: a clear dashboard, per-post stats and Search Console. Read-only Google access. No nags.
    6  * Version:           2.1.0
     6 * Version:           2.1.1
    77 * Requires at least: 6.7
    88 * Requires PHP:      8.1
    … …  
    1919defined( 'ABSPATH' ) || exit;
    2020
    21 define( 'TAGSMITH_VERSION', '2.1.0' );
     21define( 'TAGSMITH_VERSION', '2.1.1' );
    2222define( 'TAGSMITH_FILE', __FILE__ );
    2323define( 'TAGSMITH_DIR', plugin_dir_path( __FILE__ ) );
  • tagsmith/trunk/package-lock.json

    r3744485 r3744682  
    11{
    22  "name": "tagsmith",
    3   "version": "2.1.0",
     3  "version": "2.1.1",
    44  "lockfileVersion": 3,
    55  "requires": true,
    … …  
    77    "": {
    88      "name": "tagsmith",
    9       "version": "2.1.0",
     9      "version": "2.1.1",
    1010      "devDependencies": {
    1111        "@playwright/test": "^1.63.0",
  • tagsmith/trunk/package.json

    r3744485 r3744682  
    22  "name": "tagsmith",
    33  "private": true,
    4   "version": "2.1.0",
     4  "version": "2.1.1",
    55  "scripts": {
    66    "build": "wp-scripts build --webpack-src-dir=assets/src",
  • tagsmith/trunk/readme.txt

    r3744485 r3744682  
    55Tested up to: 7.1
    66Requires PHP: 8.1
    7 Stable tag: 2.1.0
     7Stable tag: 2.1.1
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    146146== Changelog ==
    147147
     148= 2.1.1 =
     149* Fixed: the Google sign-in return no longer passes through wp-admin, so sites that send logged-out wp-admin requests to a login or account page (membership and LMS plugins) can connect.
     150
    148151= 2.1.0 =
    149152* New: ad pixels for Meta, Google Ads, TikTok, LinkedIn, Microsoft Advertising, Pinterest, Snap and X, set up in the new Ads & pixels tab and off until you switch one on.
  • tagsmith/trunk/src/Admin/OAuthReturn.php

    r3744428 r3744682  
    1616/**
    1717 * Wires OAuthFlow::receive() and OAuthFlow::finalize() into WordPress.
     18 * The relay's return reaches receive() through maybe_receive() (front end);
     19 * the admin-post actions remain for the BYO redirect and older in-flight returns.
    1820 *
    1921 * CSRF note: these handlers do not use a nonce. The relay's handoff is a
    … …  
    7375         */
    7476        public function register(): void {
     77                add_action( 'init', array( $this, 'maybe_receive' ), 1 );
    7578                add_action( 'admin_post_tagsmith_oauth_return', array( $this, 'receive' ) );
    7679                add_action( 'admin_post_nopriv_tagsmith_oauth_return', array( $this, 'receive' ) );
    7780                add_action( 'admin_init', array( $this, 'maybe_finalize' ) );
     81        }
     82
     83        /**
     84         * Phase 1 on a front-end URL: takes the relay's POST to
     85         * home_url( '/?tagsmith_oauth_return=1' ), which does not pass through
     86         * wp-admin (plugins that redirect logged-out wp-admin requests would
     87         * swallow an admin-post.php return). Anything but a POST with the marker
     88         * is left alone. Same protection as the admin-post path: the single-use
     89         * random state checked in receive().
     90         */
     91        public function maybe_receive(): void {
     92                $method = isset( $_SERVER['REQUEST_METHOD'] ) && is_string( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) : '';
     93
     94                if ( 'POST' !== $method ) {
     95                        return;
     96                }
     97
     98                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- CSRF protection is the single-use random state checked in receive() (see class doc).
     99                if ( ! isset( $_GET['tagsmith_oauth_return'] ) || ! is_string( $_GET['tagsmith_oauth_return'] ) ) {
     100                        return;
     101                }
     102
     103                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- see above.
     104                if ( '1' !== sanitize_text_field( wp_unslash( $_GET['tagsmith_oauth_return'] ) ) ) {
     105                        return;
     106                }
     107
     108                $this->receive();
    78109        }
    79110
  • tagsmith/trunk/src/Auth/OAuthFlow.php

    r3744485 r3744682  
    163163
    164164        /**
    165          * The OAuth return endpoint (also the BYO redirect URI to register).
     165         * The BYO redirect URI users register with Google. Google's return here is
     166         * a logged-in GET, so wp-admin is fine. The relay does not use this URL:
     167         * see relay_return_url().
    166168         */
    167169        public static function return_url(): string {
    … …  
    170172
    171173        /**
     174         * Where the relay hands the browser back: the WordPress address (not
     175         * wp-admin).
     176         *
     177         * The handoff is a cross-site POST without cookies, so WordPress sees a
     178         * logged-out request. Membership, LMS and "hide wp-admin" plugins redirect
     179         * logged-out wp-admin requests to a login or account page before this
     180         * plugin can run, while this URL reaches OAuthReturn::maybe_receive() on
     181         * init. It is built from site_url(), not home_url(): the site address can
     182         * be a headless front end or another origin, whereas site_url() is where
     183         * WordPress runs and shares its origin and path with admin_url(). The relay
     184         * accepts this shape from 2.1.1 on.
     185         */
     186        public static function relay_return_url(): string {
     187                return add_query_arg( 'tagsmith_oauth_return', '1', site_url( '/' ) );
     188        }
     189
     190        /**
    172191         * Starts a connection: creates a single-use state bound to the user and
    173192         * returns the URL to send the browser to (the relay, or Google in BYO mode).
    … …  
    175194         * @param int      $user_id  The user starting the flow.
    176195         * @param string[] $features Subset of ['analytics','searchconsole'].
    177          * @throws ApiError 'https_required' when wp-admin is served over plain
    178          *                  http on a non-local host: the relay and Google both
    179          *                  reject a non-https return URL.
     196         * @throws ApiError 'https_required' when wp-admin (or, for the relay, the
     197         *                  WordPress address) is served over plain http on a
     198         *                  non-local host: the relay and Google both reject a
     199         *                  non-https return URL.
    180200         */
    181201        public function start_url( int $user_id, array $features ): string {
    182                 if ( ! self::secure_return_url() ) {
    183                         throw new ApiError( 'https_required', 'Google sign-in needs an https admin URL.' );
     202                $byo = $this->usable_byo_credentials();
     203
     204                // Google gets return_url(); the relay gets relay_return_url(). Both reject plain http off local hosts.
     205                if ( ! self::secure_url( self::return_url() ) || ( null === $byo && ! self::secure_url( self::relay_return_url() ) ) ) {
     206                        throw new ApiError( 'https_required', 'Google sign-in needs an https admin and WordPress address.' );
    184207                }
    185208
    … …  
    191214                );
    192215
    193                 $byo = $this->usable_byo_credentials();
    194 
    195216                if ( null === $byo ) {
    196217                        set_transient( self::START_PREFIX . $state, $start, 600 );
    … …  
    198219                        $query = array(
    199220                                'state'      => $state,
    200                                 'return_url' => self::return_url(),
     221                                'return_url' => self::relay_return_url(),
    201222                                'scopes'     => implode( ',', $features ),
    202223                                'site'       => home_url(),
    … …  
    684705
    685706        /**
    686          * Whether the OAuth return URL is acceptable to the relay and Google:
     707         * Whether an OAuth return URL is acceptable to the relay and Google:
    687708         * https, or plain http only on a local development host.
    688          */
    689         private static function secure_return_url(): bool {
    690                 $url    = self::return_url();
     709         *
     710         * @param string $url Return URL.
     711         */
     712        private static function secure_url( string $url ): bool {
    691713                $scheme = strtolower( (string) wp_parse_url( $url, PHP_URL_SCHEME ) );
    692714
  • tagsmith/trunk/tagsmith.php

    r3744485 r3744682  
    44 * Plugin URI:        https://shivaatripathi.com/tagsmith/
    55 * Description:       See Google Analytics 4 reports inside WordPress: a clear dashboard, per-post stats and Search Console. Read-only Google access. No nags.
    6  * Version:           2.1.0
     6 * Version:           2.1.1
    77 * Requires at least: 6.7
    88 * Requires PHP:      8.1
    … …  
    1919defined( 'ABSPATH' ) || exit;
    2020
    21 define( 'TAGSMITH_VERSION', '2.1.0' );
     21define( 'TAGSMITH_VERSION', '2.1.1' );
    2222define( 'TAGSMITH_FILE', __FILE__ );
    2323define( 'TAGSMITH_DIR', plugin_dir_path( __FILE__ ) );
Note: See TracChangeset for help on using the changeset viewer.