Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
85.42% |
41 / 48 |
|
77.78% |
7 / 9 |
CRAP | |
0.00% |
0 / 1 |
Login | |
85.42% |
41 / 48 |
|
77.78% |
7 / 9 |
28.10 | |
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% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
authenticate | |
90.00% |
18 / 20 |
|
0.00% |
0 / 1 |
11.12 | |||
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 | add_action( 'authenticate', [ $this, 'authenticate' ] ); |
84 | add_action( 'rtcamp.google_register_user', [ $this->authenticator, 'register' ] ); |
85 | add_action( 'rtcamp.google_redirect_url', [ $this, 'redirect_url' ] ); |
86 | add_action( 'rtcamp.google_user_created', [ $this, 'user_meta' ] ); |
87 | add_filter( 'rtcamp.google_login_state', [ $this, 'state_redirect' ] ); |
88 | add_action( 'wp_login', [ $this, 'login_redirect' ] ); |
89 | } |
90 | |
91 | /** |
92 | * Add the login button to login form. |
93 | * |
94 | * @return void |
95 | */ |
96 | public function login_button(): void { |
97 | $template = trailingslashit( plugin()->template_dir ) . 'google-login-button.php'; |
98 | $login_url = plugin()->container()->get( 'gh_client' )->authorization_url(); |
99 | |
100 | Helper::render_template( |
101 | $template, |
102 | [ |
103 | 'login_url' => $login_url, |
104 | ] |
105 | ); |
106 | } |
107 | |
108 | /** |
109 | * Authenticate the user. |
110 | * |
111 | * @param WP_User|null $user User object. Default is null. |
112 | * |
113 | * @return WP_User|WP_Error |
114 | * @throws Exception During authentication. |
115 | */ |
116 | public function authenticate( $user = null ) { |
117 | if ( $user instanceof WP_User ) { |
118 | return $user; |
119 | } |
120 | |
121 | $code = Helper::filter_input( INPUT_GET, 'code' ); |
122 | |
123 | if ( ! $code ) { |
124 | return $user; |
125 | } |
126 | |
127 | $state = Helper::filter_input( INPUT_GET, 'state' ); |
128 | $decoded_state = $state ? (array) ( json_decode( base64_decode( $state ) ) ) : null; |
129 | |
130 | if ( ! is_array( $decoded_state ) || empty( $decoded_state['provider'] ) || 'google' !== $decoded_state['provider'] ) { |
131 | return $user; |
132 | } |
133 | |
134 | if ( empty( $decoded_state['nonce'] ) || ! wp_verify_nonce( $decoded_state['nonce'], 'login_with_google' ) ) { |
135 | return $user; |
136 | } |
137 | |
138 | try { |
139 | $this->gh_client->set_access_token( $code ); |
140 | $user = $this->gh_client->user(); |
141 | $user = $this->authenticator->authenticate( $user ); |
142 | |
143 | if ( $user instanceof WP_User ) { |
144 | $this->authenticated = true; |
145 | |
146 | return $user; |
147 | } |
148 | |
149 | throw new Exception( __( 'Could not authenticate the user, please try again.', 'login-with-google' ) ); |
150 | |
151 | } catch ( Throwable $e ) { |
152 | return new WP_Error( 'google_login_failed', $e->getMessage() ); |
153 | } |
154 | } |
155 | |
156 | /** |
157 | * Add extra meta information about user. |
158 | * |
159 | * @param int $uid User ID. |
160 | * |
161 | * @return void |
162 | */ |
163 | public function user_meta( int $uid ) { |
164 | add_user_meta( $uid, 'oauth_user', 1, true ); |
165 | add_user_meta( $uid, 'oauth_provider', 'google', true ); |
166 | } |
167 | |
168 | /** |
169 | * Redirect URL. |
170 | * |
171 | * This is useful when redirect URL is present when |
172 | * trying to login to wp-admin. |
173 | * |
174 | * @param string $url Redirect URL address. |
175 | * |
176 | * @return string |
177 | */ |
178 | public function redirect_url( string $url ): string { |
179 | |
180 | return remove_query_arg( 'redirect_to', $url ); |
181 | } |
182 | |
183 | /** |
184 | * Add redirect_to location in state. |
185 | * |
186 | * @param array $state State data. |
187 | * |
188 | * @return array |
189 | */ |
190 | public function state_redirect( array $state ): array { |
191 | $redirect_to = Helper::filter_input( INPUT_GET, 'redirect_to' ); |
192 | /** |
193 | * Filter the default redirect URL in case redirect_to param is not available. |
194 | * Default to admin URL. |
195 | * |
196 | * @param string $admin_url Admin URL address. |
197 | */ |
198 | $state['redirect_to'] = $redirect_to ?? apply_filters( 'rtcamp.google_default_redirect', admin_url() ); |
199 | |
200 | return $state; |
201 | } |
202 | |
203 | /** |
204 | * Add a redirect once user has been authenticated successfully. |
205 | * |
206 | * @return void |
207 | */ |
208 | public function login_redirect(): void { |
209 | $state = Helper::filter_input( INPUT_GET, 'state' ); |
210 | |
211 | if ( ! $state || ! $this->authenticated ) { |
212 | return; |
213 | } |
214 | |
215 | $state = base64_decode( $state ); |
216 | $state = $state ? json_decode( $state ) : null; |
217 | |
218 | if ( ( $state instanceof stdClass ) && ! empty( $state->provider ) && 'google' === $state->provider && ! empty( $state->redirect_to ) ) { |
219 | wp_safe_redirect( $state->redirect_to ); |
220 | exit; |
221 | } |
222 | } |
223 | } |