OAuth 2.0 for Mail (POP3/SMTP)?
Hello,
i receiving several mails via "EmailInboundAdapter” and sending via "EmailOutboundAdapter"
Now Microsoft will force OAuth 2.0 for Outlook365-Mails and want to drop POP3 basic authentication permanetly at Oct/1 2022. All have to use OAuth 2.0 then.
IRIS documentation is very tiny for OAuth 2.0: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls...
A new paramter for a token is defined for "%Net.POP3.Connect()". But the token got only on authenication calls using ClientID and ClientSecret (last one valid for maximum 24 months). There is not support for OAuth 2.0 on EmailInboundAdapter, i think.
Is it be possible to implement an own Adapter using “%Net.POP3.Connect()” with tokens? Does anyone have experiance using OAuth 2.0 for Microsoft Outlook 365?
Many thanks for any hint or help!
Check this thread.
On IRIS 2022.1 the class (%Net.POP3).Connect(...) has a 4th parameter: AccessToken
I di not try, but maybe it will allow a connection with OAuth
Yes the access-token is for OAuth, but it is short time and you get it from Auth-Server by using ClientID, ClientSecret (and tenantID) for an registered App.
There is an option to get a long time token (refresh token), if tokens are "cached" locally, you may have a scheduled task to refresh them.
Another approach I would try here, is to use embedded python with this library:
https://pypi.org/project/O365/#authentication
Long time token: unfortunately I can't decide
I've made such an adapters based on Jose's work.
My articles, I'm afraid which is written in Japanese only, is using IMAP/SMTP against gmail as target provider but I hope it works against Office365 as well.
The idea is you prepare a JSON file like this and production will pick it up when it starts (via OnStart() callback).
After that, adapter itself get new AccessToken periodically by using a given RefreshToken (well, at least that what I've intended).
Thanks. I will check the sources on github. Hopefully it helps for POP3 ...
i have modify a python scripts to get an access token from Outlook/Azure. I put all them together and defining a service, that periodically refresh the access token and save it in a global.
This works fine.
Next i have made my own "Class RT.EnsLib.EMail.InboundAdapter Extends Ens.InboundAdapter { ..." cloning and modify EnsLib.EMail.InboundAdapter
Adding:
Set tAccessToken=""
Set:..AccessTokenName'="" tAccessToken=$GET(^OAuth2.AccessToken(..AccessTokenName))
$$$TRACE("tAccessToken="_tAccessToken)
Set tSC = ..MailServer.ConnectPort(..POP3Server,..POP3Port,..%CredentialsObj.Username,..%CredentialsObj.Password,tAccessToken)
Using the access token on Intersystems ConnectPort() method
Method ConnectPort(PopServer As %String, PopPort As %Integer, UserName As %String, Password As %String, AccessToken As %String = "") As %Status
Calling
ConnectPort(“outlook.office365.com”,995,”i...t@h...a.com”,”1…3”,”eyJ…vIQ”)
I got back:
Fehler auf POP3-Server: -ERR Authentication failure: unknown user name or bad password.
Yes, i have double checked the correctness of the access token.
Not a problem on Intersystems side.
On Outlook 365: Enabled POP access (that is turned off by default)
Just curious.
It looks %Net.POP3 implementation is following these.
https://docs.microsoft.com/en-us/exchange/client-developer/legacy-protoc...
https://docs.microsoft.com/en-us/exchange/client-developer/legacy-protoc...
So once you have deviced the way to aquire and pass access token (and enable POP3 on O365 side), you are all set?
Exact. The lack on intersystems is to aquire an access token. %Net.POP3 implements is as descripted on M$-docs.
I used a modified python-script "mutt_oauth2.py". First we had problems that python are an unauthorized client on the authorization part. Switch to multi-tanant-account AND add python on app-authentication-web-quickstart AND add redirect-uri 'http://localhost' (also on mutt_oauth2.py) was the solution to use python for aquire a access token.
💡 This question is considered a Key Question.
Got EnsLib.EMail.InboundAdapter and added some line using AccessToken[Name]. Embedded Python code based on mutt_oauth2.py I have stripped it a little (only 'authcode'-flow) Elliminate external file and using IRIS-globals. Put 'client_id', 'client_secret', 'tenant' from registrations to token (saved in global) to be independent from edit sourcecode like mutt_oauth2.py was.
To use OAuth2 you have to start with getting an URL for authentication process:
w ##class(My.EnsLib.EMail.OAuth2InboundAdapter).askAuthorization(AccessTokenName, registration, mail, clientid, clientsecret [,tenant])
registration meens the name of the python 'registrations'-Array-Element. The output is an URL to use in browser for getting an authorizationCode. After login and some confirmations you get a code. On google you find it much clear in a well styled webpage. On microsoft you find the code as part of the destination url in browsers url textfield.
w ##class(My.EnsLib.EMail.OAuth2InboundAdapter).doAuthorization(AccessTokenName, AuthorizationCode)
returns the first AccessToken and saving all details on ^OAuth2.AccessToken(AccessTokenName) global.
now you are ready to retrieve mails by oauth2.
Can be rewritten with object-script, but for me it is fine. No external command nor external files. ClassMethods can used for an authorization.
Perhaps Intersystems will be produce a better solution for us...