diff --git a/WP_Auth0.php b/WP_Auth0.php index 0b5696fe..7f8af345 100644 --- a/WP_Auth0.php +++ b/WP_Auth0.php @@ -2,7 +2,7 @@ /** * Plugin Name: Wordpress Auth0 Integration * Description: Implements the Auth0 Single Sign On solution into Wordpress - * Version: 1.2.5 + * Version: 1.2.6 * Author: Auth0 * Author URI: https://auth0.com */ @@ -12,10 +12,10 @@ define('WPA0_PLUGIN_URL', trailingslashit(plugin_dir_url(__FILE__) )); define('WPA0_LANG', 'wp-auth0'); define('AUTH0_DB_VERSION', 2); -define('WPA0_VERSION', '1.2.5'); +define('WPA0_VERSION', '1.2.6'); class WP_Auth0 { - public static function init(){ + public static function init(){ spl_autoload_register(array(__CLASS__, 'autoloader')); // WP_Auth0_Referer_Check::init(); @@ -63,6 +63,7 @@ public static function init(){ add_action( 'wp_footer', array( __CLASS__, 'a0_render_message' ) ); } + WP_Auth0_UsersRepo::init(); WP_Auth0_Settings_Section::init(); WP_Auth0_Admin::init(); WP_Auth0_ErrorLog::init(); @@ -99,7 +100,7 @@ public static function isJWTConfigured() { JWT_AUTH_Options::get('aud') == WP_Auth0_Options::get('client_id') && JWT_AUTH_Options::get('secret') == WP_Auth0_Options::get('client_secret') && JWT_AUTH_Options::get('secret_base64_encoded') && - JWT_AUTH_Options::get('override_user_repo') == 'WP_Auth0_UsersRepo' && + WP_Auth0_Options::get('jwt_auth_integration') && JWT_AUTH_Options::get('jwt_attribute') == 'sub' ); @@ -387,7 +388,7 @@ public static function init_auth0(){ } $userinfo = json_decode( $response['body'] ); - if (self::login_user($userinfo, $data)) { + if (self::login_user($userinfo, $data->id_token)) { if ($stateFromGet !== null && isset($stateFromGet->interim) && $stateFromGet->interim) { include WPA0_PLUGIN_DIR . 'templates/login-interim.php'; exit(); @@ -455,23 +456,6 @@ private static function findAuth0User($id) { return $user; } - private static function insertAuth0User($userinfo, $user_id) { - global $wpdb; - $wpdb->insert( - $wpdb->auth0_user, - array( - 'auth0_id' => $userinfo->user_id, - 'wp_id' => $user_id, - 'auth0_obj' => serialize($userinfo) - ), - array( - '%s', - '%d', - '%s' - ) - ); - } - public static function insertAuth0Error($section, $wp_error) { if ($wp_error instanceof WP_Error) { @@ -519,11 +503,11 @@ public static function delete_user ($user_id) { $wpdb->delete( $wpdb->auth0_user, array( 'wp_id' => $user_id), array( '%d' ) ); } - private static function dieWithVerifyEmail($userinfo, $data) { + private static function dieWithVerifyEmail($userinfo, $id_token) { ob_start(); $domain = WP_Auth0_Options::get( 'domain' ); - $token = $data->id_token; + $token = $id_token; $email = $userinfo->email; $connection = $userinfo->identities[0]->connection; $userId = $userinfo->user_id; @@ -534,7 +518,7 @@ private static function dieWithVerifyEmail($userinfo, $data) { wp_die($html); } - private static function login_user( $userinfo, $data ){ + public static function login_user( $userinfo, $id_token ){ // If the userinfo has no email or an unverified email, and in the options we require a verified email // notify the user he cant login until he does so. $requires_verified_email = WP_Auth0_Options::get( 'requires_verified_email' ); @@ -548,10 +532,8 @@ private static function login_user( $userinfo, $data ){ wp_die($msg); } - - if (!$userinfo->email_verified) { - self::dieWithVerifyEmail($userinfo, $data); + self::dieWithVerifyEmail($userinfo, $id_token); } } @@ -564,69 +546,31 @@ private static function login_user( $userinfo, $data ){ wp_set_auth_cookie( $user->ID ); return true; } else { - // If the user doesn't exist we need to either create a new one, or asign him to an existing one - $isDatabaseUser = false; - foreach ($userinfo->identities as $identity) { - if ($identity->provider == "auth0") { - $isDatabaseUser = true; - } - } - $joinUser = null; - // If the user has a verified email or is a database user try to see if there is - // a user to join with. The isDatabase is because we don't want to allow database - // user creation if there is an existing one with no verified email - - if (isset($userinfo->email) && ((isset($userinfo->email_verified) && $userinfo->email_verified) || $isDatabaseUser)) { - $joinUser = get_user_by( 'email', $userinfo->email ); - } - // $auto_provisioning = WP_Auth0_Options::get('auto_provisioning'); - // $allow_signup = WP_Auth0_Options::is_wp_registration_enabled() || $auto_provisioning; - $allow_signup = WP_Auth0_Options::is_wp_registration_enabled(); + try { + $creator = new WP_Auth0_UserCreator(); + $user_id = $creator->create($userinfo, $id_token); - if (!is_null($joinUser) && $joinUser instanceof WP_User) { - // If we are here, we have a potential join user - // Don't allow creation or assignation of user if the email is not verified, that would - // be hijacking - if (!$userinfo->email_verified) { - self::dieWithVerifyEmail($userinfo, $data); - } - $user_id = $joinUser->ID; - } elseif ($allow_signup) { - // If we are here, we need to create the user - $user_id = WP_Auth0_Users::create_user($userinfo); - - // Check if user was created - - if( is_wp_error($user_id) ) { - $msg = __('Error: Could not create user.', WPA0_LANG); - $msg = ' ' . $user_id->get_error_message(); - $msg .= '

'; - $msg .= '' . __('← Go back', WPA0_LANG) . ''; - wp_die($msg); - - }elseif($user_id == -2){ - $msg = __('Error: Could not create user. The registration process were rejected. Please verify that your account is whitelisted for this system.', WPA0_LANG); - $msg .= '

'; - $msg .= '' . __('← Go back', WPA0_LANG) . ''; - - wp_die($msg); - }elseif ($user_id <0){ - $msg = __('Error: Could not create user.', WPA0_LANG); - $msg .= '

'; - $msg .= '' . __('← Go back', WPA0_LANG) . ''; - wp_die($msg); - } - } else { + wp_set_auth_cookie( $user_id ); + } + catch (WP_Auth0_CouldNotCreateUserException $e) { + $msg = __('Error: Could not create user.', WPA0_LANG); + $msg = ' ' . $e->getMessage(); + $msg .= '

'; + $msg .= '' . __('← Go back', WPA0_LANG) . ''; + wp_die($msg); + } + catch (WP_Auth0_RegistrationNotEnabledException $e) { $msg = __('Error: Could not create user. The registration process is not available.', WPA0_LANG); $msg .= '

'; $msg .= '' . __('← Go back', WPA0_LANG) . ''; wp_die($msg); } - // If we are here we should have a valid $user_id with a new user or an existing one - // log him in, and update the auth0_user table - self::insertAuth0User($userinfo, $user_id); - wp_set_auth_cookie( $user_id ); + catch (WP_Auth0_EmailNotVerifiedException $e) { + self::dieWithVerifyEmail($e->userinfo, $e->id_token); + } + + return true; } } @@ -787,6 +731,7 @@ private static function autoloader($class){ $paths[] = $path; $paths[] = $path.'lib/'; + $paths[] = $path.'lib/exceptions/'; foreach($paths as $p) foreach($exts as $ext){ diff --git a/lib/WP_Auth0_Admin.php b/lib/WP_Auth0_Admin.php index 0dab80b9..13764150 100755 --- a/lib/WP_Auth0_Admin.php +++ b/lib/WP_Auth0_Admin.php @@ -72,7 +72,7 @@ public static function init_admin(){ /* ------------------------- ADVANCED ------------------------- */ - self::init_option_section('Advanced', array( + $advancedOptions = array( array('id' => 'wpa0_dict', 'name' => 'Translation', 'function' => 'render_dict'), array('id' => 'wpa0_username_style', 'name' => 'Username style', 'function' => 'render_username_style'), @@ -89,7 +89,16 @@ public static function init_admin(){ array('id' => 'wpa0_extra_conf', 'name' => 'Extra settings', 'function' => 'render_extra_conf'), array('id' => 'wpa0_cdn_url', 'name' => 'Widget URL', 'function' => 'render_cdn_url'), - )); + ); + + + + if (WP_Auth0::isJWTAuthEnabled()) { + $advancedOptions[] = array('id' => 'wpa0_jwt_auth_integration', 'name' => 'Enable JWT Auth integration', 'function' => 'render_jwt_auth_integration'); + } + + self::init_option_section('Advanced', $advancedOptions); + register_setting(WP_Auth0_Options::OPTIONS_NAME, WP_Auth0_Options::OPTIONS_NAME, array(__CLASS__, 'input_validator')); } @@ -107,6 +116,11 @@ public static function render_remember_last_login () { echo ''; echo '
' . __('Request for SSO data and enable Last time you signed in with[...] message.', WPA0_LANG) . '' . __('More info', WPA0_LANG) . ''; } + public static function render_jwt_auth_integration () { + $v = absint(WP_Auth0_Options::get( 'jwt_auth_integration' )); + echo ''; + echo '
' . __('This will enable the JWT Auth\'s Users Repository override.', WPA0_LANG) . ''; + } public static function create_account_message(){ echo '

' @@ -301,6 +315,7 @@ public static function input_validator( $input ){ $input['requires_verified_email'] = (isset($input['requires_verified_email']) ? 1 : 0); $input['wordpress_login_enabled'] = (isset($input['wordpress_login_enabled']) ? 1 : 0); + $input['jwt_auth_integration'] = (isset($input['jwt_auth_integration']) ? 1 : 0); $input['allow_signup'] = (isset($input['allow_signup']) ? 1 : 0); $input['auth0_implicit_workflow'] = (isset($input['auth0_implicit_workflow']) ? 1 : 0); diff --git a/lib/WP_Auth0_Api_Client.php b/lib/WP_Auth0_Api_Client.php index 6b53e639..e39ee90e 100644 --- a/lib/WP_Auth0_Api_Client.php +++ b/lib/WP_Auth0_Api_Client.php @@ -63,4 +63,17 @@ public static function get_user_info($domain, $access_token) { } + public static function get_user($domain, $jwt, $user_id) { + $endpoint = "https://$domain/api/v2/users/" . urlencode($user_id); + + $headers = self::get_info_headers(); + + $headers['Authorization'] = "Bearer $jwt"; + + return wp_remote_get( $endpoint , array( + 'headers' => $headers + )); + + } + } \ No newline at end of file diff --git a/lib/WP_Auth0_Configure_JWTAUTH.php b/lib/WP_Auth0_Configure_JWTAUTH.php index 8ee033fe..49416b9a 100644 --- a/lib/WP_Auth0_Configure_JWTAUTH.php +++ b/lib/WP_Auth0_Configure_JWTAUTH.php @@ -31,12 +31,11 @@ public static function render_settings_page(){ protected static function setupjwt() { if(WP_Auth0::isJWTAuthEnabled()) { - global $wpdb; - JWT_AUTH_Options::set('aud', WP_Auth0_Options::get('client_id')); JWT_AUTH_Options::set('secret', WP_Auth0_Options::get('client_secret')); JWT_AUTH_Options::set('secret_base64_encoded', true); JWT_AUTH_Options::set('override_user_repo', 'WP_Auth0_UsersRepo'); + WP_Auth0_Options::set('jwt_auth_integration', true); } } diff --git a/lib/WP_Auth0_Options.php b/lib/WP_Auth0_Options.php index 4bdbdff2..91b8787d 100755 --- a/lib/WP_Auth0_Options.php +++ b/lib/WP_Auth0_Options.php @@ -61,6 +61,7 @@ private static function defaults(){ 'custom_css' => '', 'auth0_implicit_workflow' => false, 'gravatar' => true, + 'jwt_auth_integration' => false, // 'auto_provisioning' => true, 'default_login_redirection' => home_url(), ); diff --git a/lib/WP_Auth0_UserCreator.php b/lib/WP_Auth0_UserCreator.php new file mode 100644 index 00000000..c2f1f728 --- /dev/null +++ b/lib/WP_Auth0_UserCreator.php @@ -0,0 +1,86 @@ +email) || isset($jwt->nickname)) + && isset($jwt->identities) + ); + + } + + public function create($userinfo, $token) { + + // If the user doesn't exist we need to either create a new one, or asign him to an existing one + $isDatabaseUser = false; + foreach ($userinfo->identities as $identity) { + if ($identity->provider == "auth0") { + $isDatabaseUser = true; + } + } + $joinUser = null; + // If the user has a verified email or is a database user try to see if there is + // a user to join with. The isDatabase is because we don't want to allow database + // user creation if there is an existing one with no verified email + + if (isset($userinfo->email) && ((isset($userinfo->email_verified) && $userinfo->email_verified) || $isDatabaseUser)) { + $joinUser = get_user_by( 'email', $userinfo->email ); + } + + // $auto_provisioning = WP_Auth0_Options::get('auto_provisioning'); + // $allow_signup = WP_Auth0_Options::is_wp_registration_enabled() || $auto_provisioning; + $allow_signup = WP_Auth0_Options::is_wp_registration_enabled(); + + if (!is_null($joinUser) && $joinUser instanceof WP_User) { + // If we are here, we have a potential join user + // Don't allow creation or assignation of user if the email is not verified, that would + // be hijacking + if (!$userinfo->email_verified) { + throw new WP_Auth0_EmailNotVerifiedException($userinfo, $token); + } + $user_id = $joinUser->ID; + } elseif ($allow_signup) { + // If we are here, we need to create the user + $user_id = WP_Auth0_Users::create_user($userinfo); + + // Check if user was created + + if( is_wp_error($user_id) ) { + throw new WP_Auth0_CouldNotCreateUserException($user_id->get_error_message()); + }elseif($user_id == -2){ + throw new WP_Auth0_CouldNotCreateUserException('Could not create user. The registration process were rejected. Please verify that your account is whitelisted for this system.'); + }elseif ($user_id <0){ + throw new WP_Auth0_CouldNotCreateUserException(); + } + } else { + throw new WP_Auth0_RegistrationNotEnabledException(); + } + + // If we are here we should have a valid $user_id with a new user or an existing one + // log him in, and update the auth0_user table + self::insertAuth0User($userinfo, $user_id); + + return $user_id; + + } + + public function insertAuth0User($userinfo, $user_id) { + global $wpdb; + $wpdb->insert( + $wpdb->auth0_user, + array( + 'auth0_id' => $userinfo->user_id, + 'wp_id' => $user_id, + 'auth0_obj' => serialize($userinfo) + ), + array( + '%s', + '%d', + '%s' + ) + ); + } + +} \ No newline at end of file diff --git a/lib/WP_Auth0_UsersRepo.php b/lib/WP_Auth0_UsersRepo.php index 75d25fe4..85425c39 100644 --- a/lib/WP_Auth0_UsersRepo.php +++ b/lib/WP_Auth0_UsersRepo.php @@ -2,7 +2,14 @@ class WP_Auth0_UsersRepo { - public static function getUser($jwt) { + public static function init() { + if (WP_Auth0_Options::get('jwt_auth_integration') == 1) { + add_filter( 'wp_jwt_auth_get_user', array( __CLASS__, 'getUser' ), 0,2); + } + } + + public static function getUser($jwt, $encodedJWT) { + global $wpdb; $sql = 'SELECT u.* @@ -13,14 +20,44 @@ public static function getUser($jwt) { $userRow = $wpdb->get_row($wpdb->prepare($sql, $jwt->sub)); if (is_null($userRow)) { + + $domain = WP_Auth0_Options::get( 'domain' ); + + $response = WP_Auth0_Api_Client::get_user($domain, $encodedJWT, $jwt->sub); + + if ($response['response']['code'] != 200) return null; + + $creator = new WP_Auth0_UserCreator(); + + if ($creator->tokenHasRequiredScopes($jwt)) { + $auth0User = $jwt; + } + else { + $auth0User = json_decode($response['body']); + } + + try { + $user_id = $creator->create($auth0User,$encodedJWT); + + return new WP_User($user_id); + } + catch (WP_Auth0_CouldNotCreateUserException $e) { + return null; + } + catch (WP_Auth0_RegistrationNotEnabledException $e) { + return null; + } + return null; }elseif($userRow instanceof WP_Error ) { self::insertAuth0Error('findAuth0User',$userRow); return null; + }else{ + $user = new WP_User(); + $user->init($userRow); + return $user; } - $user = new WP_User(); - $user->init($userRow); - return $user; + } diff --git a/lib/exceptions/WP_Auth0_CouldNotCreateUserException.php b/lib/exceptions/WP_Auth0_CouldNotCreateUserException.php new file mode 100644 index 00000000..bf6264c2 --- /dev/null +++ b/lib/exceptions/WP_Auth0_CouldNotCreateUserException.php @@ -0,0 +1,3 @@ +userinfo = $userinfo; + $this->id_token = $id_token; + } + +} \ No newline at end of file diff --git a/lib/exceptions/WP_Auth0_RegistrationNotEnabledException.php b/lib/exceptions/WP_Auth0_RegistrationNotEnabledException.php new file mode 100644 index 00000000..204e2870 --- /dev/null +++ b/lib/exceptions/WP_Auth0_RegistrationNotEnabledException.php @@ -0,0 +1,3 @@ +