Created
January 14, 2022 00:32
-
-
Save gscales/8a8eafc85029ce31f01f0a289469d46d to your computer and use it in GitHub Desktop.
CustomTokenCredentials implmenation
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
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using Microsoft.Exchange.WebServices.Data; | |
using System.Threading.Tasks; | |
using System.Net; | |
using System.Security; | |
using Microsoft.Identity.Client; | |
using System.Net.Http; | |
using System.Net.Http.Headers; | |
using Newtonsoft.Json.Linq; | |
namespace BasicToOAuthImplmentation | |
{ | |
public class MSALDelegateTokenClass : Microsoft.Exchange.WebServices.Data.Credentials.CustomTokenCredentials | |
{ | |
private const string GraphScope = "https://graph.microsoft.com/User.Read"; | |
private string ClientId { get; set; } | |
private string TenantId { get; set; } | |
private string RedirectUri { get; set; } | |
private string EWSAuthScope { get; set; } = "https://outlook.office.com/EWS.AccessAsUser.All"; | |
public MSALDelegateTokenClass(string clientId, string tenantid, string redirectUri) | |
{ | |
ClientId = clientId; | |
TenantId = tenantid; | |
RedirectUri = redirectUri; | |
} | |
public IPublicClientApplication app { get; set; } | |
public override string GetCustomToken() | |
{ | |
if (app == null) | |
{ | |
PublicClientApplicationBuilder pcaConfig = PublicClientApplicationBuilder.Create(ClientId).WithTenantId(TenantId); | |
app = pcaConfig.WithRedirectUri(RedirectUri).Build(); | |
} | |
var accounts = app.GetAccountsAsync().GetAwaiter().GetResult(); | |
AuthenticationResult result = null; | |
try | |
{ | |
result = app.AcquireTokenSilent(new[] { EWSAuthScope }, accounts.FirstOrDefault()) | |
.ExecuteAsync().GetAwaiter().GetResult(); | |
} | |
catch (MsalUiRequiredException ex) | |
{ | |
// A MsalUiRequiredException happened on AcquireTokenSilent. | |
// This indicates you need to call AcquireTokenInteractive to acquire a token | |
try | |
{ | |
result = app.AcquireTokenInteractive(new[] { EWSAuthScope }) | |
.ExecuteAsync().GetAwaiter().GetResult(); | |
} | |
catch (MsalException msalex) | |
{ | |
Console.WriteLine(msalex.Message); | |
// | |
} | |
} | |
catch (Exception ex) | |
{ | |
throw; | |
} | |
return "Bearer " + result.AccessToken; | |
} | |
public string GetUserName() | |
{ | |
if (app == null) | |
{ | |
var Token = GetCustomToken(); | |
} | |
var accounts = app.GetAccountsAsync().GetAwaiter().GetResult(); | |
return accounts.FirstOrDefault().Username; | |
} | |
public string GetEmailAddress() | |
{ | |
if (app == null) | |
{ | |
var token = GetCustomToken(); | |
} | |
var accounts = app.GetAccountsAsync().GetAwaiter().GetResult(); | |
var graphToken = app.AcquireTokenSilent(new[] { GraphScope }, accounts.FirstOrDefault()) | |
.ExecuteAsync().GetAwaiter().GetResult(); | |
using (var client = new HttpClient()) | |
{ | |
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); | |
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", graphToken.AccessToken); | |
var profileResult = client.GetAsync("https://graph.microsoft.com/v1.0/me?$select=mail").GetAwaiter().GetResult(); | |
dynamic profile = JObject.Parse(profileResult.Content.ReadAsStringAsync().GetAwaiter().GetResult()); | |
return profile.mail; | |
} | |
} | |
public string SetAuthenticationScope(string emailAddress) | |
{ | |
string autodiscoverv2Endpoint = $"https://outlook.office365.com/autodiscover/autodiscover.json/v1.0/{emailAddress}?Protocol=EWS"; | |
using (var client = new HttpClient()) | |
{ | |
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (compatible; AcmeInc/1.0)"); | |
dynamic adResponse = JObject.Parse(client.GetAsync(autodiscoverv2Endpoint).GetAwaiter().GetResult().Content.ReadAsStringAsync().GetAwaiter().GetResult()); | |
if(adResponse.Url != null) | |
{ | |
EWSAuthScope = "https://" + new Uri(adResponse.Url.ToString()).Host + "/EWS.AccessAsUser.All"; | |
} | |
return adResponse.Url; | |
} | |
} | |
} | |
public class MSALAppTokenClass : Microsoft.Exchange.WebServices.Data.Credentials.CustomTokenCredentials | |
{ | |
private string ClientId { get; set; } | |
private string TenantId { get; set; } | |
private SecureString ClientSecret { get; set; } | |
private string EWSAuthScope { get; set; } = "https://outlook.office.com/.default"; | |
public MSALAppTokenClass(string clientId, string tenantid, SecureString clientSecret) | |
{ | |
ClientId = clientId; | |
TenantId = tenantid; | |
ClientSecret = clientSecret; | |
} | |
public IConfidentialClientApplication app { get; set; } | |
public override string GetCustomToken() | |
{ | |
if (app == null) | |
{ | |
app = ConfidentialClientApplicationBuilder.Create(ClientId) | |
.WithClientSecret(new System.Net.NetworkCredential(string.Empty, ClientSecret).Password) | |
.WithTenantId(TenantId) | |
.Build(); | |
} | |
AuthenticationResult result = null; | |
try | |
{ | |
result = app.AcquireTokenForClient(new[] { EWSAuthScope }).ExecuteAsync().Result; | |
} | |
catch (Exception ex) | |
{ | |
throw; | |
} | |
return "Bearer " + result.AccessToken; | |
} | |
public string SetAuthenticationScope(string emailAddress) | |
{ | |
string autodiscoverv2Endpoint = $"https://outlook.office365.com/autodiscover/autodiscover.json/v1.0/{emailAddress}?Protocol=EWS"; | |
using (var client = new HttpClient()) | |
{ | |
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (compatible; AcmeInc/1.0)"); | |
dynamic adResponse = JObject.Parse(client.GetAsync(autodiscoverv2Endpoint).GetAwaiter().GetResult().Content.ReadAsStringAsync().GetAwaiter().GetResult()); | |
if (adResponse.Url != null) | |
{ | |
EWSAuthScope = "https://" + new Uri(adResponse.Url.ToString()).Host + "/.default"; | |
} | |
return adResponse.Url; | |
} | |
} | |
} | |
internal class Program | |
{ | |
static void Main(string[] args) | |
{ | |
TestDelegatePermissions(); | |
List<string> mailboxesToAccess = new List<string>{ | |
"[email protected]", | |
"[email protected]" | |
}; | |
TestApplicationPermissions(mailboxesToAccess,new NetworkCredential("", "...").SecurePassword); | |
} | |
static void TestDelegatePermissions() | |
{ | |
MSALDelegateTokenClass delegateCreds = new MSALDelegateTokenClass("9d5d77a6-fe09-473e-8931-958f15f1a96b", "1c3a18bf-da31-4f6c-a404-2c06c9cf5ae4", "msal9d5d77a6-fe09-473e-8931-958f15f1a96b://auth"); | |
var emailAddress = delegateCreds.GetEmailAddress(); | |
var ewsURL = delegateCreds.SetAuthenticationScope(emailAddress); | |
ExchangeService exchangeService = new ExchangeService(); | |
exchangeService.Credentials = delegateCreds; | |
// For Autodiscover V1 use | |
// exchangeService.AutodiscoverUrl(emailAddress, RedirectionUrlValidationCallback); | |
exchangeService.Url = new Uri(ewsURL); | |
exchangeService.HttpHeaders.Add("X-AnchorMailbox", emailAddress); | |
var InboxFolder = Folder.Bind(exchangeService, WellKnownFolderName.Inbox); | |
Console.WriteLine("Done"); | |
} | |
static void TestApplicationPermissions(List<String> mailboxesToAccess,SecureString clientSecret) | |
{ | |
ExchangeService exchangeService = new ExchangeService(); | |
MSALAppTokenClass mSALAppToken = new MSALAppTokenClass("c957131d-b228-494e-b1b8-32b6605fadb9", "1c3a18bf-da31-4f6c-a404-2c06c9cf5ae4", clientSecret); | |
exchangeService.Credentials = mSALAppToken; | |
foreach(String mailboxToAccess in mailboxesToAccess) | |
{ | |
exchangeService.Url = new Uri(mSALAppToken.SetAuthenticationScope(mailboxToAccess)); | |
exchangeService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, mailboxToAccess); | |
exchangeService.HttpHeaders.Remove("X-AnchorMailbox"); | |
exchangeService.HttpHeaders.Add("X-AnchorMailbox", mailboxToAccess); | |
var InboxFolder = Folder.Bind(exchangeService, WellKnownFolderName.Inbox); | |
Console.WriteLine("Mailbox " + mailboxToAccess); | |
} | |
Console.WriteLine("Done"); | |
} | |
private static bool RedirectionUrlValidationCallback(string redirectionUrl) | |
{ | |
// The default for the validation callback is to reject the URL. | |
bool result = false; | |
Uri redirectionUri = new Uri(redirectionUrl); | |
// Validate the contents of the redirection URL. In this simple validation | |
// callback, the redirection URL is considered valid if it is using HTTPS | |
// to encrypt the authentication credentials. | |
if (redirectionUri.Scheme == "https") | |
{ | |
result = true; | |
} | |
return result; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment