/**
  * 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;
 }