/**
  * Authenticates the user with Azure AD and WordPress.
  *
  * This method, invoked as an 'authenticate' filter, implements the OpenID Connect
  * Authorization Code Flow grant to sign the user in to Azure AD (if they aren't already),
  * obtain an ID Token to identify the current user, and obtain an Access Token to access
  * the Azure AD Graph API.
  *
  * @param WP_User|WP_Error $user A WP_User, if the user has already authenticated.
  * @param string $username The username provided during form-based signing. Not used.
  * @param string $password The password provided during form-based signing. Not used.
  *
  * @return WP_User|WP_Error The authenticated WP_User, or a WP_Error if there were errors.
  */
 function authenticate($user, $username, $password)
 {
     // Don't re-authenticate if already authenticated
     if (is_a($user, 'WP_User')) {
         return $user;
     }
     /* If 'code' is present, this is the Authorization Response from Azure AD, and 'code' has
      * the Authorization Code, which will be exchanged for an ID Token and an Access Token.
      */
     if (isset($_GET['code'])) {
         $antiforgery_id = $_SESSION['aadsso_antiforgery-id'];
         $state_is_missing = !isset($_GET['state']);
         $state_doesnt_match = $_GET['state'] != $antiforgery_id;
         if ($state_is_missing || $state_doesnt_match) {
             return new WP_Error('antiforgery_id_mismatch', sprintf(__('ANTIFORGERY_ID mismatch. Expecting %s', AADSSO), $antiforgery_id));
         }
         // Looks like we got a valid authorization code, let's try to get an access token with it
         $token = AADSSO_AuthorizationHelper::get_access_token($_GET['code'], $this->settings);
         // Happy path
         if (isset($token->access_token)) {
             try {
                 $jwt = AADSSO_AuthorizationHelper::validate_id_token($token->id_token, $this->settings, $antiforgery_id);
             } catch (Exception $e) {
                 return new WP_Error('invalid_id_token', sprintf(__('ERROR: Invalid id_token. %s', AADSSO), $e->getMessage()));
             }
             // Invoke any configured matching and auto-provisioning strategy and get the user.
             $user = $this->get_wp_user_from_aad_user($jwt);
             if (is_a($user, 'WP_User')) {
                 // At this point, we have an authorization code, an access token and the user
                 // exists in WordPress (either because it already existed, or we created it
                 // on-the-fly). All that's left is to set the roles based on group membership.
                 if (true === $this->settings->enable_aad_group_to_wp_role) {
                     $user = $this->update_wp_user_roles($user, $jwt->upn, $jwt->tid);
                 }
             }
         } elseif (isset($token->error)) {
             // Unable to get an access token ( although we did get an authorization code )
             return new WP_Error($token->error, sprintf(__('ERROR: Could not get an access token to Azure Active Directory. %s', AADSSO), $token->error_description));
         } else {
             // None of the above, I have no idea what happened.
             return new WP_Error('unknown', __('ERROR: An unknown error occured.', AADSSO));
         }
     } elseif (isset($_GET['error'])) {
         // The attempt to get an authorization code failed.
         return new WP_Error($_GET['error'], sprintf(__('ERROR: Access denied to Azure Active Directory. %s', AADSSO), $_GET['error_description']));
     }
     return $user;
 }
 function authenticate($user, $username, $password)
 {
     // Don't re-authenticate if already authenticated
     if (is_a($user, 'WP_User')) {
         return $user;
     }
     if (!isset($_GET['id_token'])) {
         if (isset($_GET['error'])) {
             // The attempt to get an authorization code failed (i.e., the reply from the STS was "No.")
             return new WP_Error($_GET['error'], sprintf(__('ERROR: Access denied to Azure Active Directory. %s', 'aad-sso'), $_GET['error_description']));
         }
         return $user;
     }
     try {
         AADSSO_AuthorizationHelper::$base_uri = $this->settings->base_uri;
         $jwt = AADSSO_AuthorizationHelper::validate_id_token($_GET['id_token']);
     } catch (Exception $e) {
         return new WP_Error('invalid_id_token', sprintf(__('ERROR: Invalid id_token. %s', 'aad-sso'), $e->getMessage()));
     }
     if (!isset($jwt->altsecid) || !$jwt->altsecid) {
         return new WP_Error('missing_altsecid_property', sprintf(__('%s is not a valid account. Please sign-out first and then sign-in with your Windows Live ID.', 'aad-sso'), $jwt->unique_name));
     }
     if (!wp_verify_nonce($jwt->nonce, self::NONCE_NAME)) {
         return new WP_Error('nonce_fail', sprintf(__('NONCE_NAME mismatch. Expecting %s', 'aad-sso'), self::NONCE_NAME));
     }
     if ($jwt->aud != $this->settings->client_id) {
         //	Need to check [aud] is the same as the client id
         return new WP_Error('client_id_mismatch', sprintf(__('ERROR: aud ( %s ) does not match Client ID', 'aad-sso'), $jwt->aud));
     }
     if (strpos($jwt->iss, 'sts.windows.net') == false && strpos($jwt->iss, 'sts.windows-ppe.net') == false) {
         //	[iss] contains sts.windows.net or sts.windows-ppe.net
         return new WP_Error('issuer_mismatch', sprintf(__('ERROR: Issuer was %s, expected windows.net', 'aad-sso'), $jwt->iss));
     }
     if ((int) $jwt->iat > (int) time()) {
         //	[iat] must not be in the future
         return new WP_Error('issuing_time_error', sprintf(__('ERROR: Account must be issued in the past, was issued at %s.', 'aad-sso'), $jwt->iat));
     }
     if ((int) $jwt->exp <= (int) time()) {
         //	[exp] must not be in the past
         return new WP_Error('issuing_is_expired', sprintf(__('ERROR: Account has expired on %s', 'aad-sso'), $jwt->exp));
     }
     // Try to find an existing user in WP with the altsecid of the currect AAD user
     $user = $this->get_user_by_aad_id($jwt->altsecid);
     // If we have a user, log them in
     if (!empty($user) && is_a($user, 'WP_User')) {
         // At this point, we have an authorization code, an access token and the user exists in WordPress.
         $user = apply_filters('aad_sso_found_user', $user, $jwt);
         return $user;
     }
     /*
      * No user found. Now decide if we are allowed to create a new
      * user or not. Will use the WordPress setting from Settings > General
      */
     $reg_open = get_option('users_can_register');
     $override_reg = apply_filters('aad_override_user_registration', $this->settings->override_user_registration, $jwt);
     if (!$reg_open && !$override_reg) {
         return new WP_Error('user_not_registered', sprintf(__('ERROR: The authenticated user %s is not a registered user in this blog.', 'aad-sso'), $jwt));
     }
     $email = $this->get_jwt_email($jwt);
     if (is_wp_error($email)) {
         return $email;
     }
     $username = explode('@', $email);
     $username = apply_filters('aad_sso_login_username', $username[0], $jwt);
     $username = get_user_by('login', $username) ? 'aadsso-' . sanitize_text_field($jwt->altsecid) : $username;
     // Setup the minimum required user data
     $userdata = array('user_login' => wp_slash($username), 'user_email' => wp_slash($email), 'user_pass' => wp_generate_password(20, true), 'first_name' => isset($jwt->given_name) ? esc_html($jwt->given_name) : '', 'last_name' => isset($jwt->family_name) ? esc_html($jwt->family_name) : '', 'role' => $this->settings->default_wp_role ? $this->settings->default_wp_role : 'subscriber');
     $userdata['display_name'] = $userdata['nickname'] = $userdata['first_name'] && $userdata['last_name'] ? $userdata['first_name'] . ' ' . $userdata['last_name'] : $userdata['first_name'];
     // Allow user-creation override
     $user = apply_filters('aad_sso_new_user_override', null, $userdata, $jwt);
     // If we have a user, log them in
     if (!empty($user) && is_a($user, 'WP_User')) {
         // At this point, the user exists in WordPress.
         $user = apply_filters('aad_sso_found_user', $user, $jwt);
         return $user;
     }
     $new_user_id = wp_insert_user($userdata);
     if (is_wp_error($new_user_id)) {
         return $new_user_id;
     }
     // update usermeta so we know who the user is next time
     update_user_meta($new_user_id, $this->user_id_meta_key, sanitize_text_field($jwt->altsecid));
     $user = new WP_User($new_user_id);
     // @todo do_action new_user
     $user = apply_filters('aad_sso_new_user', $user, $jwt);
     return $user;
 }