oauth2-azure-2.0.0 with AAD V2 authorization and token endpoints, Graph V1 and phpMailer
decomplexity opened this issue · 32 comments
I have finally managed** to get PHPMailer working successfully with AAD V2 authorization and token endpoints, Graph V1 and oauth2-azure-2.0.0
This necessitated the following client overrides:
$provider->urlAPI = https://graph.microsoft.com/;
$provider->API_VERSION = '1.0';
$provider->defaultEndPointVersion = TheNetworg\OAuth2\Client\Provider\Azure::ENDPOINT_VERSION_2_0;
But one code change was needed in Azure.php itself: somewhere around line 207 ‘graph.windows.net’ was amended to ‘graph.microsoft.com’
If would be most helpful if all these, but especially the latter one, could be included in the next update to Azure.php.
** after a struggle: client scopes have one URI and the corresponding AAD permissions another URI, and Graph does not support V2 tokens, only V1. And MSFT initial said that SMTP AUTH would not be included in Graph’s permissions list but only in O365’s. But mid-2020 it suddenly appeared in Graph but vanished from O365, and an enable/disable switch was added to the tenant (accessible via Powershell) which could be overridden at user level (accessible via a pulldown in the user account). What was not widely publicised was that the default for new tenants was ‘disabled’. Etc
Thanks, this information is very useful. I've updated the code accordingly but am still getting the error you mention in #114 . I'm developing an OAuth2 module (CMS based) to more easily support OAuth2 with PHPMailer. Did you ever resolve the "invalid grant" issue?
@imclean557 what error exactly are you getting please?
In my case it appears it was a side-effect of having a Graph-specific client scope operand when authenticating, but since MSFT themselves were at the same time changing e.g. default settings, testing was a choppy sea.
If AAD Graph permissions are null (!!) and the client scope is just https://outlook.office.com/SMTP.Send, the access token has an ‘aud’ of https://outlook.office.com and an ‘scp’ of SMTP.Send, and authentication is OK.
But if the client scope also contains a Graph scope such as Mail.Send, ‘aud’ changes to 00000003-0000-0000-c000-000000000000 (aka Graph) and subsequent authentication fails with ‘incorrect credentials’. I would have expected a “scope asking for one token for two different resources” type error message from the token endpoint but the V2 authorization endpoint does not flag that as a specific error. Other error messages such as 'invalid grant' also seem possible.
And, as would be expected, if the client scope is just SMTP.Send (or https://graph.microsoft.com/SMTP.Send; the Graph URI prefix being now the default) authentication fails with ‘incorrect credentials’
@imclean557 might find the SendOauth2 PHP wrapper for PHPMailer useful, if only to check the provider factory. It built to handle the numerous sending endpoints in typical website, and was our intent to add it to GitHub in April (there is already a placeholder) after other work completes. We will try to bring this forward as we have had requests offboard, but Ian is welcome to a copy if he emails us via the decomplexity,com website.
Thanks @decomplexity . The requested scopes are offline_access https://outlook.office.com/SMTP.Send
. See attached for the MS Graph permissions. Should these be removed completely? It looks like I haven't added any scopes for the application under "Expose an API". Should there be something there?
Sorry, I'm not very MS literate. I'm configuring a test account to help a client.
Thanks for the offer of SendOAuth2 PHP.
@hajekj The exact error is "invalid_grant AADSTS9002313: Invalid request. Request is malformed or invalid".
@decomplexity I filled in the contact form on your website and received an error when submitting it: "Sending failed. Error message"
Would you mind showing how your scopes are configured in the code? I haven't tried the SMTP.Send scopes so maybe it has a different behavior.
Sorry Ian - if you tried to send via the website between around 20.15 through 22.30 AEDT (UTC+11) Monday or whatever your time-zone is, one of my colleagues - now with a smartly smacked wrist - was trying out some CAPTCHA changes on the live system. If it wasn't then, I'll get someone to have a look; it seems to be working today as we have already received a couple of emails that route.
Please email me direct on SendOauth2@decomplexity.com
To your earlier points: your client scope is fine and your Graph permissions as fine (although, as I mentioned in a prior post, authentication + authorisation appear to work with no Graph permissions at all! Microsoft have as yet been unable to clarify how they have set this up: my pure guess is that they have defined SMTP.Send, openid and offline_access as default permissions to Exchange Online (aka https://outlook.office.com/) but not allowed these to be visible or altered via AAD. Note that the minimum scopes needed are:
- for get_auth_token (i.e. to get the refresh token): the two you quote
- for subsequent PHPMailer authentication: just https://outlook.office.com/SMTP.Send (i.e. if you add offline-access here, I assume you will get a new refresh token each time you authenticate)
@hajekj The scopes are configured as an array: $provider->scope = ['offline_access', 'https://outlook.office.com/SMTP.Send'];
However, I haven't specified a scopeSeparator
.
@decomplexity Thanks, I've removed "offline_access" from the PHPMailer auth and am still getting the same error. Retrieving the original refresh token* ("code") works fine. Are there any logs at the Microsoft end of things I can check for more detailed error messages?
Also, this is a new tenant so even though the SMTP permission is available, is it possible SMTP needs to be enabled somewhere else?
*Edit: terminology. Perhaps I should calls this "auth token" instead.
Hi all, i'm working on phpmailer+oauth2+microsoft from some days, and i'm in struggle with this.
I was using stevenmaguire as provider, with many option, many tentatives, many errors, token unreadable on jwt.io, ecc.
So first ideas was "stop and %$& this". But i really need to configure oauth with phpmailer and a provider that can work. with stevenmaguire, smtp auth fail without log or reason, so i've read october decomplexity post so ok, i can try a new provider but i need some guides or assistance to implement it. i need to send a single mail when user make login (on other portal), and now only send mail task is missing., someone can help with also a partial code for this? thanks
Hi Matteo
Have a look at the PHPMailer\PHPMailer Wiki document called "Microsoft OAuth2 SMTP issues" that I filed there in December.
Hi Decomplexity, i've read all. I've understood difference for Graph and "office.com". I'm assuming that a backend status about API and frontend (scope) are not mutated from your post (october 2022). So in this case, i've wrote correctly scope, api url, checked for smtp auth in user and checked for every permission in azure portal. i'm stuck in this situation with a classic 535 5.7.3.
I'm available also to change a provider if this can help, but from my side i'm out of ideas. any advice?
As detailed in the Wiki document in the Token Scope section, could you post the value shown for the Resource field in the Basic Info tab and also for the Oauth Scope Info field in the Additional Details tab.
Could you also post the access token that is failing: temporarily amend PHPMailer / OAuth.php / function getToken() to:
$accesstoken = $this->provider->getAccessToken(
$this->getGrant(),
['refresh_token' => $this->oauthRefreshToken]
);
var_dump($accesstoken);
return $accesstoken;
For your security, you may wish to wait for two hours or so before posting to allow the token to expire
Hi Decomplexity, sorry for my late reply but i was out of city for work.
Token is 0.ATEAhP1wn2rkq0OCoIIy4LqLQXYehM83fd1DtUxXvP8pBsYxANs.AgABAAEAAAD--DLA3VO7QrddgJg7WevrAgDs_wQA9P-e6UD6P3D9spQNkw5hIR55-KJm0Ap3p-0Nlc7AO3ihUE2TZ52mUhMc9o7DcvOmN3P8-qN7oM1ZoziNLCpmsAWJiu5ske_BJ-dZh9I5TktWRAjQmuqcb2857uK3w9XrF-oZva83cnrg7_quBy78sRJg5cGJ8zzxtGWFIYx8FCuDKb1e4FFbBROY1-403vzYBM0N9SMYmRT1ZnDXqWHkS0dp5dZFLhTxiS21W32lCOmgFXiqGR0RyO-iO9fzOKkv-xYDM6SKPSUz3dWBL0StScGra40QXkwEcceDlAhJgqjS279AIurLc3qp-TxHRx3eKrbzS50EinVxV97W0T3FIm2ZyNK0ZQNf1afBesYwzQLjTVbkCUmJuWzjfjU-RfhNmZ7cQiSsqXvq7eay_ECpfZZK3ZGNALKv87uhXKX0FKCjlZx0eNw6Cl5kXzWDfcREznqkMtFCXXY5zUd3h1s9RvO74J4AxzHPg0RJel1KRZBAKAIwmLQsgGSDMVMB4w6dercK_xO0vBqOSCpt5jRIlPCpG9XsxLq5a9aukxTOgMMA1G8ZREAY4Wl6B08S2rQa8ULQ3zXXlViwKFePUbMlIqVlgyAtjwlqrbD64-iPfVsIbd14YGlchB5tSVXEK5aORur0Qf5N3oaneNZ7iWb8lOJfJ1zdTZUZlVqrcFfC_V5sLSjJImVS6wZTyhhuhUIEZKcyLu73AdZtK6XA4MSz7e3TMnjl5ch3DO_OpW3H1kK5_ak7Mp783zlWnF3v-Kuu0oEMBXobsfRWlJboES55jWYyN_B1OqK9ohGRhR30xs7ftaRT6B_ZGrwlsU8ZPC6cBfmOEW9CoOXy6wljQ1WUNb8KSu4EmY0z9aPaZgriw11u4l57IovnplhYnXFEY12SvKU1hjH-Qq3c9NGUuPWVG30lIonORQQioLfOgQwKONv91XdjmZg4hdjZsh9z9gydfypGbxKpqTbqjVv05wZugPW6uPCImj-7m2n4Axh3zkK3jHob-07Q9tU9fe7Ao7htnxYFETYWSgGxZ5isH21zTQtoken: 0.ATEAhP1wn2rkq0OCoIIy4LqLQXYehM83fd1DtUxXvP8pBsYxANs.AgABAAEAAAD--DLA3VO7QrddgJg7WevrAgDs_wQA9P-e6UD6P3D9spQNkw5hIR55-KJm0Ap3p-0Nlc7AO3ihUE2TZ52mUhMc9o7DcvOmN3P8-qN7oM1ZoziNLCpmsAWJiu5ske_BJ-dZh9I5TktWRAjQmuqcb2857uK3w9XrF-oZva83cnrg7_quBy78sRJg5cGJ8zzxtGWFIYx8FCuDKb1e4FFbBROY1-403vzYBM0N9SMYmRT1ZnDXqWHkS0dp5dZFLhTxiS21W32lCOmgFXiqGR0RyO-iO9fzOKkv-xYDM6SKPSUz3dWBL0StScGra40QXkwEcceDlAhJgqjS279AIurLc3qp-TxHRx3eKrbzS50EinVxV97W0T3FIm2ZyNK0ZQNf1afBesYwzQLjTVbkCUmJuWzjfjU-RfhNmZ7cQiSsqXvq7eay_ECpfZZK3ZGNALKv87uhXKX0FKCjlZx0eNw6Cl5kXzWDfcREznqkMtFCXXY5zUd3h1s9RvO74J4AxzHPg0RJel1KRZBAKAIwmLQsgGSDMVMB4w6dercK_xO0vBqOSCpt5jRIlPCpG9XsxLq5a9aukxTOgMMA1G8ZREAY4Wl6B08S2rQa8ULQ3zXXlViwKFePUbMlIqVlgyAtjwlqrbD64-iPfVsIbd14YGlchB5tSVXEK5aORur0Qf5N3oaneNZ7iWb8lOJfJ1zdTZUZlVqrcFfC_V5sLSjJImVS6wZTyhhuhUIEZKcyLu73AdZtK6XA4MSz7e3TMnjl5ch3DO_OpW3H1kK5_ak7Mp783zlWnF3v-Kuu0oEMBXobsfRWlJboES55jWYyN_B1OqK9ohGRhR30xs7ftaRT6B_ZGrwlsU8ZPC6cBfmOEW9CoOXy6wljQ1WUNb8KSu4EmY0z9aPaZgriw11u4l57IovnplhYnXFEY12SvKU1hjH-Qq3c9NGUuPWVG30lIonORQQioLfOgQwKONv91XdjmZg4hdjZsh9z9gydfypGbxKpqTbqjVv05wZugPW6uPCImj-7m2n4Axh3zkK3jHob-07Q9tU9fe7Ao7htnxYFETYWSgGxZ5isH21zTQ
Hi Matteo
This doesn't look like an Access token - and it appears to be two identical tokens or codes (see from character 1140).
Each one could be the Refresh token as refresh tokens are not decodable.
If you present this to PHPMailer as an Access token you are sure to get a 535 5.7.3
The one provider I can promise works is the present one (TheNetworg), we use it on our many websites, and I have never found installation errors or bad request or uncaught function. It is worth checking that your installation directory structure is correct:
vendor/thenetworg/oauth2-azure/src/Grant/....
vendor/thenetworg/oauth2-azure/src/Provider/...
vendor/thenetworg/oauth2-azure/src/Token/...
plus TheLeague OAuth2 abstract providers etc:
vendor/league/oauth2-client/....
plus vendor/ firebase, guzzlehttp, paragonie, psr, ralouphie, symfony
and assuming you installed using composer:
composer and autoload.php
Hi, i can confirm that directory structure is correct, just verified, and yes i'm using composer and autoload.php.
with thenetworkg i've problems about implementations, so do you have example code?
A bit baffled by this! Since you have TheNetworg producer installed, I guess you could download the four wrapper modules from decomplexity/SendOauth2 or Packagist (plus the examples for invoking them) and see if you get errors with them. You just need to add your client ID, secret and so on in SendOauthD. (In passing, new version of SendOauth2 will be released in March that refreshes refresh tokens automatically, supports certificates as well as client secrets and so on)
Hi, i've downloaded it with examples and i've added my info (client id, secret, etc...) to SendOauthD.
Mylack, i don't understand storage organization so let me know if correct:
- four file SendOauth2D, 2C,2B,2A will be kept in decompleity/sendoauth2/src
- 3 file Send...-settings.php, 2D-invoke.php and 2A-invoke.php will be kept at the same level of vendor folder.
- file SendOauth2D-invoke.php can be used as-is to send mail, with SendOauth2D-settings.php filled? otherwise how i can "construct" a working file? actually i've this kind of error:
[Thu Jan 19 16:23:35.141965 2023] [php:error] [pid 155233] [client 192.168.20.48:61836]
PHP Fatal error: Uncaught Error: Call to undefined method PHPMailer\PHPMailer\PHPMailer::aAddBCC() in /var/www/host.domain.com/public_html/auth2/vendor/decomplexity/sendoauth2/src/SendOauth2A.php:466\n
Stack trace:\n#0 /var/www/host.domain.com/public_html/auth2/vendor/decomplexity/sendoauth2/src/SendOauth2A.php(341): decomplexity\SendOauth2\SendOauth2A->assignValues()\n#1
/var/www/host.domain.com/public_html/auth2/file.php(7): decomplexity\SendOauth2\SendOauth2A->__construct()\n#2 {main}\n
thrown in /var/www/host.domain.com/public_html/auth2/vendor/decomplexity/sendoauth2/src/SendOauth2A.php on line 466
i'm not a developer, i'm system administrator, so i'm not used to this envoronment.
thanks!
The four files SendOauth2D, 2C, 2B, 2A should be kept in vendor/decomplexity/sendoauth2/src
Your invoking files (SendOauth2D_invoke.php and whatever you call your mainline PHPMailer calling file) would typically be in one directory level above /vendor, perhaps just php/
If you send me your email address via the Contact page of decomplexity.com I will send you a newer version of SendOauth2A.php.
Did you successfully get a refresh token using SendOauth2D_invoke.php?
No when using SendOauth2D_invoke.php i got back error in my previous post, so no token at all for a moment.
New SendOauth2A.php file will be in same path of older, right?
ok for invoking file, i will put in un level up, that is /vendor parent folder(in error you can see that public_html is web root, oauth2 is folder that contains all file and composer installation. my difficulty at the moment is "join" invoke file with phpmailer file, i was using phpmailer file with mail function when using old provider. now, i'm supposing (wrong?) that sendoauth istance will trigger phpmailer class itself. probably i'm wrong, and for this i see that error in previous post. do you have a semi-completed file?
ah, just for "fun", this is customer purpose: user login on web page in a custom portal (made by another developer). at login task, user must receive a mail with a otp code (otp engine is inside web application), but due to deprecation of plain auth, we need to implement oauth2. i've create azure app, with a (long) list of permission, authorized app as tenant administrator, put as redirect url the same url of invoke file. mail sender will be noreply@domain.com(exchange plan 1 license), i've autorized app and made first login as admin@domain.com (no license, only admin rights with otp in microsoft authenticator app).
hope that now my purpose is clearer to you, let me know if not
i will write you via contact page in a minute.
EDIT: sent!
Hi Matteo
My reply to the email address you sent via my Contact page was bounced by GitHub "due to organizational settings".
Do you have a normal business or personal email address?
yes, give me 1 minutes
This mail is on Office365, now it should arrived correctly
Still being bounced!
Sent another one!
And still being bounced!
do you know what provider is correctly working?
sorry, i mean mail provider...to avoid mail bounce. so if you know if gmail, hotmail or others works, let me know and i will write from one of them, or register a mail account on it if i don't have
thanks
The only email address I have for you is the @github one you sent me. Emailing via our Contact page ignores the sender's email address (unless it is in the text of the message) to try to block spam.
Github itself is blocking my replies to you.
You can email me directly at:
SendOauth2@decomplexity.com
from any @outlook.com or @gmail.com or similar address.
Hi Max,
this is my situation, it's a little bit different form once that you send mail via mail but tell me if i need to change something:
domain
/auth
SendOauth2D-invoke.php
SendOauth2D-settings.php
SendOauth2A-invoke.php
/vendor
/decomplexity
/sendoauth2
/src
SendOauth2A.php
SendOauth2B.php
SendOauth2C.php
SendOauth2D.php
At the same level of decomplexity folder, i have firebase, guzzlehttp, league, paragonie, phpmailer, psr, ralouphie, src, symfony, thenetworg, composer, autoload.php
I've configured all parameters in SendOauth2D-settings.php.
When call via browser domain/auth/SendOauth2D-invoke.php, i get this error(via ssh apache log):
PHP Fatal error: Uncaught Error: Class "decomplexity\sendoauth2\SendOauth2C" not found in domain/auth/vendor/decomplexity/sendoauth2/src/SendOauth2D.php:159\n
Stack trace:\n#0 domain/auth/SendOauth2D-invoke.php(11): decomplexity\sendoauth2\SendOauth2D->__construct()\n#1 {main}\n thrown in domain/auth/vendor/decomplexity/sendoauth2/src/SendOauth2D.php on line 159
i've made also change for redirect url in azure.
thanks!
I will like to inform you that I have a bank account in Australia, Malaysia, Indonesia, and Thailand for quick cash out. For invoice payment, bank wire transfers, or any payment form. If interested please let me know me.
Thanks
Mark.