Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
OneTapLogin
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 7
380
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 init
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 one_tap_prompt
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 one_tap_scripts
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
12
 validate_token
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
56
 authenticate
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * One Tap Login Class.
4 *
5 * This class will be responsible for handling
6 * Google's one tap login for web functioning.
7 *
8 * @package RtCamp\GoogleLogin\Modules
9 * @since 1.0.16
10 */
11
12declare(strict_types=1);
13
14namespace RtCamp\GoogleLogin\Modules;
15
16use Exception;
17use RtCamp\GoogleLogin\Utils\Authenticator;
18use RtCamp\GoogleLogin\Utils\GoogleClient;
19use RtCamp\GoogleLogin\Utils\Helper;
20use RtCamp\GoogleLogin\Interfaces\Module;
21use RtCamp\GoogleLogin\Utils\TokenVerifier;
22use function RtCamp\GoogleLogin\plugin;
23
24/**
25 * Class OneTapLogin
26 *
27 * @package RtCamp\GoogleLogin\Modules
28 */
29class OneTapLogin implements Module {
30    /**
31     * Settings Module.
32     *
33     * @var Settings
34     */
35    private $settings;
36
37    /**
38     * Token verifier.
39     *
40     * @var TokenVerifier
41     */
42    private $token_verifier;
43
44    /**
45     * Google client instance.
46     *
47     * @var GoogleClient
48     */
49    private $google_client;
50
51    /**
52     * Authenticator service.
53     *
54     * @var Authenticator
55     */
56    private $authenticator;
57
58    /**
59     * OneTapLogin constructor.
60     *
61     * @param Settings      $settings Settings object.
62     * @param TokenVerifier $verifier Token verifier object.
63     * @param GoogleClient  $client   Google client instance.
64     * @param Authenticator $authenticator Authenticator service instance.
65     */
66    public function __construct( Settings $settings, TokenVerifier $verifier, GoogleClient $client, Authenticator $authenticator ) {
67        $this->settings       = $settings;
68        $this->token_verifier = $verifier;
69        $this->google_client  = $client;
70        $this->authenticator  = $authenticator;
71    }
72
73    /**
74     * Module name.
75     *
76     * @return string
77     */
78    public function name(): string {
79        return 'one_tap_login';
80    }
81
82    /**
83     * Module Initialization activity.
84     *
85     * Everything will happen if and only if one tap is active in settings.
86     */
87    public function init(): void {
88        if ( $this->settings->one_tap_login ) {
89            add_action( 'login_enqueue_scripts', [ $this, 'one_tap_scripts' ] );
90            add_action( 'login_footer', [ $this, 'one_tap_prompt' ] );
91            add_action( 'wp_ajax_nopriv_validate_id_token', [ $this, 'validate_token' ] );
92            add_action( 'rtcamp.id_token_verified', [ $this, 'authenticate' ] );
93            add_action(
94                'init',
95                function () {
96                    if ( ! is_user_logged_in() ) {
97                        $hook_prefix = ( 'sitewide' === $this->settings->one_tap_login_screen ) ? 'wp' : 'login';
98                        add_action( $hook_prefix . '_enqueue_scripts', [ $this, 'one_tap_scripts' ] );
99                        add_action( $hook_prefix . '_footer', [ $this, 'one_tap_prompt' ], 10000 );
100                    }
101                }
102            );
103        }
104    }
105
106    /**
107     * Show one tap prompt markup.
108     *
109     * @return void
110     */
111    public function one_tap_prompt(): void { ?>
112        <div id="g_id_onload" data-client_id="<?php echo esc_attr( $this->settings->client_id ); ?>" data-login_uri="<?php echo esc_attr( wp_login_url() ); ?>" data-callback="LoginWithGoogleDataCallBack"></div>
113        <?php
114    }
115
116    /**
117     * Enqueue one-tap related scripts.
118     *
119     * @return void
120     */
121    public function one_tap_scripts(): void {
122        $filename = ( defined( 'WP_SCRIPT_DEBUG' ) && true === WP_SCRIPT_DEBUG ) ? 'onetap.min.js' : 'onetap.js';
123
124        wp_enqueue_script(
125            'login-with-google-one-tap',
126            'https://accounts.google.com/gsi/client',
127            [],
128            filemtime( trailingslashit( plugin()->path ) . 'assets/build/js/onetap.js' ),
129            true
130        );
131
132        $data = [
133            'ajaxurl' => admin_url( 'admin-ajax.php' ),
134            'state'   => $this->google_client->state(),
135            'homeurl' => get_option( 'home', '' ),
136        ];
137
138        wp_register_script(
139            'login-with-google-one-tap-js',
140            trailingslashit( plugin()->url ) . 'assets/build/js/' . $filename,
141            [
142                'wp-i18n',
143            ],
144            filemtime( trailingslashit( plugin()->path ) . 'assets/build/js/onetap.js' ),
145            true
146        );
147
148        wp_add_inline_script(
149            'login-with-google-one-tap-js',
150            'var TempAccessOneTap=' . json_encode( $data ), //phpcs:disable WordPress.WP.AlternativeFunctions.json_encode_json_encode
151            'before'
152        );
153
154        wp_enqueue_script( 'login-with-google-one-tap-js' );
155    }
156
157    /**
158     * Validate the ID token.
159     *
160     * @return void
161     * @throws Exception Credential verification failure exception.
162     */
163    public function validate_token(): void {
164        try {
165            $token    = Helper::filter_input( INPUT_POST, 'token', FILTER_SANITIZE_STRING );
166            $verified = $this->token_verifier->verify_token( $token );
167
168            if ( ! $verified ) {
169                throw new Exception( __( 'Cannot verify the credentials', 'login-with-google' ) );
170            }
171
172            /**
173             * Do something when token has been verified successfully.
174             *
175             * If we are here that means ID token has been verified.
176             *
177             * @since 1.0.16
178             */
179            do_action( 'rtcamp.id_token_verified' );
180
181            $redirect_to   = apply_filters( 'rtcamp.google_default_redirect', admin_url() );
182            $state         = Helper::filter_input( INPUT_POST, 'state', FILTER_SANITIZE_STRING );
183            $decoded_state = $state ? (array) ( json_decode( base64_decode( $state ) ) ) : null;
184
185            if ( is_array( $decoded_state ) && ! empty( $decoded_state['provider'] ) && 'google' === $decoded_state['provider'] ) {
186                $redirect_to = $decoded_state['redirect_to'] ?? $redirect_to;
187            }
188
189            wp_send_json_success(
190                [
191                    'redirect' => $redirect_to,
192                ]
193            );
194            die;
195
196        } catch ( Exception $e ) {
197            wp_send_json_error( $e->getMessage() );
198        }
199    }
200
201    /**
202     * Authenticate the user in WordPress.
203     *
204     * @return void
205     * @throws Exception Authentication exception.
206     */
207    public function authenticate(): void {
208        $user = $this->token_verifier->current_user();
209
210        if ( is_null( $user ) ) {
211            throw new Exception( __( 'User not found to authenticate', 'login-with-google' ) );
212        }
213
214        $wp_user = $this->authenticator->authenticate( $user );
215        $this->authenticator->set_auth_cookies( $wp_user );
216    }
217}