PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.1.6.9.3
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.1.6.9.3
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.1.6.9.3, at inc/jwt/includes/class-jwt-public.php

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