PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.8
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
learnpress / inc / jwt / includes / class-jwt-public.php

class-jwt-public.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.8, at inc/jwt/includes/class-jwt-public.php

393 lines 10.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use LP\Firebase\JWT\JWT;
4
5 /**
6 * REST API: LP_Jwt_Public
7 *
8 * @package LPJWTAuth
9 * @since 1.0.0
10 * @author Nhamdv <daonham95@gmail.com>
11 */
12
13 class LP_Jwt_Public {
14 private $name;
15
16 private $version;
17
18 private $namespace;
19
20 private $jwt_error;
21
22 private $secret_key;
23
24 public function __construct( $name, $version ) {
25 $this->name = $name;
26 $this->version = $version;
27 $this->namespace = $this->name . '/' . $this->version;
28
29 if ( ! defined( 'SECURE_AUTH_KEY' ) && ! defined( 'LP_SECURE_AUTH_KEY' ) ) {
30 return;
31 }
32
33 $this->secret_key = defined( 'LP_SECURE_AUTH_KEY' ) ? LP_SECURE_AUTH_KEY : SECURE_AUTH_KEY;
34 }
35
36 public function register_routes() {
37 register_rest_route(
38 $this->namespace,
39 'token',
40 array(
41 'methods' => WP_REST_Server::CREATABLE,
42 'callback' => array( $this, 'generate_token' ),
43 'args' => array(
44 'username' => array(
45 'description' => esc_html__( 'The username of the user.', 'learnpress' ),
46 'type' => 'string',
47 'sanitize_callback' => 'sanitize_text_field',
48 'validate_callback' => 'rest_validate_request_arg',
49 ),
50 'password' => array(
51 'description' => esc_html__( 'The password of the user.', 'learnpress' ),
52 'type' => 'string',
53 'sanitize_callback' => 'sanitize_text_field',
54 'validate_callback' => 'rest_validate_request_arg',
55 ),
56 ),
57 'schema' => array( $this, 'get_item_schema' ),
58 'permission_callback' => '__return_true',
59 )
60 );
61
62 register_rest_route(
63 $this->namespace,
64 'token/validate',
65 array(
66 'methods' => WP_REST_Server::CREATABLE,
67 'callback' => array( $this, 'validate_token' ),
68 'permission_callback' => '__return_true',
69 )
70 );
71
72 register_rest_route(
73 $this->namespace,
74 'token/register',
75 array(
76 'methods' => WP_REST_Server::CREATABLE,
77 'callback' => array( $this, 'register' ),
78 'permission_callback' => '__return_true',
79 )
80 );
81 }
82
83 public function get_item_schema() {
84 $schema = array(
85 '$schema' => 'http://json-schema.org/draft-04/schema#',
86 'title' => esc_html__( 'JSON Web Token', 'learnpress' ),
87 'type' => 'object',
88 'properties' => array(
89 'token' => array(
90 'description' => esc_html__( 'JSON Web Token.', 'learnpress' ),
91 'type' => 'string',
92 'readonly' => true,
93 ),
94 'user_id' => array(
95 'description' => esc_html__( 'The ID of the user.', 'learnpress' ),
96 'type' => 'integer',
97 'readonly' => true,
98 ),
99 'user_login' => array(
100 'description' => esc_html__( 'The username of the user', 'learnpress' ),
101 'type' => 'string',
102 'readonly' => true,
103 ),
104 'user_email' => array(
105 'description' => esc_html__( 'The email address of the user.', 'learnpress' ),
106 'type' => 'string',
107 'readonly' => true,
108 ),
109 ),
110 );
111
112 return apply_filters( 'lp_rest_authentication_token_schema', $schema );
113 }
114
115 /**
116 * Add CORs support to the request.
117 */
118 public function add_cors_support() {
119 $enable_cors = defined( 'LP_JWT_AUTH_CORS_ENABLE' ) ? LP_JWT_AUTH_CORS_ENABLE : false;
120
121 if ( $enable_cors ) {
122 $headers = apply_filters( 'lp_jwt_auth_cors_allow_headers', 'Access-Control-Allow-Headers, Content-Type, Authorization' );
123 header( sprintf( 'Access-Control-Allow-Headers: %s', $headers ) );
124 }
125 }
126
127 public function register( WP_REST_Request $request ) {
128 $username = $request->get_param( 'username' );
129 $password = $request->get_param( 'password' );
130 $confirm_password = $request->get_param( 'confirm_password' );
131 $email = $request->get_param( 'email' );
132
133 $customer_id = LP_Forms_Handler::learnpress_create_new_customer( $email, $username, $password, $confirm_password );
134
135 if ( is_wp_error( $customer_id ) ) {
136 return new WP_Error(
137 $customer_id->get_error_code(),
138 $customer_id->get_error_message(),
139 array(
140 'status' => 403,
141 )
142 );
143 }
144
145 return $this->generate_token( $request );
146 }
147
148 public function generate_token( WP_REST_Request $request ) {
149 $secret_key = $this->secret_key;
150 $username = $request->get_param( 'username' );
151 $password = $request->get_param( 'password' );
152
153 if ( ! $secret_key ) {
154 return new WP_Error(
155 'lp_jwt_auth_bad_config',
156 esc_html__( 'LearnPress JWT is not configurated properly, please contact the admin', 'learnpress' ),
157 array(
158 'status' => 403,
159 )
160 );
161 }
162
163 /** Try to authenticate the user with the passed credentials*/
164 $user = wp_authenticate( $username, $password );
165
166 /** If the authentication fails return a error*/
167 if ( is_wp_error( $user ) ) {
168 $error_code = $user->get_error_code();
169
170 return new WP_Error(
171 '[lp_jwt_auth] ' . $error_code,
172 $user->get_error_message( $error_code ),
173 array(
174 'status' => 403,
175 )
176 );
177 }
178
179 /** Valid credentials, the user exists create the according Token */
180 $issued_at = time();
181 $not_before = apply_filters( 'lp_jwt_auth_not_before', $issued_at, $issued_at );
182 $expire = apply_filters( 'lp_jwt_auth_expire', $issued_at + WEEK_IN_SECONDS, $issued_at );
183
184 $token = array(
185 'iss' => get_bloginfo( 'url' ),
186 'iat' => $issued_at,
187 'nbf' => $not_before,
188 'exp' => $expire,
189 'data' => array(
190 'user' => array(
191 'id' => $user->data->ID,
192 ),
193 ),
194 );
195
196 /** Let the user modify the token data before the sign. */
197 $token = JWT::encode( apply_filters( 'lp_jwt_auth_token_before_sign', $token, $user ), $secret_key );
198
199 /** The token is signed, now create the object with no sensible user data to the client*/
200 $data = array(
201 'token' => $token,
202 'user_id' => $user->data->ID,
203 'user_login' => $user->data->user_login,
204 'user_email' => $user->data->user_email,
205 'user_display_name' => $user->data->display_name,
206 );
207
208 return apply_filters( 'lp_jwt_auth_token_before_dispatch', $data, $user );
209 }
210
211 /**
212 * This is our Middleware to try to authenticate the user according to the
213 * token send.
214 *
215 * @param (int|bool) $user Logged User ID
216 *
217 * @return (int|bool)
218 */
219 public function determine_current_user( $user_id ) {
220 $rest_prefix = trailingslashit( rest_get_url_prefix() );
221 $request_uri = esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) );
222 $valid_api_uri = strpos( $request_uri, $rest_prefix . $this->name . '/' );
223
224 /**
225 * Only check when rest url has wp-json/learnpress/.
226 */
227 if ( ! empty( $user_id ) || $valid_api_uri === false ) {
228 return $user_id;
229 }
230
231 /*
232 * if the request URI is for validate the token don't do anything,
233 * this avoid double calls to the validate_token function.
234 */
235 $validate_token = strpos( $request_uri, '/token' );
236
237 /** All course is public so donot need token */
238 $is_rest_courses = strpos( $request_uri, '/courses' ) || strpos( $request_uri, '/reset-password' ) || strpos( $request_uri, '/course_category' ) || strpos( $request_uri, '/sections/' ) || strpos( $request_uri, '/section-items/' ) || strpos( $request_uri, '/users' );
239
240 if ( $validate_token > 0 ) {
241 return $user_id;
242 }
243
244 $token = $this->validate_token( false );
245
246 if ( is_wp_error( $token ) ) {
247 if ( ! $is_rest_courses ) {
248 $this->jwt_error = $token;
249 }
250
251 return $user_id;
252 }
253
254 return $token->data->user->id;
255 }
256
257 public function validate_token( $output = true ) {
258 /*
259 * Looking for the HTTP_AUTHORIZATION header, if not present just
260 * return the user.
261 */
262 $auth = isset( $_SERVER['HTTP_AUTHORIZATION'] ) ? sanitize_text_field( $_SERVER['HTTP_AUTHORIZATION'] ) : false;
263
264 /* Double check for different auth header string (server dependent) */
265 if ( ! $auth ) {
266 $auth = isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ? sanitize_text_field( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) : false;
267 }
268
269 if ( ! $auth ) {
270 return new WP_Error(
271 'lp_jwt_auth_no_auth_header',
272 esc_html__( 'Authorization header not found.', 'learnpress' ),
273 array(
274 'status' => 401,
275 )
276 );
277 }
278
279 /*
280 * The HTTP_AUTHORIZATION is present verify the format
281 * if the format is wrong return the user.
282 */
283 list( $token ) = sscanf( $auth, 'Bearer %s' );
284
285 if ( ! $token ) {
286 return new WP_Error(
287 'lp_jwt_auth_bad_auth_header',
288 esc_html__( 'Authentication token is missing.', 'learnpress' ),
289 array(
290 'status' => 401,
291 )
292 );
293 }
294
295 /** Get the Secret Key */
296 $secret_key = $this->secret_key;
297
298 if ( ! $secret_key ) {
299 return new WP_Error(
300 'lp_jwt_auth_bad_config',
301 esc_html__( 'LearnPress JWT is not configurated properly, please contact the admin', 'learnpress' ),
302 array(
303 'status' => 401,
304 )
305 );
306 }
307
308 /** Try to decode the token */
309 try {
310 $token = JWT::decode( $token, $secret_key, array( 'HS256' ) );
311
312 /** The Token is decoded now validate the iss */
313 if ( $token->iss != get_bloginfo( 'url' ) ) {
314 return new WP_Error(
315 'lp_jwt_auth_bad_iss',
316 esc_html__( 'The iss do not match with this server', 'learnpress' ),
317 array(
318 'status' => 401,
319 )
320 );
321 }
322
323 /** So far so good, validate the user id in the token */
324 if ( ! isset( $token->data->user->id ) ) {
325 return new WP_Error(
326 'lp_jwt_auth_bad_request',
327 esc_html__( 'User ID not found in the token', 'learnpress' ),
328 array(
329 'status' => 401,
330 )
331 );
332 }
333
334 if ( ! isset( $token->exp ) ) {
335 return new WP_Error(
336 'rest_authentication_missing_token_expiration',
337 esc_html__( 'The token must have an expiration date.', 'learnpress' ),
338 array(
339 'status' => 401,
340 )
341 );
342 }
343
344 if ( time() > $token->exp ) {
345 return new WP_Error(
346 'rest_authentication_token_expired',
347 esc_html__( 'The token has expired.', 'learnpress' ),
348 array(
349 'status' => 401,
350 )
351 );
352 }
353
354 /** Everything looks good return the decoded token if the $output is false */
355 if ( ! $output ) {
356 return $token;
357 }
358
359 /** If the output is true return an answer to the request to show it */
360 return array(
361 'code' => 'lp_jwt_auth_valid_token',
362 'message' => esc_html__( 'Valid access token.', 'learnpress' ),
363 'data' => array(
364 'status' => 200,
365 'exp' => $token->exp - time(),
366 ),
367 );
368 } catch ( Exception $e ) {
369 return new WP_Error(
370 'lp_jwt_auth_invalid_token',
371 $e->getMessage(),
372 array(
373 'status' => 401,
374 )
375 );
376 }
377 }
378
379 /**
380 * Filter to hook the rest_pre_dispatch, if the is an error in the request
381 * send it, if there is no error just continue with the current request.
382 *
383 * @param $request
384 */
385 public function rest_pre_dispatch( $request ) {
386 if ( is_wp_error( $this->jwt_error ) ) {
387 return $this->jwt_error;
388 }
389
390 return $request;
391 }
392 }
393