Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.38% covered (success)
90.38%
47 / 52
71.43% covered (success)
71.43%
5 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
OneTapLogin
90.38% covered (success)
90.38%
47 / 52
71.43% covered (success)
71.43%
5 / 7
19.32
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 name
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 init
63.64% covered (warning)
63.64%
7 / 11
0.00% covered (danger)
0.00%
0 / 1
4.77
 one_tap_prompt
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 one_tap_scripts
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
3
 validate_token
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
7.01
 authenticate
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
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"
113             data-client_id="<?php echo esc_html( $this->settings->client_id ); ?>"
114             data-login_uri="<?php echo esc_html( wp_login_url() ); ?>"
115             data-callback="LoginWithGoogleDataCallBack"
116        ></div>
117        <?php
118    }
119
120    /**
121     * Enqueue one-tap related scripts.
122     *
123     * @return void
124     */
125    public function one_tap_scripts(): void {
126        $filename = ( defined( 'WP_SCRIPT_DEBUG' ) && true === WP_SCRIPT_DEBUG ) ? 'onetap.min.js' : 'onetap.js';
127
128        wp_enqueue_script(
129            'login-with-google-one-tap',
130            'https://accounts.google.com/gsi/client',
131            [],
132            filemtime( trailingslashit( plugin()->path ) . 'assets/build/js/onetap.js' ),
133            true
134        );
135
136        $data = [
137            'ajaxurl' => admin_url( 'admin-ajax.php' ),
138            'state'   => $this->google_client->state(),
139            'homeurl' => get_option( 'home', '' ),
140        ];
141
142        wp_register_script(
143            'login-with-google-one-tap-js',
144            trailingslashit( plugin()->url ) . 'assets/build/js/' . $filename,
145            [
146                'wp-i18n',
147            ],
148            filemtime( trailingslashit( plugin()->path ) . 'assets/build/js/onetap.js' ),
149            true
150        );
151
152        wp_add_inline_script(
153            'login-with-google-one-tap-js',
154            'var TempAccessOneTap=' . json_encode( $data ), //phpcs:disable WordPress.WP.AlternativeFunctions.json_encode_json_encode
155            'before'
156        );
157
158        wp_enqueue_script( 'login-with-google-one-tap-js' );
159    }
160
161    /**
162     * Validate the ID token.
163     *
164     * @return void
165     * @throws Exception Credential verification failure exception.
166     */
167    public function validate_token(): void {
168        try {
169            $token    = Helper::filter_input( INPUT_POST, 'token', FILTER_SANITIZE_STRING );
170            $verified = $this->token_verifier->verify_token( $token );
171
172            if ( ! $verified ) {
173                throw new Exception( __( 'Cannot verify the credentials', 'login-with-google' ) );
174            }
175
176            /**
177             * Do something when token has been verified successfully.
178             *
179             * If we are here that means ID token has been verified.
180             *
181             * @since 1.0.16
182             */
183            do_action( 'rtcamp.id_token_verified' );
184
185            $redirect_to   = apply_filters( 'rtcamp.google_default_redirect', admin_url() );
186            $state         = Helper::filter_input( INPUT_POST, 'state', FILTER_SANITIZE_STRING );
187            $decoded_state = $state ? (array) ( json_decode( base64_decode( $state ) ) ) : null;
188
189            if ( is_array( $decoded_state ) && ! empty( $decoded_state['provider'] ) && 'google' === $decoded_state['provider'] ) {
190                $redirect_to = $decoded_state['redirect_to'] ?? $redirect_to;
191            }
192
193            wp_send_json_success(
194                [
195                    'redirect' => $redirect_to,
196                ]
197            );
198            die;
199
200        } catch ( Exception $e ) {
201            wp_send_json_error( $e->getMessage() );
202        }
203    }
204
205    /**
206     * Authenticate the user in WordPress.
207     *
208     * @return void
209     * @throws Exception Authentication exception.
210     */
211    public function authenticate(): void {
212        $user = $this->token_verifier->current_user();
213
214        if ( is_null( $user ) ) {
215            throw new Exception( __( 'User not found to authenticate', 'login-with-google' ) );
216        }
217
218        $wp_user = $this->authenticator->authenticate( $user );
219        $this->authenticator->set_auth_cookies( $wp_user );
220    }
221}