Skip to content

update the smtp provider to support oauth authentication.#5460

Merged
mitchelsellers merged 20 commits intodnnsoftware:developfrom
zyhfish:task/smtp-oauth-interface
Aug 29, 2023
Merged

update the smtp provider to support oauth authentication.#5460
mitchelsellers merged 20 commits intodnnsoftware:developfrom
zyhfish:task/smtp-oauth-interface

Conversation

@zyhfish
Copy link
Copy Markdown
Contributor

@zyhfish zyhfish commented Dec 29, 2022

Fix #5139.

zyhfish and others added 3 commits December 29, 2022 07:57
Fixed an issue that prevented sending smtp test emails
Due to the UI changes in dnnsoftware#5152 the frontend now posts an obfuscated password unless the user is currently changing the password.
That scenario got handled correctly for UpdateSmtpSettings but the same logic needed to be implements in SendTestEmail for that button to work fine.
Copy link
Copy Markdown
Contributor

@valadas valadas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice, I left a couple of questions/suggestiions and would like to give it a quick test before approving. I can do that with gmail I guess ?

@zyhfish
Copy link
Copy Markdown
Contributor Author

zyhfish commented Dec 29, 2022

Thanks so much @valadas , the suggestions have been applied.

@bdukes
Copy link
Copy Markdown
Contributor

bdukes commented Jan 10, 2023

@zyhfish am I correct to understand that this change doesn't do anything until you have an ISmtpOAuthProvider implementation in your site? Do you have an example implementation we can review and test with?

@zyhfish
Copy link
Copy Markdown
Contributor Author

zyhfish commented Jan 11, 2023

@zyhfish am I correct to understand that this change doesn't do anything until you have an ISmtpOAuthProvider implementation in your site? Do you have an example implementation we can review and test with?

Hi @bdukes , thanks so much for your update, can you please ping me on Slack when you are online, so that we can discuss further, thx.

Copy link
Copy Markdown
Contributor

@bdukes bdukes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still need to see an ISmtpOAuthProvider implementation to test the frontend, but I've adjusted the backend to have fewer dependencies directly on MailKit, and I'm happy with how things stand now.

@bdukes
Copy link
Copy Markdown
Contributor

bdukes commented Jan 12, 2023

Ben shared two SMTP OAuth provider packages developed for Evoq, and I was able to test with those. Once installed, there's a fourth SMTP Auth option, OAuth.
OAuth configuration screen

I was able to select on of the auth providers, enter the details and save. After clicking Save, a section displays with an Authorize button, which opens a pop-up to the SMTP provider. I wasn't able to figure out the details of getting a test SMTP service and auth setup (this would at least require a valid redirect URL to be configured to the test site), but was able to test at least that far.

OAuth config screen with Authorize button

@mitchelsellers
Copy link
Copy Markdown
Contributor

mitchelsellers commented Jan 12, 2023

Am I to understand that addition of this, at this time does NOT give the DNN Platform the proper ability to utilize OAuth? If this is correct, do we have the information available, in the extension points created to actually create something that is of value for the open source project?

@bdukes
Copy link
Copy Markdown
Contributor

bdukes commented Jan 12, 2023

Correct, to add an implementation to the open source project, we would need to create classes that implement the ISmtpOAuthProivder interface.

/// <summary>A contract specifying the ability to authenticate an SMTP client with an OAuth 2 endpoint.</summary>
public interface ISmtpOAuthProvider
{
/// <summary>Gets provider name.</summary>
string Name { get; }
/// <summary>Gets the localized name.</summary>
string LocalizedName { get; }
/// <summary>Whether the provider has completed the authorization process for the portal.</summary>
/// <param name="portalId">The portal ID.</param>
/// <returns><see langword="true"/> if the authorization has been completed, otherwise <see langword="false"/>..</returns>
bool IsAuthorized(int portalId);
/// <summary>Get the authorize URL.</summary>
/// <param name="portalId">The portal ID.</param>
/// <returns>The URL.</returns>
string GetAuthorizeUrl(int portalId);
/// <summary>Get the provider parameters.</summary>
/// <param name="portalId">the portal ID of the setting, pass <see cref="Null.NullInteger"/> if it's a global setting.</param>
/// <returns>The list of settings.</returns>
IList<SmtpOAuthSetting> GetSettings(int portalId);
/// <summary>Update provider settings.</summary>
/// <param name="portalId">the portal id of the setting, pass <see cref="Null.NullInteger"/> if it's a global setting.</param>
/// <param name="settings">the settings.</param>
/// <param name="errorMessages">the errors.</param>
/// <returns>Whether update the settings successfully.</returns>
bool UpdateSettings(int portalId, IDictionary<string, string> settings, out IList<string> errorMessages);
/// <summary>Authorize the SMTP client.</summary>
/// <param name="portalId">The portal ID.</param>
/// <param name="smtpClient">The SMTP client.</param>
void Authorize(int portalId, IOAuth2SmtpClient smtpClient);
/// <summary>Authorize the SMTP client.</summary>
/// <param name="portalId">The portal id.</param>
/// <param name="smtpClient">The SMTP client.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A <see cref="Task"/> indicating completion.</returns>
Task AuthorizeAsync(int portalId, IOAuth2SmtpClient smtpClient, CancellationToken cancellationToken = default(CancellationToken));
}

@zyhfish
Copy link
Copy Markdown
Contributor Author

zyhfish commented Jan 31, 2023

Hi @mitchelsellers , is this PR able to be merged and included in next release? thx

@david-poindexter david-poindexter added this to the Future: Minor milestone Jan 31, 2023
@bdukes
Copy link
Copy Markdown
Contributor

bdukes commented Jan 31, 2023

@zyhfish since this is a new feature, we're planning to hold this PR for a 9.12 or 10.0 release

@david-poindexter
Copy link
Copy Markdown
Contributor

@zyhfish since this is a feature, we are holding this one until the next minor release. Thanks!

@david-poindexter
Copy link
Copy Markdown
Contributor

Hi @david-poindexter , can you please help to check whether this PR can be merged or there is anything else need to be done? thx

@zyhfish I'm with @mitchelsellers here - are there plans for an implementation to be provided?

@mitchelsellers
Copy link
Copy Markdown
Contributor

Just to update everyone since this is a public channel.

I've been in communication offline with @zyhfish about what is needed to accept/merge this item

@zyhfish
Copy link
Copy Markdown
Contributor Author

zyhfish commented Aug 23, 2023

Hi @mitchelsellers @david-poindexter , the gmail/exchange implementations have been added, can you please help to check? thx

@valadas
Copy link
Copy Markdown
Contributor

valadas commented Aug 23, 2023

Awesome! Thanks @zyhfish I'll try my best to test/review tomorrow...

@valadas
Copy link
Copy Markdown
Contributor

valadas commented Aug 23, 2023

@zyhfish I have fixed a couple of stylecop issues that prevented the build from running. Now we have 2 failing tests, can you help on those ?

@bdukes
Copy link
Copy Markdown
Contributor

bdukes commented Aug 23, 2023

I just pushed a change to those tests 🤞🏻

@bdukes
Copy link
Copy Markdown
Contributor

bdukes commented Aug 24, 2023

I pushed a few commits to clean up the code, moving from ServiceLocator to DI and using async where available, among other things.

Use async where available
Use file-scoped namespaces
Remove unused code
@bdukes bdukes force-pushed the task/smtp-oauth-interface branch from e0cbd5f to 8f63392 Compare August 24, 2023 17:14
@valadas
Copy link
Copy Markdown
Contributor

valadas commented Aug 24, 2023

I installed this build and managed to figure out how to get OAuth connected which worked, but then sending a test email fails with:
image

@valadas
Copy link
Copy Markdown
Contributor

valadas commented Aug 24, 2023

@zyhfish do you have some sort of documentation on the process to use Exchange and/or google ones. I managed to figure out to get oauth working with exchange but it apparently needs also to have smtp configured and I am not sure what that should be set to...

Copy link
Copy Markdown
Contributor

@valadas valadas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so the MailKit repository has some docs for both Google and Exchange and I managed to test the google one and it works fine. For Exchange, my Microsoft accounts are all messed up and I could not get it to find the right tenant and stuff. But I got to the OAuth authentication part working, so this looks good to me. Thanks @zyhfish

@mitchelsellers mitchelsellers merged commit c5cf724 into dnnsoftware:develop Aug 29, 2023
@zyhfish zyhfish deleted the task/smtp-oauth-interface branch August 29, 2023 23:58
@dowdian
Copy link
Copy Markdown

dowdian commented Sep 14, 2023

I am using the new DNN_Platform_9.13.0-rc0001 in a test environment to attempt to connect it to my Exchange instance via OAuth 2.0. I have succeeded in connecting to the Registered App using the new provider:
image

...and I have provided the App with the following permissions:
image

...further, I have used the New-ServicePrincipal Cmdlet followed by the Add-MailboxPermission Cmdlet to give the App permissions to a particular mailbox using the Exchange Online PowerShell.

Basically, I followed the instructions here: https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#use-client-credentials-grant-flow-to-authenticate-smtp-imap-and-pop-connections

However, when I attempt to send a test email, I get the following error message:

AbsoluteURL:/API/PersonaBar/ServerSettingsSmtpHost/SendTestEmail
DefaultDataProvider:DotNetNuke.Data.SqlDataProvider, DotNetNuke
ExceptionGUID:2ffa6bd3-180d-4813-a597-17ec4aaf1961
AssemblyVersion:
PortalId:-1
UserId:-1
TabId:-1
RawUrl:
Referrer:
UserAgent:
ExceptionHash:uXNMQ/JMz4Kn9Q3nTNmjBWMi2QA=
Message:535: 5.7.3 Authentication unsuccessful [FR2P281CA0175.DEUP281.PROD.OUTLOOK.COM 2023-09-14T10:33:30.710Z 08DBB4BB46D82F30]
StackTrace:
   at MailKit.Net.Smtp.SmtpClient.<AuthenticateAsync>d__86.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MailKit.Net.Smtp.SmtpClient.Authenticate(SaslMechanism mechanism, CancellationToken cancellationToken)
   at DotNetNuke.Services.Mail.MailKitMailProvider.OAuthSmtpClient.Authenticate(String username, String token)
   at Dnn.ExchangeOnlineAuthProvider.Components.ExchangeOnlineOAuthProvider.Authorize(Int32 portalId, IOAuth2SmtpClient smtpClient)
   at DotNetNuke.Services.Mail.MailKitMailProvider.SendMail(MailInfo mailInfo, SmtpInfo smtpInfo)
InnerMessage:
InnerStackTrace:
Source:MailKit
FileName:
FileLineNumber:0
FileColumnNumber:0
Method:
Server Name: webwk00000F

Am I missing something? Am I posting this question in the wrong place? Any assistance would be most appreciated.

Thank you for all of your work on making this and all the other features of DNN.

PS. I should add that this DNN instance is running in Azure as an App Service on the same tenant as the Exchange server. I'm not sure if that makes a difference, but I thought it was worth stating.

@valadas
Copy link
Copy Markdown
Contributor

valadas commented Sep 14, 2023

@zyhfish I was also able to do the oAuth part and got into some other issues afterwards, so I was only able to properly test the google one. Do you have some guidance or docs we could add to the feature to help people setup ?

@dowdian
Copy link
Copy Markdown

dowdian commented Sep 18, 2023

@zyhfish I have re-read the instructions I linked to above and confirmed that my configuration is set up correctly. I also added the delegated permission Mail.Send to see if that might have an effect.
image
It did not.

@dowdian
Copy link
Copy Markdown

dowdian commented Sep 22, 2023

@zyhfish and @valadas, has there been any news on this change? Thank you.

@valadas
Copy link
Copy Markdown
Contributor

valadas commented Sep 22, 2023

@dowdian unfortunatelly not, I don't know enough in the azure world to even know how to begin troubleshooting this. @zyhfish told me he tested it, so I guess it's just a matter of jiggling all the handles right... One think you may want to double-check, are you sending the test emails with a "from" address that matches the exchange account address ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add OAuth 2.0 token-based support for sending emails

6 participants