the-djmaze / snappymail

Simple, modern & fast web-based email client
https://snappymail.eu
GNU Affero General Public License v3.0
1.01k stars 120 forks source link

"Page not found" in Nextcloud with Microsoft 365 login after clicking Accept in Permissions requested page #1777

Closed ccci-code closed 2 weeks ago

ccci-code commented 2 weeks ago

Describe the bug The following error is displayed after Accepting Microsoft permissions while logging in to SnappyMail from Nextcloud: Page not found The page could not be found on the server or you may not be allowed to view it.

Back to Nextcloud

To Reproduce Steps to reproduce the behavior:

  1. Install SnappyMail in Nextcloud, configure Client ID, secret, etc after configuring app in Microsoft
  2. Click on Email button in top menubar
  3. Click on Outlook button on the SnappyMail login page
  4. Login to Microsoft, enter email, password, then on the Microsoft Permissions requested page, click Accept button
  5. Page not found is then displayed in Nextcloud

Expected behavior I expect to see my inbox

Please complete the following information:

Debug/logging information Read here how to log I enabled logging, but no error messages were displayed

Additional context I'm hoping to replace the slow Nextcloud Mail which is currently working with Microsoft 365 and S/MIME.

I configured the redirect URI in Microsoft Azure as: https://[netcloud host]/index.php/apps/snappymail/LoginO365

The link in the browser on the Not found page in Nextcloud is: https://[nextcloud host]/index.php/apps/snappymail/LoginO365?code=&state=o365&session_state=#

the-djmaze commented 2 weeks ago

Correct because Microsoft does not allow query parameters for personal setups. It's a problem of Microsoft, not SnappyMail.

Maybe if enough people complain, Microsoft will listen.

I hate Microsoft with their crap, so you should workaround this modifying your Nextcloud .htaccess or something.

See #1645

ccci-code commented 2 weeks ago

Thanks, @the-djmaze I know you closed this, but I found a few things wrong in the code that might be causing some of the problems. I understand if you want to ignore this. But it seems so close to working now.

Microsoft does allow query parameters for Web platform redirect urls for school and work accounts so I was able to use as the redirect url: https://nextcloud-dev.example.com/index.php/apps/snappymail/?LoginO365

I modified plugins/login-o365/LoginOAuth2.js line 8: redirect_uri: document.location.href.replace(/\/$/, '') + '/LoginO365', to redirect_uri: document.location.href.replace(/\/$/, '') + '/?LoginO365',

Next, you have an extra double quote on line 116 of plugins/login-o365/index.php: $aUserInfo = $oO365->fetch('https://graph.microsoft.com/oidc/userinfo"'); should be: $aUserInfo = $oO365->fetch('https://graph.microsoft.com/oidc/userinfo');

Also, there's no 'id' returned by Microsoft in the userinfo response so the index.php code fails on line 121 and 135 (see https://learn.microsoft.com/en-us/entra/identity-platform/userinfo)

$aUserInfo = $aUserInfo['result'];
if (empty($aUserInfo['id'])) {

The only fields returned are sub, name, family_name, given_name, picture, and email. They suggest using an ID token instead to get the same information and eliminate an extra trip to their server.

With the changes, I can see that I'm getting my userinfo from Microsoft along with the access token. But now I get the same message as described in https://github.com/the-djmaze/snappymail/issues/1663

If I back up to: /apps/snappymail/, I get the message: An error occurred. Please refresh the page and try again.Error: Network response error: 403

I downloaded both the release and git versions of snappymail and made the same changes there as well. However, it returns me to the login page after I click the Accept button on the Microsoft login page.

the-djmaze commented 2 weeks ago

Microsoft does allow query parameters for Web platform redirect urls for school and work accounts.

Correct! But that wasn't implemented in my code. Like i said in the linked issue, i've made draft code to get something working.

So if your improvements work, then they get into the extension 😉

ccci-code commented 1 week ago

Found the problem. You have to use the scopes 'https://outlook.office.com/SMTP.Send' and 'https://outlook.office.com/IMAP.AccessAsUser.All' that you have listed as legacy in plugins/login-o365/LoginOAuth2.js and use them also in the plugins/login-o365/index.php file.

Ignore the Microsoft documentation that tells you to use the https://graph.microsoft.com/* scopes. You can't authenticate to their IMAP server with the access token returned by the graph urls. For now, at least.

I use the tenant_id instead of tenant when requesting the access token. (I added ->SetAllowedInJs() in the configMapping() function for the tenant_id variable).

$aResponse = $oO365->getAccessToken(
    \str_replace('{{tenant}}', $this->Config()->Get('plugin', 'tenant_id', 'common'), static::TOKEN_URI),
    'authorization_code',
    array(
        'code' => $_GET['code'],
        'client_id' => $this->Config()->Get('plugin', 'client_id', ''),
        'client_secret' => $this->Config()->Get('plugin', 'client_secret', ''),
        'scope' => 'offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send'),
        'redirect_uri' => $oHttp->GetFullUrl() . '?LoginO365'
    )
);

I extract the email address from the access token in plugins/login-o365/index.php instead of making the call to https://graph.microsoft.com/oidc/userinfo (which requires getting a separate access_code using the graph url).

$oO365->setAccessToken($sAccessToken);
$oO365->setAccessTokenType(OAuth2\Client::ACCESS_TOKEN_BEARER);

list($header, $payload, $signature) = explode('.', $sAccessToken);
$jsonToken = base64_decode($payload);
$aUserInfo = json_decode($jsonToken, true);

$aUserInfo['upn'] contains my email address

I replaced "$oAccount->CryptKey()" with true in the $oActions->StorageProvider()->Get and Put() functions since they were causing an error.

I can now view my email - much faster than the Nextcloud email! I still have some issues with S/MIME. I had to add the content header to the beginning of the input text in the decrypt function in snappymail/smime/openssl.php before openssl_pkcs7_decrypt would work. Not sure why the header is missing.

MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Type: application/pkcs7-mime; smime-type=enveloped-data;
name="smime.p7m"
Content-Transfer-Encoding: base64

I still can't send encrypted email. I get a "Signing failed" error that I still haven't tracked down.

Edit: error was caused by using an unencrypted private key