-
-
Notifications
You must be signed in to change notification settings - Fork 822
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update ExchangeOAuth2.md for SMTP auth
Based on a PR by @yorickdevries Fixes pr #1763
- Loading branch information
Showing
1 changed file
with
104 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,38 +37,57 @@ nuget package for obtaining the access token which will be needed by MailKit to | |
server. | ||
|
||
```csharp | ||
const string EmailAddress = "[email protected]"; | ||
|
||
var options = new PublicClientApplicationOptions { | ||
ClientId = "Application (client) ID", | ||
TenantId = "Directory (tenant) ID", | ||
|
||
// Use "https://login.microsoftonline.com/common/oauth2/nativeclient" for apps using | ||
// embedded browsers or "http://localhost" for apps that use system browsers. | ||
RedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient" | ||
}; | ||
static async Task<AuthenticationResult> GetPublicClientOAuth2CredentialsAsync (string protocol, string emailAddress, CancellationToken cancellationToken = default) | ||
{ | ||
var options = new PublicClientApplicationOptions { | ||
ClientId = "Application (client) ID", | ||
TenantId = "Directory (tenant) ID", | ||
|
||
// Use "https://login.microsoftonline.com/common/oauth2/nativeclient" for apps using | ||
// embedded browsers or "http://localhost" for apps that use system browsers. | ||
RedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient" | ||
}; | ||
|
||
var publicClientApplication = PublicClientApplicationBuilder | ||
.CreateWithApplicationOptions (options) | ||
.Build (); | ||
var publicClientApplication = PublicClientApplicationBuilder | ||
.CreateWithApplicationOptions (options) | ||
.Build (); | ||
|
||
string[] scopes; | ||
|
||
var scopes = new string[] { | ||
"email", | ||
"offline_access", | ||
"https://outlook.office.com/IMAP.AccessAsUser.All", // Only needed for IMAP | ||
//"https://outlook.office.com/POP.AccessAsUser.All", // Only needed for POP | ||
//"https://outlook.office.com/SMTP.Send", // Only needed for SMTP | ||
}; | ||
|
||
AuthenticationResult? result; | ||
|
||
try { | ||
// First, check the cache for an auth token. | ||
result = await publicClientApplication.AcquireTokenSilent (scopes, EmailAddress).ExecuteAsync (); | ||
} catch (MsalUiRequiredException) { | ||
// If that fails, then try getting an auth token interactively. | ||
result = await publicClientApplication.AcquireTokenInteractive (scopes).WithLoginHint (EmailAddress).ExecuteAsync (); | ||
if (protocol.Equals ("IMAP", StringComparison.OrdinalIgnoreCase)) { | ||
scopes = new string[] { | ||
"email", | ||
"offline_access", | ||
"https://outlook.office.com/IMAP.AccessAsUser.All" | ||
}; | ||
} else if (protocol.Equals ("POP", StringComparison.OrdinalIgnoreCase)) { | ||
scopes = new string[] { | ||
"email", | ||
"offline_access", | ||
"https://outlook.office.com/POP.AccessAsUser.All" | ||
}; | ||
} else { | ||
scopes = new string[] { | ||
"email", | ||
"offline_access", | ||
"https://outlook.office.com/SMTP.Send" | ||
}; | ||
} | ||
|
||
try { | ||
// First, check the cache for an auth token. | ||
return await publicClientApplication.AcquireTokenSilent (scopes, emailAddress).ExecuteAsync (cancellationToken); | ||
} catch (MsalUiRequiredException) { | ||
// If that fails, then try getting an auth token interactively. | ||
return await publicClientApplication.AcquireTokenInteractive (scopes).WithLoginHint (emailAddress).ExecuteAsync (cancellationToken); | ||
} | ||
} | ||
``` | ||
|
||
#### IMAP (using PublicClientApplication) | ||
|
||
```csharp | ||
var result = await GetPublicClientOAuth2CredentialsAsync ("IMAP", "[email protected]"); | ||
|
||
// Note: We always use result.Account.Username instead of `Username` because the user may have selected an alternative account. | ||
var oauth2 = new SaslMechanismOAuth2 (result.Account.Username, result.AccessToken); | ||
|
@@ -80,6 +99,21 @@ using (var client = new ImapClient ()) { | |
} | ||
``` | ||
|
||
#### SMTP (using PublicClientApplication) | ||
|
||
```csharp | ||
var result = await GetPublicClientOAuth2CredentialsAsync ("SMTP", "[email protected]"); | ||
|
||
// Note: We always use result.Account.Username instead of `Username` because the user may have selected an alternative account. | ||
var oauth2 = new SaslMechanismOAuth2 (result.Account.Username, result.AccessToken); | ||
|
||
using (var client = new SmtpClient ()) { | ||
await client.ConnectAsync ("smtp.office365.com", 587, SecureSocketOptions.StartTls); | ||
await client.AuthenticateAsync (oauth2); | ||
await client.DisconnectAsync (true); | ||
} | ||
``` | ||
|
||
Note: Once you've acquired an auth token using the interactive method above, you can avoid prompting the user | ||
if you cache the `result.Account` information and then silently reacquire auth tokens in the future using | ||
the following code: | ||
|
@@ -139,21 +173,36 @@ nuget package for obtaining the access token which will be needed by MailKit to | |
server. | ||
|
||
```csharp | ||
var confidentialClientApplication = ConfidentialClientApplicationBuilder.Create (clientId) | ||
.WithAuthority ($"https://login.microsoftonline.com/{tenantId}/v2.0") | ||
.WithCertificate (certificate) // or .WithClientSecret (clientSecret) | ||
.Build (); | ||
|
||
var scopes = new string[] { | ||
// For IMAP and POP3, use the following scope | ||
"https://ps.outlook.com/.default" | ||
static async Task<AuthenticationResult> GetConfidentialClientOAuth2CredentialsAsync (string protocol, CancellationToken cancellationToken = default) | ||
{ | ||
var confidentialClientApplication = ConfidentialClientApplicationBuilder.Create (clientId) | ||
.WithAuthority ($"https://login.microsoftonline.com/{tenantId}/v2.0") | ||
.WithCertificate (certificate) // or .WithClientSecret (clientSecret) | ||
.Build (); | ||
|
||
string[] scopes; | ||
|
||
if (protocol.Equals ("SMTP", StringComparison.OrdinalIgnoreCase)) { | ||
scopes = new string[] { | ||
// For SMTP, use the following scope | ||
"https://outlook.office365.com/.default" | ||
}; | ||
} else { | ||
scopes = new string[] { | ||
// For IMAP and POP3, use the following scope | ||
"https://ps.outlook.com/.default" | ||
}; | ||
} | ||
|
||
return await confidentialClientApplication.AcquireTokenForClient (scopes).ExecuteAsync (cancellationToken); | ||
} | ||
``` | ||
|
||
// For SMTP, use the following scope | ||
// "https://outlook.office365.com/.default" | ||
}; | ||
#### IMAP (using ConfidentialClientApplication) | ||
|
||
var authToken = await confidentialClientApplication.AcquireTokenForClient (scopes).ExecuteAsync (); | ||
var oauth2 = new SaslMechanismOAuth2 (accountEmailAddress, authToken.AccessToken); | ||
```csharp | ||
var result = await GetConfidentialClientOAuth2CredentialsAsync ("IMAP"); | ||
var oauth2 = new SaslMechanismOAuth2 ("[email protected]", result.AccessToken); | ||
|
||
using (var client = new ImapClient ()) { | ||
await client.ConnectAsync ("outlook.office365.com", 993, SecureSocketOptions.SslOnConnect); | ||
|
@@ -162,6 +211,19 @@ using (var client = new ImapClient ()) { | |
} | ||
``` | ||
|
||
#### SMTP (using ConfidentialClientApplication) | ||
|
||
```csharp | ||
var result = await GetConfidentialClientOAuth2CredentialsAsync ("SMTP"); | ||
var oauth2 = new SaslMechanismOAuth2 ("[email protected]", result.AccessToken); | ||
|
||
using (var client = new SmtpClient ()) { | ||
await client.ConnectAsync ("smtp.office365.com", 587, SecureSocketOptions.StartTls); | ||
await client.AuthenticateAsync (oauth2); | ||
await client.DisconnectAsync (true); | ||
} | ||
``` | ||
|
||
## Additional Resources | ||
|
||
For more information, check out the [Microsoft.Identity.Client](https://docs.microsoft.com/en-us/dotnet/api/microsoft.identity.client?view=azure-dotnet) | ||
|