Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
86.79% |
46 / 53 |
|
77.78% |
7 / 9 |
CRAP | |
0.00% |
0 / 1 |
Login | |
86.79% |
46 / 53 |
|
77.78% |
7 / 9 |
27.56 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
name | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
init | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
login_button | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
authenticate | |
90.48% |
19 / 21 |
|
0.00% |
0 / 1 |
11.10 | |||
user_meta | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
redirect_url | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
state_redirect | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
login_redirect | |
37.50% |
3 / 8 |
|
0.00% |
0 / 1 |
23.62 |
1 | <?php |
2 | /** |
3 | * Login class. |
4 | * |
5 | * This will manage the login flow, which includes adding the |
6 | * google login button on wp-login page, authorizing the user, |
7 | * authenticating user and redirecting him to admin. |
8 | * |
9 | * @package RtCamp\GoogleLogin |
10 | * @since 1.0.0 |
11 | */ |
12 | |
13 | declare(strict_types=1); |
14 | |
15 | namespace RtCamp\GoogleLogin\Modules; |
16 | |
17 | use WP_User; |
18 | use WP_Error; |
19 | use stdClass; |
20 | use Throwable; |
21 | use Exception; |
22 | use RtCamp\GoogleLogin\Utils\Helper; |
23 | use RtCamp\GoogleLogin\Utils\GoogleClient; |
24 | use RtCamp\GoogleLogin\Utils\Authenticator; |
25 | use RtCamp\GoogleLogin\Interfaces\Module as ModuleInterface; |
26 | use function RtCamp\GoogleLogin\plugin; |
27 | |
28 | /** |
29 | * Class Login. |
30 | * |
31 | * @package RtCamp\GoogleLogin\Modules |
32 | */ |
33 | class Login implements ModuleInterface { |
34 | /** |
35 | * Google client instance. |
36 | * |
37 | * @var GoogleClient |
38 | */ |
39 | private $gh_client; |
40 | |
41 | /** |
42 | * Authenticator instance. |
43 | * |
44 | * @var Authenticator |
45 | */ |
46 | private $authenticator; |
47 | |
48 | /** |
49 | * Flag for determining whether the user has been authenticated |
50 | * from plugin. |
51 | * |
52 | * @var bool |
53 | */ |
54 | private $authenticated = false; |
55 | |
56 | /** |
57 | * Login constructor. |
58 | * |
59 | * @param GoogleClient $client GH Client object. |
60 | * @param Authenticator $authenticator Settings object. |
61 | */ |
62 | public function __construct( GoogleClient $client, Authenticator $authenticator ) { |
63 | $this->gh_client = $client; |
64 | $this->authenticator = $authenticator; |
65 | } |
66 | |
67 | /** |
68 | * Module name. |
69 | * |
70 | * @return string |
71 | */ |
72 | public function name(): string { |
73 | return 'login_flow'; |
74 | } |
75 | |
76 | /** |
77 | * Initialize login flow. |
78 | * |
79 | * @return void |
80 | */ |
81 | public function init(): void { |
82 | add_action( 'login_form', [ $this, 'login_button' ] ); |
83 | // Priority is 20 because of issue: https://core.trac.wordpress.org/ticket/46748. |
84 | add_action( 'authenticate', [ $this, 'authenticate' ], 20 ); |
85 | add_action( 'rtcamp.google_register_user', [ $this->authenticator, 'register' ] ); |
86 | add_action( 'rtcamp.google_redirect_url', [ $this, 'redirect_url' ] ); |
87 | add_action( 'rtcamp.google_user_created', [ $this, 'user_meta' ] ); |
88 | add_filter( 'rtcamp.google_login_state', [ $this, 'state_redirect' ] ); |
89 | add_action( 'wp_login', [ $this, 'login_redirect' ] ); |
90 | } |
91 | |
92 | /** |
93 | * Add the login button to login form. |
94 | * |
95 | * @return void |
96 | */ |
97 | public function login_button(): void { |
98 | $template = trailingslashit( plugin()->template_dir ) . 'google-login-button.php'; |
99 | $login_url = plugin()->container()->get( 'gh_client' )->authorization_url(); |
100 | |
101 | Helper::render_template( |
102 | $template, |
103 | [ |
104 | 'login_url' => $login_url, |
105 | ] |
106 | ); |
107 | } |
108 | |
109 | /** |
110 | * Authenticate the user. |
111 | * |
112 | * @param WP_User|null $user User object. Default is null. |
113 | * |
114 | * @return WP_User|WP_Error |
115 | * @throws Exception During authentication. |
116 | */ |
117 | public function authenticate( $user = null ) { |
118 | if ( $user instanceof WP_User ) { |
119 | return $user; |
120 | } |
121 | |
122 | $code = Helper::filter_input( INPUT_GET, 'code', FILTER_SANITIZE_STRING ); |
123 | |
124 | if ( ! $code ) { |
125 | return $user; |
126 | } |
127 | |
128 | $state = Helper::filter_input( INPUT_GET, 'state', FILTER_SANITIZE_STRING ); |
129 | $decoded_state = $state ? (array) ( json_decode( base64_decode( $state ) ) ) : null; |
130 | |
131 | if ( ! is_array( $decoded_state ) || empty( $decoded_state['provider'] ) || 'google' !== $decoded_state['provider'] ) { |
132 | return $user; |
133 | } |
134 | |
135 | if ( empty( $decoded_state['nonce'] ) || ! wp_verify_nonce( $decoded_state['nonce'], 'login_with_google' ) ) { |
136 | return $user; |
137 | } |
138 | |
139 | try { |
140 | $this->gh_client->set_access_token( $code ); |
141 | $user = $this->gh_client->user(); |
142 | $user = $this->authenticator->authenticate( $user ); |
143 | |
144 | if ( $user instanceof WP_User ) { |
145 | $this->authenticated = true; |
146 | |
147 | /** |
148 | * Fires once the user has been authenticated via Google OAuth. |
149 | * |
150 | * @since 1.3.0 |
151 | * |
152 | * @param WP_User $user WP User object. |
153 | */ |
154 | do_action( 'rtcamp.google_user_authenticated', $user ); |
155 | |
156 | return $user; |
157 | } |
158 | |
159 | throw new Exception( __( 'Could not authenticate the user, please try again.', 'login-with-google' ) ); |
160 | |
161 | } catch ( Throwable $e ) { |
162 | return new WP_Error( 'google_login_failed', $e->getMessage() ); |
163 | } |
164 | } |
165 | |
166 | /** |
167 | * Add extra meta information about user. |
168 | * |
169 | * @param int $uid User ID. |
170 | * |
171 | * @return void |
172 | */ |
173 | public function user_meta( int $uid ) { |
174 | add_user_meta( $uid, 'oauth_user', 1, true ); |
175 | add_user_meta( $uid, 'oauth_provider', 'google', true ); |
176 | } |
177 | |
178 | /** |
179 | * Redirect URL. |
180 | * |
181 | * This is useful when redirect URL is present when |
182 | * trying to login to wp-admin. |
183 | * |
184 | * @param string $url Redirect URL address. |
185 | * |
186 | * @return string |
187 | */ |
188 | public function redirect_url( string $url ): string { |
189 | |
190 | return remove_query_arg( 'redirect_to', $url ); |
191 | } |
192 | |
193 | /** |
194 | * Add redirect_to location in state. |
195 | * |
196 | * @param array $state State data. |
197 | * |
198 | * @return array |
199 | */ |
200 | public function state_redirect( array $state ): array { |
201 | $redirect_to = Helper::filter_input( INPUT_GET, 'redirect_to', FILTER_SANITIZE_STRING ); |
202 | /** |
203 | * Filter the default redirect URL in case redirect_to param is not available. |
204 | * Default to admin URL. |
205 | * |
206 | * @param string $admin_url Admin URL address. |
207 | */ |
208 | $state['redirect_to'] = $redirect_to ?? apply_filters( 'rtcamp.google_default_redirect', admin_url() ); |
209 | |
210 | return $state; |
211 | } |
212 | |
213 | /** |
214 | * Add a redirect once user has been authenticated successfully. |
215 | * |
216 | * @return void |
217 | */ |
218 | public function login_redirect(): void { |
219 | $state = Helper::filter_input( INPUT_GET, 'state', FILTER_SANITIZE_STRING ); |
220 | |
221 | if ( ! $state || ! $this->authenticated ) { |
222 | return; |
223 | } |
224 | |
225 | $state = base64_decode( $state ); |
226 | $state = $state ? json_decode( $state ) : null; |
227 | |
228 | if ( ( $state instanceof stdClass ) && ! empty( $state->provider ) && 'google' === $state->provider && ! empty( $state->redirect_to ) ) { |
229 | wp_safe_redirect( $state->redirect_to ); |
230 | exit; |
231 | } |
232 | } |
233 | } |