public async Task Run( #pragma warning disable CA1801 // Remove unused parameter [TimerTrigger("0 */1 * * * *")] TimerInfo timerInfo) #pragma warning restore CA1801 // Remove unused parameter { if (_appSettings is null || _appSettings.Value is null || _appSettings.Value.EmailToSmsSettings is null || string.IsNullOrEmpty(_appSettings.Value.EmailToSmsSettings.ResellerApiKey) || string.IsNullOrEmpty(_appSettings.Value.EmailToSmsSettings.ResellerApiSecret) || string.IsNullOrEmpty(_appSettings.Value.EmailToSmsSettings.MailboxAddress)) { _logger.LogWarning("Missing Email to SMS configuration. Aborting!"); return; } // TODO Move GraphServiceClient and auth to DI IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder .Create(_appSettings.Value.EmailToSmsSettings.MicrosoftGraphApiClientId) .WithTenantId(_appSettings.Value.EmailToSmsSettings.MicrosoftGraphApiTenantID) .WithClientSecret(_appSettings.Value.EmailToSmsSettings.MicrosoftGraphApiSecret) .Build(); var graphServiceClient = new GraphServiceClient(new ClientCredentialProvider(confidentialClientApplication)); // TODO, make this better or move to config. // Hard coding OWNit conveyancing for now. var clientIds = new Dictionary <string, int>(); clientIds.Add("b6rtmjuc", 87627); var messageExceptions = new List <Exception>(); ISMSService smsService = new BurstSMSService( _appSettings.Value.EmailToSmsSettings.ResellerApiKey, _appSettings.Value.EmailToSmsSettings.ResellerApiSecret); var mailboxEmailAddress = _appSettings.Value.EmailToSmsSettings.MailboxAddress; var currentMessageRequest = graphServiceClient.Users[mailboxEmailAddress] .MailFolders.Inbox .Messages .Request() .Select(m => (new { m.Id, m.From, m.Subject, m.Body, m.InternetMessageHeaders, m.ParentFolderId, m.Categories })); // Get IDs of standard folders to be able to move messages once they're processed var inboxFolder = await graphServiceClient.Users[mailboxEmailAddress].MailFolders.Inbox.Request().GetAsync(); var invalidMessagesFolder = await graphServiceClient.Users[mailboxEmailAddress].EnsureFolder("Inbox", _invalidMessagesFolderName); var clientsFolder = await graphServiceClient.Users[mailboxEmailAddress].EnsureFolder("Inbox", _clientsFolderName); var clientFolderIds = new Dictionary <string, string>(); // Loop through pages do { var currentMessages = await currentMessageRequest.GetAsync(); // Loop through messages in current page foreach (var message in currentMessages) { var parsedBodyText = string.Empty; try { var messageBodyHtmlDoc = new HtmlAgilityPack.HtmlDocument(); messageBodyHtmlDoc.LoadHtml(message.Body.Content); // - Using InnerText strips HTML. // - Then we replace Windows CRLF's in case there are any. We must strip these first because if we // stripped LF first then we'd have a bunch of CR's floating around all alone. // - Then strip Unix LF's, in case there are any. parsedBodyText = HttpUtility.HtmlDecode(messageBodyHtmlDoc.DocumentNode.InnerText) .Replace("\r\n", "", StringComparison.Ordinal) .Replace("\n", "", StringComparison.Ordinal); var smsEmailJson = JsonConvert.DeserializeObject <SmsEmailJson>(parsedBodyText); // Check for required values if (!smsEmailJson.IsValid(out List <string> missingFields)) { // Create a support ticket with the details. var ticketResponse = await _mediator.Send(new CreateTicketCommand() { FromEmail = GetBestEmail(smsEmailJson), TicketPriority = TicketPriority.Low, Subject = GenerateTicketSubject(smsEmailJson), DescriptionHtml = GenerateMessageMissingFields(smsEmailJson, missingFields) }); // Store the category for a bad response from the gateway await graphServiceClient.AddMessageCategoriesAsync(mailboxEmailAddress, message, new[] { $"Ticket: {ticketResponse.Id}", EmailCategories.MissingRequiredData }); await graphServiceClient.Users[mailboxEmailAddress].Messages[message.Id].Move(invalidMessagesFolder.Id).Request().PostAsync(); } else { // Data appears valid, ensure folder exists for client, checking cached IDs first if (smsEmailJson.SmsGo.Value == true) { // Send SMS. Will throw if there's a problem. var clientId = clientIds[smsEmailJson.SmsKey]; var cleanMobileNumber = SMSHelper.CleanMobileNumber(smsEmailJson.MobileNumber); if (_appSettings.Value.EmailToSmsSettings.SkipSendingSms) { _logger.LogWarning( "Skipping SMS Send: Client ID: {clientId}, Mobile Number: {mobileNumber}, Message: '{message}', Send At: {sendAt}, Replies to Email: {repliesToEmail}", clientId, cleanMobileNumber, smsEmailJson.Message, smsEmailJson.SendAt, smsEmailJson.RepliesToEmail); await graphServiceClient.AddMessageCategoriesAsync(mailboxEmailAddress, message, EmailCategories.SmsSkipped); } else { var sendAt = SMSHelper.LocalisedSendAt(smsEmailJson.SendAt, smsEmailJson.SendAtTimeZoneID, _logger); if (sendAt != smsEmailJson.SendAt) { _logger.LogInformation( "SendAt was translated from '{SendAt}' to '{ParsedSendAt}' based on supplied Time Zone ID '{TimeZoneID}'.", smsEmailJson.SendAt, sendAt, smsEmailJson.SendAtTimeZoneID); } var sendSmsResponse = await smsService.SendSms(clientId, cleanMobileNumber, smsEmailJson.Message, sendAt, smsEmailJson.RepliesToEmail); if (sendSmsResponse.Error is null) { // Create a support ticket with the details. var ticketResponse = await _mediator.Send(new CreateTicketCommand() { FromEmail = GetBestEmail(smsEmailJson), TicketPriority = TicketPriority.Low, Subject = GenerateTicketSubject(smsEmailJson), DescriptionHtml = GenerateMessageUnknownGatewayResponse(smsEmailJson) }); await graphServiceClient.AddMessageCategoriesAsync(mailboxEmailAddress, message, $"Ticket: {ticketResponse.Id}", EmailCategories.UnknownError); } else if (sendSmsResponse.Error.Code == "SUCCESS") { _logger.LogInformation( "SMS Sent: Cost: {cost}, Sms Message ID: {messageId}, Code: {code}, Descripton: {description}, Send At: {sendAt}", sendSmsResponse.Cost, sendSmsResponse.MessageId, sendSmsResponse.Error?.Code, sendSmsResponse.Error?.Description, sendSmsResponse.SendAt); await graphServiceClient.AddMessageCategoriesAsync(mailboxEmailAddress, message, EmailCategories.SmsSent); } else { // Create a support ticket with the details. var ticketResponse = await _mediator.Send(new CreateTicketCommand() { FromEmail = GetBestEmail(smsEmailJson), TicketPriority = TicketPriority.Low, Subject = GenerateTicketSubject(smsEmailJson), DescriptionHtml = GenerateMessageGatewayError(message, smsEmailJson, sendSmsResponse) }); // Store the category for a bad response from the gateway await graphServiceClient.AddMessageCategoriesAsync(mailboxEmailAddress, message, $"Ticket: {ticketResponse.Id}", EmailCategories.GatewayBadRequest); _logger.LogError( "SMS Gateway returned error response. Code: '{code}', Description: '{description}'.", sendSmsResponse.Error.Code, sendSmsResponse.Error.Description); } } } else { await graphServiceClient.AddMessageCategoriesAsync(mailboxEmailAddress, message, EmailCategories.SmsGoFalse); } // Finally, move to client folder string currentClientFolderId; if (clientFolderIds.ContainsKey(smsEmailJson.OrgKey)) { currentClientFolderId = clientFolderIds[smsEmailJson.OrgKey]; } else { var currentClientFolder = await graphServiceClient.Users[mailboxEmailAddress].EnsureFolder(clientsFolder, smsEmailJson.OrgKey); currentClientFolderId = currentClientFolder.Id; } await graphServiceClient.Users[mailboxEmailAddress].Messages[message.Id].Move(currentClientFolderId).Request().PostAsync(); } } catch (Exception ex) { messageExceptions.Add(ex); // Just in case something goes wrong, we don't want a failure during cleanup to prevent processing future messages try { if (ex is JsonReaderException jex) { // Create a support ticket with the details. var ticketResponse = await _mediator.Send(new CreateTicketCommand() { FromEmail = _fallbackEmailFrom, TicketPriority = TicketPriority.Low, Subject = $"SMS Failure - couldn't parse JSON", DescriptionHtml = GenerateMessageJsonParseError(parsedBodyText, jex) }); await graphServiceClient.AddMessageCategoriesAsync(mailboxEmailAddress, message, $"Ticket: {ticketResponse.Id}", EmailCategories.InvalidJson); } else { var ticketResponse = await _mediator.Send(new CreateTicketCommand() { FromEmail = _fallbackEmailFrom, TicketPriority = TicketPriority.Low, Subject = $"SMS Failure - Unknown error", DescriptionHtml = GenerateMessageUnknownException(message, ex) }); await graphServiceClient.AddMessageCategoriesAsync(mailboxEmailAddress, message, EmailCategories.UnknownError); } // Move to generic invalid message folder await graphServiceClient.Users[mailboxEmailAddress].Messages[message.Id].Move(invalidMessagesFolder.Id).Request().PostAsync(); } catch (Exception cleanupEx) { _logger.LogError(cleanupEx, "Exception encountered during message cleanup!"); messageExceptions.Add(cleanupEx); } } } currentMessageRequest = currentMessages.NextPageRequest; } while (currentMessageRequest != null); if (messageExceptions.Count > 0) { throw new AggregateException("Processing of one or more messages has failed", messageExceptions); } }
public static async Task Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log) { var storage = CloudStorageAccount.Parse(StorageAccount); var cloudTableClient = storage.CreateCloudTableClient(); var groupsStatisticsTable = cloudTableClient.GetTableReference("groupsstatistics"); var groupsOwnersTable = cloudTableClient.GetTableReference("groupowners"); var groupsGuestsTable = cloudTableClient.GetTableReference("groupguests"); await groupsStatisticsTable.CreateIfNotExistsAsync().ConfigureAwait(false); await groupsOwnersTable.CreateIfNotExistsAsync().ConfigureAwait(false); await groupsGuestsTable.CreateIfNotExistsAsync().ConfigureAwait(false); // Get a Bearer Token with the App var httpClient = new HttpClient(); string token; try { var app = ConfidentialClientApplicationBuilder.Create(AppId) .WithAuthority($"https://login.windows.net/{TenantId}/oauth2/token") .WithRedirectUri("https://governance365function") .WithClientSecret(AppSecret) .Build(); var authenticationProvider = new MsalAuthenticationProvider(app, Scopes); token = authenticationProvider.GetTokenAsync().Result; httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); } catch (Exception) { throw; } httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); JObject groupsPageJson = null; do { if (string.IsNullOrEmpty(groupsPageJson?["@odata.nextLink"]?.ToString())) { // first group page groupsPageJson = JObject.Parse(await(await httpClient.GetAsync(groupsUrl + groupsSelection).ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false)); } else { // next group page groupsPageJson = JObject.Parse(await(await httpClient.GetAsync(groupsPageJson?["@odata.nextLink"]?.ToString()).ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false)); } if (!(groupsPageJson["value"] is JArray groupsPage)) { continue; } // Batch operation to store pages of groups in Azure table storage var groupPageBatchOperation = new TableBatchOperation(); foreach (var group in groupsPage) { var memberCount = 0; var ownerCount = 0; var guestCount = 0; #region PROCESS GROUP TYPE AND DYNAMIC MEMBERSHIP var groupType = string.Empty; var dynamicMemberShip = false; var yammerProvisionedOffice365Group = false; foreach (var type in (JArray)group["groupTypes"]) { if (type.ToString().Equals("Unified")) { if (group["resourceProvisioningOptions"].ToString().Contains("Team")) { groupType = "Team"; } else { groupType = "Office365 Group"; } if (group["creationOptions"].ToString().Contains("YammerProvisioning")) { yammerProvisionedOffice365Group = true; } } if (type.ToString().Equals("DynamicMembership")) { dynamicMemberShip = true; } } if (groupType.Equals(string.Empty)) { if (!group["mailEnabled"].ToString().ToLower().Equals("true")) { groupType = "Security Group"; } else { if (!group["securityEnabled"].ToString().ToLower().Equals("true")) { groupType = "Distribution Group"; } else { groupType = "Mail-enabled Security Group"; } } } #endregion const string groupMemberSelection = "?$select=id,userPrincipalName,displayName,userType,accountEnabled"; var groupMembersUrl = $"https://graph.microsoft.com/v1.0/groups/{group["id"]?.ToString()}/members{groupMemberSelection}"; var groupOwnersUrl = $"https://graph.microsoft.com/v1.0/groups/{group["id"]?.ToString()}/owners{groupMemberSelection}"; #region PROCESS GROUP MEMBERS AND GUESTS var processMemberPageResult = await ProcessGroupMemberPageAsync(groupMembersUrl, group, httpClient, groupsGuestsTable, groupsOwnersTable, false).ConfigureAwait(false); memberCount += processMemberPageResult.MemberCount; guestCount += processMemberPageResult.GuestCount; while (processMemberPageResult.NextLink != null) { processMemberPageResult = await ProcessGroupMemberPageAsync(processMemberPageResult.NextLink, group, httpClient, groupsGuestsTable, groupsOwnersTable, false).ConfigureAwait(false); memberCount += processMemberPageResult.MemberCount; guestCount += processMemberPageResult.GuestCount; } #endregion #region PROCESS GROUP OWNERS var processOwnerPageResult = await ProcessGroupMemberPageAsync(groupOwnersUrl, group, httpClient, groupsGuestsTable, groupsOwnersTable, true).ConfigureAwait(false); ownerCount += processOwnerPageResult.OwnerCount; while (processOwnerPageResult.NextLink != null) { processOwnerPageResult = await ProcessGroupMemberPageAsync(processOwnerPageResult.NextLink, group, httpClient, groupsGuestsTable, groupsOwnersTable, true).ConfigureAwait(false); ownerCount += processOwnerPageResult.OwnerCount; } #endregion var groupStatisticsEntity = new GroupStatisticsItemTableEntity() { PartitionKey = "group", RowKey = group["id"]?.ToString(), Classification = group["classification"]?.ToString(), CreatedDateTime = group["createdDateTime"] != null && !group["createdDateTime"].ToString().Equals(string.Empty) ? DateTimeOffset.Parse(group["createdDateTime"].ToString()) : (DateTimeOffset?)null, DeletedDateTime = group["deletedDateTime"] != null && !group["deletedDateTime"].ToString().Equals(string.Empty) ? DateTimeOffset.Parse(group["deletedDateTime"].ToString()) : (DateTimeOffset?)null, Description = group["description"]?.ToString(), DisplayName = group["displayName"]?.ToString(), GroupType = groupType, DynamicMembership = dynamicMemberShip, Id = group["id"]?.ToString(), Mail = group["mail"]?.ToString(), MailEnabled = group["mailEnabled"]?.ToString(), MailNickname = group["mailNickname"]?.ToString(), OnPremisesSyncEnabled = group["onPremisesSyncEnabled"]?.ToString(), RenewedDateTime = group["renewedDateTime"] != null & !group["renewedDateTime"].ToString().Equals(string.Empty) ? DateTimeOffset.Parse(group["renewedDateTime"].ToString()) : (DateTimeOffset?)null, SecurityEnabled = group["securityEnabled"]?.ToString(), Visibility = group["visibility"]?.ToString(), Guests = guestCount, Owners = ownerCount, Members = memberCount }; groupPageBatchOperation.Add(TableOperation.InsertOrReplace(groupStatisticsEntity)); } await groupsStatisticsTable.ExecuteBatchAsync(groupPageBatchOperation).ConfigureAwait(false); } while (!string.IsNullOrEmpty(groupsPageJson["@odata.nextLink"]?.ToString())); httpClient.Dispose(); }
public async Task ServicePrincipal_OBO_LongRunningProcess_PPE_Async() { //An explanation of the OBO for service principal scenario can be found here https://aadwiki.windows-int.net/index.php?title=App_OBO_aka._Service_Principal_OBO var settings = ConfidentialAppSettings.GetSettings(Cloud.Public); var cert = settings.GetCertificate(); IReadOnlyList <string> middleTierApiScopes = new List <string>() { OBOServicePpeClientID + "/.default" }; IReadOnlyList <string> downstreamApiScopes = new List <string>() { OBOServiceDownStreamApiPpeClientID + "/.default" }; var clientConfidentialApp = ConfidentialClientApplicationBuilder .Create(OBOClientPpeClientID) .WithAuthority(PPEAuthenticationAuthority) .WithCertificate(cert) .WithTestLogging() .Build(); var middletierServiceApp = ConfidentialClientApplicationBuilder .Create(OBOServicePpeClientID) .WithAuthority(PPEAuthenticationAuthority) .WithCertificate(cert) .Build(); var userCacheRecorder = middletierServiceApp.UserTokenCache.RecordAccess(); Trace.WriteLine("1. Upstream client gets an app token"); var authenticationResult = await clientConfidentialApp .AcquireTokenForClient(middleTierApiScopes) .ExecuteAsync() .ConfigureAwait(false); string clientToken = authenticationResult.AccessToken; Trace.WriteLine("2. MidTier kicks off the long running process by getting an OBO token"); string cacheKey = null; authenticationResult = await(middletierServiceApp as ILongRunningWebApi). InitiateLongRunningProcessInWebApi(downstreamApiScopes, clientToken, ref cacheKey) .ExecuteAsync().ConfigureAwait(false); Assert.IsNotNull(authenticationResult.AccessToken); Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); Assert.AreEqual(cacheKey, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.IdentityProvider, authenticationResult.AuthenticationResultMetadata.TokenSource); Assert.IsNull( userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheExpiry, "The cache expiry is not set because there is an RT in the cache"); Trace.WriteLine("3. Later, mid-tier needs the token again, and one is in the cache"); authenticationResult = await(middletierServiceApp as ILongRunningWebApi) .AcquireTokenInLongRunningProcess(downstreamApiScopes, cacheKey) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual(TokenSource.Cache, authenticationResult.AuthenticationResultMetadata.TokenSource); Trace.WriteLine("4. After the original token expires, the mid-tier needs a token again. RT will be used."); TokenCacheHelper.ExpireAllAccessTokens(middletierServiceApp.UserTokenCache as ITokenCacheInternal); authenticationResult = await(middletierServiceApp as ILongRunningWebApi) .AcquireTokenInLongRunningProcess(downstreamApiScopes, cacheKey) .ExecuteAsync() .ConfigureAwait(false); Assert.IsNotNull(authenticationResult.AccessToken); Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); Assert.AreEqual(cacheKey, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.IdentityProvider, authenticationResult.AuthenticationResultMetadata.TokenSource); Assert.IsNull( userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheExpiry, "The cache expiry is not set because there is an RT in the cache"); }
private IConfidentialClientApplication BuildConfidentialClientApplication() => ConfidentialClientApplicationBuilder .Create(AzureAdB2CConfiguration.ClientId) .WithTenantId(AzureAdB2CConfiguration.TenantId) .WithClientSecret(AzureAdB2CConfiguration.ClientSecret) .Build();
private static async Task RunAsync() { AuthConfig authConfig = AuthConfig.ReadJsonFromfile("appsettings.json"); IConfidentialClientApplication application; application = ConfidentialClientApplicationBuilder.Create(authConfig.ClientId) .WithClientSecret(authConfig.ClientSecret) .WithAuthority(new Uri(authConfig.Authority)) .Build(); string[] Resourceids = new string[] { authConfig.ResourceId }; AuthenticationResult result = null; try { result = await application.AcquireTokenForClient(Resourceids).ExecuteAsync(); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Token Acquired \n"); Console.WriteLine(result.AccessToken); Console.ResetColor(); } catch (MsalClientException ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(ex.Message); Console.ResetColor(); } if (!string.IsNullOrEmpty(result.AccessToken)) { var httpclient = new HttpClient(); var defaultrequestHeaders = httpclient.DefaultRequestHeaders; if (defaultrequestHeaders == null || !defaultrequestHeaders.Accept.Any (m => m.MediaType == "application/Json")) { httpclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/Json")); } defaultrequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken); HttpResponseMessage responseMessage = await httpclient.GetAsync(authConfig.BaseAddress); if (responseMessage.IsSuccessStatusCode) { Console.ForegroundColor = ConsoleColor.Green; string json = await responseMessage.Content.ReadAsStringAsync(); Console.WriteLine(json); } else { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"failed to call API: {responseMessage.StatusCode}"); string content = await responseMessage.Content.ReadAsStringAsync(); Console.WriteLine($"Content:{content}"); } Console.ResetColor(); } }
private static async Task RunAsync() { // Read application settings from appsettings.json (tenant ID, app ID, client secret, etc.) AppSettings config = AppSettingsFile.ReadFromJsonFile(); // Initialize the client credential auth provider IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder .Create(config.AppId) .WithTenantId(config.TenantId) .WithClientSecret(config.ClientSecret) .Build(); ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication); // Set up the Microsoft Graph service client with client credentials GraphServiceClient graphClient = new GraphServiceClient(authProvider); Program.PrintCommands(); try { while (true) { Console.Write("Enter command, then press ENTER: "); string decision = Console.ReadLine(); switch (decision.ToLower()) { case "1": await UserService.ListUsers(graphClient);; break; case "2": await UserService.GetUserById(graphClient);; break; case "3": await UserService.GetUserBySignInName(config, graphClient);; break; case "4": await UserService.DeleteUserById(graphClient); break; case "5": await UserService.SetPasswordByUserId(graphClient); break; case "6": await UserService.BulkCreate(config, graphClient); break; case "7": await UserService.ListApplications(graphClient); break; case "help": Program.PrintCommands(); break; case "exit": return; default: Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Invalid command. Enter 'help' to show a list of commands."); Console.ResetColor(); break; } Console.ResetColor(); } } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; var innerException = ex.InnerException; if (innerException != null) { while (innerException != null) { Console.WriteLine(innerException.Message); innerException = innerException.InnerException; } } else { Console.WriteLine(ex.Message); } } finally { Console.ResetColor(); } Console.ReadLine(); }
private async Task <UserProfile> CallGraphAPIOnBehalfOfUser() { string[] scopes = { "user.read" }; UserProfile profile = null; // We will use MSAL.NET to get a token to call the API On Behalf Of the current user try { string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant); // Creating a ConfidentialClientApplication using the Build pattern (https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Client-Applications) var app = ConfidentialClientApplicationBuilder.Create(clientId) .WithAuthority(authority) .WithClientSecret(appKey) .WithRedirectUri(redirectUri) .Build(); // Hooking MSALPerUserSqlTokenCacheProvider class on ConfidentialClientApplication's UserTokenCache. MSALPerUserSqlTokenCacheProvider sqlCache = new MSALPerUserSqlTokenCacheProvider(app.UserTokenCache, dbContext, ClaimsPrincipal.Current); //Grab the Bearer token from the HTTP Header using the identity bootstrap context. This requires SaveSigninToken to be true at Startup.Auth.cs var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as System.IdentityModel.Tokens.BootstrapContext; // Creating a UserAssertion based on the Bearer token sent by TodoListClient request. //urn:ietf:params:oauth:grant-type:jwt-bearer is the grant_type required when using On Behalf Of flow: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow UserAssertion userAssertion = new UserAssertion(bootstrapContext.Token, "urn:ietf:params:oauth:grant-type:jwt-bearer"); // Acquiring an AuthenticationResult for the scope user.read, impersonating the user represented by userAssertion, using the OBO flow AuthenticationResult result = await app.AcquireTokenOnBehalfOf(scopes, userAssertion) .ExecuteAsync(); string accessToken = result.AccessToken; if (accessToken == null) { throw new Exception("Access Token could not be acquired."); } // Call the Graph API and retrieve the user's profile. string requestUrl = String.Format(CultureInfo.InvariantCulture, graphUserUrl, HttpUtility.UrlEncode(tenant)); HttpClient client = new HttpClient(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); HttpResponseMessage response = await client.SendAsync(request); // Return the user's profile. if (response.IsSuccessStatusCode) { string responseString = await response.Content.ReadAsStringAsync(); profile = JsonConvert.DeserializeObject <UserProfile>(responseString); return(profile); } // An unexpected error occurred calling the Graph API. throw new Exception("An unexpected error occurred calling the Graph API."); } catch (MsalUiRequiredException msalServiceException) { /* * If you used the scope `.default` on the client application, the user would have been prompted to consent for Graph API back there * and no incremental consents are required (this exception is not expected). However, if you are using the scope `user_impersonation`, * this exception will be thrown at the first time the API tries to access Graph on behalf of the user for an incremental consent. * You must then, add the logic to delegate the consent screen to your client application here. * This sample doesn't use the incremental consent strategy. */ throw msalServiceException; } catch (Exception ex) { throw ex; } }
static void Main(string[] args) { string requestUrl = ConfigurationManager.AppSettings["RequestURL"]; string ClientID = ConfigurationManager.AppSettings["ClientID"]; string TenantID = ConfigurationManager.AppSettings["TenantID"]; string Authority = $"https://login.microsoftonline.com/{TenantID}/v2.0"; string ClientSecret = ConfigurationManager.AppSettings["ClientSecret"]; string[] scopes = ConfigurationManager.AppSettings["Scopes"].ToString().Split(','); //Setting app with MSAL IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(ClientID) .WithClientSecret(ClientSecret) .WithAuthority(new Uri(Authority)) .Build(); //requesting token AuthenticationResult result = null; try { Task <AuthenticationResult> authTask = app.AcquireTokenForClient(scopes) .ExecuteAsync(); //authTask.Start(); authTask.Wait(); result = authTask.Result; } catch (MsalUiRequiredException ex) { // The application doesn't have sufficient permissions. // - Did you declare enough app permissions during app creation? // - Did the tenant admin grant permissions to the application? } catch (MsalServiceException ex) when(ex.Message.Contains("AADSTS70011")) { // Invalid scope. The scope has to be in the form "https://resourceurl/.default" // Mitigation: Change the scope to be as expected. } // Call Web API var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken); string json = "{'name':'Mr. Testbuddy'}"; var data = new StringContent(json, Encoding.UTF8, "application/json"); Task <HttpResponseMessage> postTask = httpClient.PostAsync(requestUrl, data); postTask.Wait(); var webApiResponse = postTask.Result; Task <string> messageTask = webApiResponse.Content.ReadAsStringAsync(); messageTask.Wait(); Console.WriteLine(messageTask.Result); Console.ReadLine(); }
public static async Task GetSpoDocumentItems(GraphServiceClient graphClient, List <DriveItem> docLibItems, string driveId, CloudBlobContainer container, bool getAcls) { foreach (var item in docLibItems) { if (item.Folder != null) { string ParentFolderPathString = null; string fullFolderNamePath = null; var folderName = item.Name; if (item.ParentReference.Path != null) { var ParentFolderPathSplit = item.ParentReference.Path.Split(":"); if (ParentFolderPathSplit.Length >= 1) { ParentFolderPathString = ParentFolderPathSplit[1]; if (ParentFolderPathString.Length >= 1) { fullFolderNamePath = String.Format("{0}/{1}", ParentFolderPathString, folderName); } else { fullFolderNamePath = folderName; } } } else { fullFolderNamePath = folderName; } var folderItems = await GetFolderContents(graphClient, fullFolderNamePath, driveId); if (folderItems.Count > 0) { await GetSpoDocumentItems(graphClient, folderItems, driveId, container, _getAcls); } } // Let's download the first file we get in the response. if (item.File != null) { // We'll use the file metadata to determine size and the name of the downloaded file // and to get the download URL. if (item.Deleted != null) { if (item.Deleted.State == "deleted") { Console.WriteLine("Deleted Item detected"); var spoItemUrl = await azTableStorage.GetSpoItemEntitiesInPartion(item.Id); //Clean up the Storage account path for the deleted item so we dont index it again await AzureBLOBStorage.DeleteFileFromAzureBLOB(spoItemUrl, container); //Clean up the json metadata file for the above file: string spoItemUrlJson = ($"{spoItemUrl}.json"); await AzureBLOBStorage.DeleteFileFromAzureBLOB(spoItemUrlJson, container); break; } } var driveItemInfo = await graphClient.Drives[driveId].Items[item.Id].Request().GetAsync(); var SPWebUrl = driveItemInfo.WebUrl; var createdAuthorDisplayName = driveItemInfo.CreatedBy.User.DisplayName; var baseFileName = SPWebUrl; var jsonMetadataFileName = String.Format("{0}.json", baseFileName); //Below is for ACL Security trimming extraction which is still work in progress. if (getAcls) { var driveItemPermissions = await graphClient.Drives[driveId].Items[item.Id].Permissions.Request().GetAsync(); foreach (var driveItemPermission in driveItemPermissions) { var grantedDispayName = driveItemPermission.GrantedTo.User.DisplayName; var grantedObjectId = driveItemPermission.GrantedTo.User.Id; //If no ID is present then its a sharepoint group if (grantedObjectId == null) { //var groupLookup = await graphClient.Groups.Request()//.Filter("displayName+eq+true") //.Select(e => new { // e.Id, // e.DisplayName //}) //.GetAsync(); //var groupLookup = await graphClient.Groups.Request().GetAsync(); //var groupLookup = await graphClient.Groups.Request().Filter(string.Format("displayName+eq+{0}", grantedDispayName)).GetAsync(); var scopes = new[] { _spoHostName + "/.default" }; //var scopes = new[] { _spoHostName + "/Sites.FullControl.All" }; //var scopes = new[] { "https://graph.microsoft.com/contacts.read" }; var v1Authority = _authority.Replace("/v2.0", ""); var clientApplication = ConfidentialClientApplicationBuilder.Create(_clientId) .WithAuthority(_authority) .WithClientSecret(_clientSecret) .WithClientId(_clientId) .WithTenantId(_tenantId) .Build(); var result = await clientApplication.AcquireTokenForClient(scopes).ExecuteAsync(); HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", "Bearer " + result.AccessToken); client.DefaultRequestHeaders.Add("Accept", "application/json"); ////setup the client get HttpResponseMessage result2 = await client.GetAsync(String.Format("{0}/_api/Web/SiteGroups/GetByName('{1}')/users", SPWebUrl, grantedDispayName)); //var requestUrl = "https://m365x069897.sharepoint.com/_api/web?$select=Title"; var requestUrl = "https://m365x069897.sharepoint.com/_api/web/SiteGroups/"; HttpResponseMessage result3 = await client.GetAsync(requestUrl); string filter = string.Format("startswith(displayName, {0}", grantedDispayName); //string filter = string.Format("displayName startswith '{0}'", grantedDispayName); var groupLookup = await graphClient.Groups .Request() .Filter($"startswith(displayName, '{grantedDispayName}')") //.Filter(filter) .Select("id, displayName").GetAsync(); var ac = groupLookup; } } } var fields = await graphClient.Drives[driveId].Items[item.Id].ListItem.Fields.Request().GetAsync(); //generate metadata content and upload to blob var metadataFields = fields.AdditionalData; foreach (var metadataFieldToIgnore in metadataFieldsToIgnore) { //Console.WriteLine("Removing key [{0}] from metadata fields to extract", metadataFieldToIgnore); try { metadataFields.Remove(metadataFieldToIgnore); } catch { //swallow exceptions - where fields we want to remove may not exist / theres a better way to do this altogether. } } metadataFields.Add("SPWebUrl", SPWebUrl); metadataFields.Add("createdAuthorDisplayName", createdAuthorDisplayName); // Get the download URL. This URL is preauthenticated and has a short TTL. object downloadUrl; driveItemInfo.AdditionalData.TryGetValue("@microsoft.graph.downloadUrl", out downloadUrl); long size = (long)driveItemInfo.Size; Console.WriteLine("located file {0}, full url [{1}]", baseFileName, downloadUrl.ToString()); //await DownloadFileLocal(graphClient, downloadUrl, fileName); if (metadataJSONStore.Equals("True")) { //Metadata JSON logic using (var metadataJson = GenerateJsonMetadataFile(metadataFields)) { var uploadUri = await AzureBLOBStorage.UploadFileToAzureBLOB(metadataJson, jsonMetadataFileName, container); //External JSON file approach await AzureBLOBStorage.DownloadFileToAzureBLOB(graphClient, downloadUrl, baseFileName, container, uploadUri); } } else { //BLOB metadata approach await AzureBLOBStorage.DownloadFileToAzureBLOB(graphClient, downloadUrl, baseFileName, container, metadataFields); } //Persist the itemId and url to Storage Table SpoItem spoItemEntity = new SpoItem(item.Id, SPWebUrl); azTableStorage.InsertSpoItemEntity(spoItemEntity); } } }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential // cookies is needed for a given request. options.CheckConsentNeeded = context => true; // requires using Microsoft.AspNetCore.Http; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddAuthentication(AzureADB2CDefaults.AuthenticationScheme) .AddAzureADB2C(options => Configuration.Bind("AzureAdB2C", options)); services.AddAuthorization(options => { options.AddPolicy("OrgAManager", policy => policy.RequireAssertion(context => context.User.HasClaim(c => c.Type == "extension_zaprole" && c.Value == "org_a_manager"))); options.AddPolicy("OrgBEmployee", policy => policy.RequireAssertion(context => context.User.HasClaim(c => c.Type == "extension_zaprole" && c.Value == "org_b_employee"))); options.AddPolicy("ShiftViewer", policy => policy.RequireAssertion(context => context.User.HasClaim(c => c.Type == "extension_zaprole" && c.Value == "org_a_manager" || c.Type == "extension_zaprole" && c.Value == "org_b_employee"))); }); services.AddLocalization(options => options.ResourcesPath = "Resources"); services.AddControllers().AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix) .AddDataAnnotationsLocalization(); services.AddRazorPages(); services.AddSingleton <IValidationAttributeAdapterProvider, DateLessThanAttributeAdapterProvider>(); services.AddTransient <Database>(x => this.GetCosmosDatabase().Result); services.AddSingleton <IRepository <Library.Models.Organization> >(x => new OrganizationRepository(x.GetService <Database>(), this.Configuration["OrganizationName"])); services.AddSingleton <IRepository <Library.Models.Shift>, ShiftRepository>(); services.AddSingleton <IRepository <PartnerOrganization>, PartnerRepository>(); services.AddSingleton <IRepository <Employee>, EmployeeRepository>(); services.AddTransient <IConfidentialClientApplication>(x => ConfidentialClientApplicationBuilder .Create(this.Configuration["ClientId"]) .WithTenantId(this.Configuration["TenantId"]) .WithClientSecret(this.Configuration["ClientSecret"]) .Build()); services.AddTransient <IAuthenticationProvider, ClientCredentialProvider>(); services.AddSingleton <IGraphServiceClient, GraphServiceClient>(); services.Configure <RequestLocalizationOptions>(options => { var supportedCultures = new List <CultureInfo> { new CultureInfo("en-US"), new CultureInfo("fr") }; options.DefaultRequestCulture = new RequestCulture("en-US"); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; }); }
public OnBehalfOfCredential(string clientId, string clientSecret, string userAccessToken) { _confidentialClient = ConfidentialClientApplicationBuilder.Create(clientId).WithClientSecret(clientSecret).Build(); _userAssertion = new UserAssertion(userAccessToken); }
public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl = returnUrl ?? Url.Content("~/"); if (ModelState.IsValid) { // This doesn't count login failures towards account lockout // To enable password failures to trigger account lockout, set lockoutOnFailure: true var result = await _signInManager.PasswordSignInAsync(Input.UserName, Input.Password, Input.RememberMe, lockoutOnFailure: false); // Logged in user if (result.Succeeded) { var user = await _userManager.FindByNameAsync(Input.UserName); // Set user id claim if (!this.User.Claims.Any(x => x.Value == "Id")) { var userIdClaim = new Claim("Id", user.Id); await _userManager.AddClaimAsync(user, userIdClaim); } // Getting bearer token from Azure AD once we're successfully logged in var tokenOptions = new ApiOptions(); _configuration.GetSection(ApiOptions.Token).Bind(tokenOptions); IConfidentialClientApplication app; app = ConfidentialClientApplicationBuilder.Create(tokenOptions.ClientId) .WithClientSecret(tokenOptions.ClientSecret) .WithAuthority(new Uri(tokenOptions.Authority)) .Build(); string[] resourceIds = new string[] { tokenOptions.ResourceId }; AuthenticationResult authResult = null; try { authResult = await app.AcquireTokenForClient(resourceIds).ExecuteAsync(); Response.Cookies.Append("OnebrbApiToken", authResult.AccessToken); } catch (Exception ex) { throw new Exception($"Failed to fetch bearer token. {ex.Message}"); } _logger.LogInformation("User logged in."); return LocalRedirect(returnUrl); } if (result.RequiresTwoFactor) { return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); } if (result.IsLockedOut) { _logger.LogWarning("User account locked out."); return RedirectToPage("./Lockout"); } else { ModelState.AddModelError(string.Empty, "Invalid login attempt."); return Page(); } } // If we got this far, something failed, redisplay form return Page(); }
public static async Task Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log) { var storage = CloudStorageAccount.Parse(StorageAccount); var cloudTableClient = storage.CreateCloudTableClient(); var usersTable = cloudTableClient.GetTableReference("users"); var userStatisticsTable = cloudTableClient.GetTableReference("userstatistics"); await usersTable.CreateIfNotExistsAsync().ConfigureAwait(false); await userStatisticsTable.CreateIfNotExistsAsync().ConfigureAwait(false); // Get a Bearer Token with the App var httpClient = new HttpClient(); string token; try { var app = ConfidentialClientApplicationBuilder.Create(AppId) .WithAuthority($"https://login.windows.net/{TenantId}/oauth2/token") .WithRedirectUri("https://governance365function") .WithClientSecret(AppSecret) .Build(); var authenticationProvider = new MsalAuthenticationProvider(app, Scopes); token = authenticationProvider.GetTokenAsync().Result; httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); } catch (Exception) { throw; } httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); //initialize object for storing all user statistics UserQueryObject usersPage = null; var userStatistics = new UserStatisticsTableEntity() { PartitionKey = "userstatistics", RowKey = TenantId, DeactivatedUsers = 0, DeletedUsers = 0, GuestUsers = 0, InternalUsers = 0, Users = 0 }; //get all users (until nextlinnk is empty) and members/guests + sum up statistics do { if (string.IsNullOrEmpty(usersPage?.OdataNextLink?.ToString())) { //first request usersPage = JsonConvert.DeserializeObject <Governance365.Models.UserQueryObject>(await(await httpClient.GetAsync(usersUrl + UserSelectedProperties).ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false)); } else { //next page request usersPage = JsonConvert.DeserializeObject <Governance365.Models.UserQueryObject>(await(await httpClient.GetAsync(usersPage?.OdataNextLink?.ToString()).ConfigureAwait(false)).Content.ReadAsStringAsync().ConfigureAwait(false)); } //batch request to store pages of users in Azure storage var userPageBatchOperation = new TableBatchOperation(); foreach (var user in usersPage.Value) { if (user.UserType != null) { userStatistics.Users++; if (user.UserType.Equals("Member")) { userStatistics.InternalUsers++; } else if (user.UserType.Equals("Guest")) { userStatistics.GuestUsers++; } if (string.IsNullOrEmpty(user.AccountEnabled) && !bool.Parse(user.AccountEnabled)) { userStatistics.DeactivatedUsers++; } } user.PartitionKey = "user"; user.RowKey = user.Id.ToString(); //add user entity to batch operation userPageBatchOperation.Add(TableOperation.InsertOrReplace(user)); } //write user page to Azure tabel storage await usersTable.ExecuteBatchAsync(userPageBatchOperation).ConfigureAwait(false); } while (!string.IsNullOrEmpty(usersPage.OdataNextLink?.ToString())); //write user statistics to table "userstatistics" -> single value with overwrite var insertUserStatisticsOperation = TableOperation.InsertOrReplace(userStatistics); await userStatisticsTable.ExecuteAsync(insertUserStatisticsOperation).ConfigureAwait(false); httpClient.Dispose(); }
public static async Task <IActionResult> RunAsync( [HttpTrigger( AuthorizationLevel.Anonymous, "get", Route = "moderation/permission") ] HttpRequestMessage req, ClaimsPrincipal identity, TraceWriter log) { string authorizationStatus = req.Headers.GetValues("AuthorizationStatus").FirstOrDefault(); if (Convert.ToInt32(authorizationStatus).Equals((int)HttpStatusCode.Accepted)) { string redirectUri = "https://th-admin-dev-weu-func.azurewebsites.net"; IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(Environment.GetEnvironmentVariable("TheatreersAdminClientId")) .WithTenantId("74b32fe7-9082-4068-9cbe-876773845c52") .WithClientSecret(Environment.GetEnvironmentVariable("TheatreersAdminClientSecret")) .WithRedirectUri(redirectUri) .Build(); // With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the // application permissions need to be set statically (in the portal or by PowerShell), and then granted by // a tenant administratorResourceId = "someAppIDURI"; string ResourceId = "https://graph.windows.net"; string[] scopes = new[] { ResourceId + "/.default" }; AuthenticationResult token = null; try { token = await app.AcquireTokenForClient(scopes) .ExecuteAsync(); log.Info($"Token: {token}"); } catch (MsalServiceException ex) { // Case when ex.Message contains: // AADSTS70011 Invalid scope. The scope has to be of the form "https://resourceUrl/.default" // Mitigation: change the scope to be as expected } log.Info($"Token: {token}"); log.Info("Getting user permissions in B2C Directory"); string requestBody = await req.Content.ReadAsStringAsync(); dynamic data = JsonConvert.DeserializeObject(requestBody); var request = new HttpRequestMessage(); log.Info($"Token: {token}"); request.RequestUri = new System.Uri("https://graph.windows.net/theatreers.onmicrosoft.com/users?api-version=1.6"); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.AccessToken); var client = await httpClient.SendAsync(request); var body = await client.Content.ReadAsStringAsync(); return(new OkObjectResult(body)); } else { return(new UnauthorizedResult()); } }
static async Task Main(string[] args) { // Read application settings from appsettings.json (tenant ID, app ID, client secret, etc.) AppSettings config = AppSettingsFile.ReadFromJsonFile(); // Initialize the client credential auth provider IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder .Create(config.AppId) .WithTenantId(config.TenantId) .WithClientSecret(config.ClientSecret) .Build(); ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication); // Set up the Microsoft Graph service client with client credentials GraphServiceClient graphClient = new GraphServiceClient(authProvider); // Create object to store create user responses List <SpreadsheetModelUserCreated> responses = new List <SpreadsheetModelUserCreated>(); PrintCommands(); try { while (true) { Console.Write("Enter command, then press ENTER: "); string decision = Console.ReadLine(); switch (decision.ToLower()) { case "1": await UserService.ListUsers(graphClient); break; case "2": await UserService.GetUserById(graphClient); break; case "3": await UserService.GetUserBySignInName(config, graphClient); break; case "4": await UserService.DeleteUserById(graphClient); break; case "5": await UserService.SetPasswordByUserId(graphClient); break; case "6": await UserService.BulkCreate(config, graphClient); break; case "7": await UserService.CreateUserWithCustomAttribute(graphClient, config.B2cExtensionAppClientId, config.TenantId); break; case "8": await UserService.ListUsersWithCustomAttribute(graphClient, config.B2cExtensionAppClientId); break; case "9": await UserService.BulkCreateFromCsv(config, graphClient, responses); SpreadsheetService.SaveResponseDataAsCSV(responses); break; case "help": Program.PrintCommands(); break; case "exit": return; default: Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Invalid command. Enter 'help' to show a list of commands."); Console.ResetColor(); break; } Console.ResetColor(); } } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"An error occurred: {ex}"); Console.ResetColor(); } Console.ReadLine(); }
static async Task Main(string[] args) { string clientId = "6af093f3-b445-4b7a-beae-046864468ad6"; string tenant = "msidentitysamplestesting.onmicrosoft.com"; string[] scopes = new[] { "api://8206b76f-586e-4098-b1e5-598c1aa3e2a1/.default" }; // Simulates the configuration, could be a IConfiguration or anything Dictionary <string, string> Configuration = new Dictionary <string, string>(); // Certificate Loading string keyVaultContainer = "https://WebAppsApisTests.vault.azure.net"; string keyVaultReference = "Self-Signed-5-5-22"; CertificateDescription certDescription = CertificateDescription.FromKeyVault(keyVaultContainer, keyVaultReference); ICertificateLoader certificateLoader = new DefaultCertificateLoader(); certificateLoader.LoadIfNeeded(certDescription); // Create the confidential client application IConfidentialClientApplication app; app = ConfidentialClientApplicationBuilder.Create(clientId) // Alternatively to the certificate you can use .WithClientSecret(clientSecret) .WithCertificate(certDescription.Certificate) .WithTenantId(tenant) .Build(); // In memory token caches (App and User caches) // app.AddInMemoryTokenCache(); // Or // Distributed token caches (App and User caches) // Add one of the below: SQL, Redis, CosmosDb app.AddDistributedTokenCache(services => { services.AddDistributedMemoryCache(); services.AddLogging(configure => configure.AddConsole()) .Configure <LoggerFilterOptions>(options => options.MinLevel = Microsoft.Extensions.Logging.LogLevel.Debug); /* Remove comments to use SQL cache implementation * services.AddDistributedSqlServerCache(options => * { * // SQL Server token cache * // Requires to reference Microsoft.Extensions.Caching.SqlServer * options.ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=TestCache;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"; * options.SchemaName = "dbo"; * options.TableName = "TestCache"; * * // You don't want the SQL token cache to be purged before the access token has expired. Usually * // access tokens expire after 1 hour (but this can be changed by token lifetime policies), whereas * // the default sliding expiration for the distributed SQL database is 20 mins. * // Use a value which is above 60 mins (or the lifetime of a token in case of longer lived tokens) * options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90); * }); */ /* Remove comments to use Redis cache implementation * // Add Redis * services.AddStackExchangeRedisCache(options => * { * options.Configuration = "localhost"; * options.InstanceName = "Redis"; * }); */ /* Remove comments to use CosmosDB cache implementation * // Add CosmosDB * services.AddCosmosCache((CosmosCacheOptions cacheOptions) => * { * cacheOptions.ContainerName = Configuration["CosmosCacheContainer"]; * cacheOptions.DatabaseName = Configuration["CosmosCacheDatabase"]; * cacheOptions.ClientBuilder = new CosmosClientBuilder(Configuration["CosmosConnectionString"]); * cacheOptions.CreateIfNotExists = true; * }); */ }); // Acquire a token (twice) var result = await app.AcquireTokenForClient(scopes) .ExecuteAsync(); Console.WriteLine(result.AuthenticationResultMetadata.TokenSource); result = await app.AcquireTokenForClient(scopes) .ExecuteAsync(); Console.WriteLine(result.AuthenticationResultMetadata.TokenSource); }
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/AcquireTokenAsync/*'/> public override Task <SqlAuthenticationToken> AcquireTokenAsync(SqlAuthenticationParameters parameters) => Task.Run(async() => { AuthenticationResult result; string scope = parameters.Resource.EndsWith(s_defaultScopeSuffix) ? parameters.Resource : parameters.Resource + s_defaultScopeSuffix; string[] scopes = new string[] { scope }; if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal) { IConfidentialClientApplication ccApp = ConfidentialClientApplicationBuilder.Create(parameters.UserId) .WithAuthority(parameters.Authority) .WithClientSecret(parameters.Password) .WithClientName(Common.DbConnectionStringDefaults.ApplicationName) .WithClientVersion(Common.ADP.GetAssemblyVersion().ToString()) .Build(); result = ccApp.AcquireTokenForClient(scopes).ExecuteAsync().Result; SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Service Principal auth mode. Expiry Time: {0}", result.ExpiresOn); return(new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn)); } /* * Today, MSAL.NET uses another redirect URI by default in desktop applications that run on Windows * (urn:ietf:wg:oauth:2.0:oob). In the future, we'll want to change this default, so we recommend * that you use https://login.microsoftonline.com/common/oauth2/nativeclient. * * https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-desktop-app-registration#redirect-uris */ string redirectUri = s_nativeClientRedirectUri; #if NETCOREAPP if (parameters.AuthenticationMethod != SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow) { redirectUri = "http://localhost"; } #endif PublicClientAppKey pcaKey = new PublicClientAppKey(parameters.Authority, redirectUri, _applicationClientId #if NETFRAMEWORK , _iWin32WindowFunc #endif #if NETSTANDARD , _parentActivityOrWindowFunc #endif ); IPublicClientApplication app = GetPublicClientAppInstance(pcaKey); if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryIntegrated) { if (!string.IsNullOrEmpty(parameters.UserId)) { result = app.AcquireTokenByIntegratedWindowsAuth(scopes) .WithCorrelationId(parameters.ConnectionId) .WithUsername(parameters.UserId) .ExecuteAsync().Result; } else { result = app.AcquireTokenByIntegratedWindowsAuth(scopes) .WithCorrelationId(parameters.ConnectionId) .ExecuteAsync().Result; } SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Integrated auth mode. Expiry Time: {0}", result.ExpiresOn); } else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryPassword) { SecureString password = new SecureString(); foreach (char c in parameters.Password) { password.AppendChar(c); } password.MakeReadOnly(); result = app.AcquireTokenByUsernamePassword(scopes, parameters.UserId, password) .WithCorrelationId(parameters.ConnectionId) .ExecuteAsync().Result; SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Password auth mode. Expiry Time: {0}", result.ExpiresOn); } else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive || parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow) { // Fetch available accounts from 'app' instance System.Collections.Generic.IEnumerable <IAccount> accounts = await app.GetAccountsAsync(); IAccount account; if (!string.IsNullOrEmpty(parameters.UserId)) { account = accounts.FirstOrDefault(a => parameters.UserId.Equals(a.Username, System.StringComparison.InvariantCultureIgnoreCase)); } else { account = accounts.FirstOrDefault(); } if (null != account) { try { // If 'account' is available in 'app', we use the same to acquire token silently. // Read More on API docs: https://docs.microsoft.com/dotnet/api/microsoft.identity.client.clientapplicationbase.acquiretokensilent result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync(); SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result.ExpiresOn); } catch (MsalUiRequiredException) { // An 'MsalUiRequiredException' is thrown in the case where an interaction is required with the end user of the application, // for instance, if no refresh token was in the cache, or the user needs to consent, or re-sign-in (for instance if the password expired), // or the user needs to perform two factor authentication. result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod); SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result.ExpiresOn); } } else { // If no existing 'account' is found, we request user to sign in interactively. result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod); SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result.ExpiresOn); } } else { SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | {0} authentication mode not supported by ActiveDirectoryAuthenticationProvider class.", parameters.AuthenticationMethod); throw SQL.UnsupportedAuthenticationSpecified(parameters.AuthenticationMethod); } return(new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn)); });
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(AzureADDefaults.AuthenticationScheme) .AddAzureAD(options => Configuration.Bind("AzureAd", options)); // <added> List <string> scopes = new List <string>(); scopes.Add("offline_access"); scopes.Add("user.read"); var appSettings = new AzureADOptions(); Configuration.Bind("AzureAd", appSettings); var application = ConfidentialClientApplicationBuilder.Create(appSettings.ClientId) .WithAuthority(appSettings.Instance + appSettings.TenantId + "/v2.0") .WithRedirectUri("https://localhost:5001" + appSettings.CallbackPath) .WithClientSecret(appSettings.ClientSecret) .Build(); // TODO: add MS Graph Code here var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async(request) => { var graphUserAccount = new Helpers.GraphUserAccount(request.Properties["User"] as System.Security.Claims.ClaimsPrincipal); var accessToken = await application.AcquireTokenSilent(scopes, graphUserAccount).ExecuteAsync(); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", accessToken.AccessToken); })); services.AddSingleton <GraphServiceClient>(graphServiceClient); services.Configure <OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, async options => { // configure authority to use v2 endpoint options.Authority = options.Authority + "/v2.0/"; // asking Azure AD for id_token (to establish identity) and authorization code (to get access/refresh tokens for calling services) options.ResponseType = OpenIdConnectResponseType.CodeIdToken; // add the permission scopes you want the application to use options.Scope.Add("offline_access"); options.Scope.Add("user.read"); // validate the token issuer options.TokenValidationParameters.NameClaimType = "preferred_username"; // wire up event to do second part of code authorization flow (exchanging authorization code for token) var handler = options.Events.OnAuthorizationCodeReceived; options.Events.OnAuthorizationCodeReceived = async context => { // handle the auth code returned post signin context.HandleCodeRedemption(); if (!context.HttpContext.User.Claims.Any()) { (context.HttpContext.User.Identity as ClaimsIdentity).AddClaims(context.Principal.Claims); } // get token var token = await application.AcquireTokenByAuthorizationCode(options.Scope, context.ProtocolMessage.Code).ExecuteAsync(); context.HandleCodeRedemption(null, token.IdToken); await handler(context).ConfigureAwait(false); }; }); // </added> services.AddControllersWithViews(options => { var policy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(policy)); }); services.AddRazorPages(); }
private async Task RunOnBehalfOfTestAsync(LabResponse labResponse) { LabUser user = labResponse.User; string oboHost; string secret; string authority; string publicClientID; string confidentialClientID; string[] oboScope; switch (labResponse.User.AzureEnvironment) { case AzureEnvironment.azureusgovernment: oboHost = ArlingtonCloudHost; secret = _keyVault.GetSecret(TestConstants.MsalArlingtonOBOKeyVaultUri).Value; authority = labResponse.Lab.Authority + "organizations"; publicClientID = ArlingtonPublicClientIDOBO; confidentialClientID = ArlingtonConfidentialClientIDOBO; oboScope = s_arlingtonOBOServiceScope; break; default: oboHost = PublicCloudHost; secret = _keyVault.GetSecret(TestConstants.MsalOBOKeyVaultUri).Value; authority = TestConstants.AuthorityOrganizationsTenant; publicClientID = PublicCloudPublicClientIDOBO; confidentialClientID = PublicCloudConfidentialClientIDOBO; oboScope = s_publicCloudOBOServiceScope; break; } //TODO: acquire scenario specific client ids from the lab response SecureString securePassword = new NetworkCredential("", user.GetOrFetchPassword()).SecurePassword; var msalPublicClient = PublicClientApplicationBuilder.Create(publicClientID) .WithAuthority(authority) .WithRedirectUri(TestConstants.RedirectUri) .WithTestLogging() .Build(); var builder = msalPublicClient.AcquireTokenByUsernamePassword(oboScope, user.Upn, securePassword); builder.WithAuthority(authority); var authResult = await builder.ExecuteAsync().ConfigureAwait(false); var confidentialApp = ConfidentialClientApplicationBuilder .Create(confidentialClientID) .WithAuthority(new Uri(oboHost + authResult.TenantId), true) .WithClientSecret(secret) .WithTestLogging() .Build(); var userCacheRecorder = confidentialApp.UserTokenCache.RecordAccess(); UserAssertion userAssertion = new UserAssertion(authResult.AccessToken); string atHash = userAssertion.AssertionHash; authResult = await confidentialApp.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); MsalAssert.AssertAuthResult(authResult, user); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); await confidentialApp.GetAccountsAsync().ConfigureAwait(false); Assert.IsNull(userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); }
static async System.Threading.Tasks.Task Main(string[] args) { Console.WriteLine(".NET Core Graph Tutorial\n"); var appConfig = LoadAppSettings(); if (appConfig == null) { Console.WriteLine("Missing or invalid appsettings.json...exiting"); return; } var appId = appConfig["appId"]; var tenantId = appConfig["tenantId"]; var clientSecret = appConfig["clientSecret"]; // Initialize the auth provider with values from appsettings.json IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder .Create(appId) .WithTenantId(tenantId) .WithClientSecret(clientSecret) .Build(); //Install-Package Microsoft.Graph.Auth -PreRelease ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication); // Initialize Graph client GraphHelper.Initialize(authProvider); int choice = -1; while (choice != 0) { Console.WriteLine("Please choose one of the following options:"); Console.WriteLine("0. Exit"); Console.WriteLine("1. Get meeting's members"); Console.WriteLine("2. Get call's attendance"); try { choice = int.Parse(Console.ReadLine()); } catch (System.FormatException) { // Set to invalid value choice = -1; } switch (choice) { case 0: // Exit the program Console.WriteLine("Goodbye..."); break; case 1: Console.WriteLine("Please input the meetingId"); string meetingId = Console.ReadLine(); var members = await GraphHelper.GetTeamMembers(meetingId); foreach (User member in members.ToList()) { Console.WriteLine("Found member: " + member.DisplayName); } //var meeting = await GraphHelper.GetMeeting("https://teams.microsoft.com/l/meetup-join/19%3ameeting_NTg3ZGQ5YjYtNjI1Ny00ZTQ4LTg0ZWMtMmI4ZjZkYjJkNGRj%40thread.v2/0?context=%7b%22Tid%22%3a%221aed5afa-c363-478e-ae14-b73b6949addb%22%2c%22Oid%22%3a%228b406a47-ed00-45cb-ad12-cd01d6143bbb%22%7d"); break; case 2: Console.WriteLine("Please input the callId"); string callId = Console.ReadLine(); //var callRecord = await GraphHelper.GetCallRecord(callId != "" ? callId : "f4ea5721-a7b5-44ee-8ceb-9dfa7a6dd41e"); var callRecord = await GraphHelper.GetCallRecordSessions(callId != ""?callId : "f4ea5721-a7b5-44ee-8ceb-9dfa7a6dd41e"); var joinWebUrl = callRecord.JoinWebUrl; if (joinWebUrl == null) { break; } foreach (Session session in callRecord.Sessions) { ParticipantEndpoint caller = (ParticipantEndpoint)session.Caller; var user = await GraphHelper.GetUserAsync(caller.Identity.User.Id); //TODO - Find the mapping between this userId and the university's student ID. StudentEvent studentEvent = new StudentEvent { //TODO - Find course ID based on joinWebUrl. CourseID = "COMP0088", // Course ID Upper case. Timestamp = ((DateTimeOffset)session.StartDateTime).UtcDateTime, EventType = EventType.Attendance, ActivityType = "Meeting", ActivityName = "Weekly Lecture", Student = new Student { Email = user.Mail, FirstName = user.GivenName, LastName = user.Surname, ID = user.Id } }; Console.WriteLine(studentEvent.ToString()); //_eventAggregator.ProcessEvent(studentEvent); } break; default: Console.WriteLine("Invalid choice! Please try again."); break; } } }
public async Task <IActionResult> Submit([FromBody] FieldData body) { AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json"); // You can run this sample using ClientSecret or Certificate. The code will differ only when instantiating the IConfidentialClientApplication bool isUsingClientSecret = AppUsesClientSecret(config); // Even if this is a console application here, a daemon application is a confidential client application IConfidentialClientApplication app; if (isUsingClientSecret) { app = ConfidentialClientApplicationBuilder.Create(config.ClientId) .WithClientSecret(config.ClientSecret) .WithAuthority(new Uri(config.Authority)) .Build(); } else { X509Certificate2 certificate = ReadCertificate(config.CertificateName); app = ConfidentialClientApplicationBuilder.Create(config.ClientId) .WithCertificate(certificate) .WithAuthority(new Uri(config.Authority)) .Build(); } // With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the // application permissions need to be set statically (in the portal or by PowerShell), and then granted by // a tenant administrator. string[] scopes = new string[] { $"{config.ApiUrl}.default" }; AuthenticationResult result = null; try { result = await app.AcquireTokenForClient(scopes) .ExecuteAsync(); } catch (MsalServiceException ex) when(ex.Message.Contains("AADSTS70011")) { // Invalid scope. The scope has to be of the form "https://resourceurl/.default" // Mitigation: change the scope to be as expected return(BadRequest("Scope not supported")); } if (result != null) { var SOBListLocation = $"{config.CpscSharepoint},c0cefe40-beeb-41a9-b4f5-9960bcfa010b,fbb78c64-1220-42fe-a319-94c493a9a105/lists/6f53c37b-d6ba-46c3-91a9-2e942a984af9/items"; var webapiUrl = $"{config.ApiUrl}v1.0/sites/{SOBListLocation}"; var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, webapiUrl); httpRequestMessage.Content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json"); var httpClient = new HttpClient(); var apiCaller = new ProtectedApiCallHelper(httpClient); var res = await apiCaller.CallWebApiAndProcessResultASync(httpRequestMessage, result.AccessToken); return(Ok(res)); // await apiCaller.AddToSiteList("siteid", "listId", "payload", Display);// } else { return(Ok("result is null")); } }
private static async Task RunAsync() { AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json"); // You can run this sample using ClientSecret or Certificate. The code will differ only when instantiating the IConfidentialClientApplication bool isUsingClientSecret = AppUsesClientSecret(config); // Even if this is a console application here, a daemon application is a confidential client application IConfidentialClientApplication app; if (isUsingClientSecret) { app = ConfidentialClientApplicationBuilder.Create(config.ClientId) .WithClientSecret(config.ClientSecret) .WithAuthority(new Uri(config.Authority)) .Build(); } else { X509Certificate2 certificate = ReadCertificate(config.CertificateName); app = ConfidentialClientApplicationBuilder.Create(config.ClientId) .WithCertificate(certificate) .WithAuthority(new Uri(config.Authority)) .Build(); } // With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the // application permissions need to be set statically (in the portal or by PowerShell), and then granted by // a tenant administrator. string[] scopes = new string[] { $"{config.ApiUrl}.default" }; AuthenticationResult result = null; try { result = await app.AcquireTokenForClient(scopes) .ExecuteAsync(); //Console.ForegroundColor = ConsoleColor.Green; //Console.WriteLine("Token acquired"); //Console.ResetColor(); } catch (MsalServiceException ex) when(ex.Message.Contains("AADSTS70011")) { // Invalid scope. The scope has to be of the form "https://resourceurl/.default" // Mitigation: change the scope to be as expected Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Scope provided is not supported"); Console.ResetColor(); } if (result != null) { var httpClient = new HttpClient(); var apiCaller = new ProtectedApiCallHelper(httpClient); //await apiCaller.CallWebApiAndProcessResultASync($"{config.ApiUrl}v1.0/users", result.AccessToken, Display); //await apiCaller.CallWebApiAndProcessResultASync($"{config.ApiUrl}v1.0/users/[email protected]/events", result.AccessToken, Display); //await apiCaller.CallWebApiAndProcessResultASync($"{config.ApiUrl}v1.0/users/[email protected]/calendarView?startDateTime=2020-09-01T16:00:00.0000000&endDateTime=2020-12-07T16:00:00.0000000", result.AccessToken, Display); //await apiCaller.CallWebApiAndProcessResultASync($"{config.ApiUrl}v1.0/users/[email protected]/calendarView?startDateTime=2020-09-01T16:00:00.0000000&endDateTime=2020-12-07T16:00:00.0000000", result.AccessToken, Display); //await apiCaller.CallWebApiAndProcessResultASync($"{config.ApiUrl}v1.0/users//[email protected]/calendargroup/calendars/calendarView?startDateTime=2020-09-01T16:00:00.0000000&endDateTime=2020-12-07T16:00:00.0000000", result.AccessToken, Display); //await apiCaller.CallWebApiAndProcessResultASync($"{config.ApiUrl}v1.0/me/calendar", result.AccessToken, Display); await apiCaller.CallWebApiAndProcessResultASync($"{config.ApiUrl}v1.0/users/[email protected]/calendar/getSchedule", result.AccessToken, Display); } }
public static async Task <string> GetLabAccessTokenAsync(string authority, string[] scopes, LabAccessAuthenticationType authType, string clientId, string certThumbprint, string clientSecret) { AuthenticationResult authResult; IConfidentialClientApplication confidentialApp; X509Certificate2 cert; switch (authType) { case LabAccessAuthenticationType.ClientCertificate: var clientIdForCertAuth = String.IsNullOrEmpty(clientId) ? LabAccessConfidentialClientId : clientId; var certThumbprintForLab = String.IsNullOrEmpty(clientId) ? LabAccessThumbPrint : certThumbprint; cert = CertificateHelper.FindCertificateByThumbprint(certThumbprintForLab); if (cert == null) { throw new InvalidOperationException( "Test setup error - cannot find a certificate in the My store for KeyVault. This is available for Microsoft employees only."); } confidentialApp = ConfidentialClientApplicationBuilder .Create(clientIdForCertAuth) .WithAuthority(new Uri(authority), true) .WithCertificate(cert) .Build(); s_staticCache.Bind(confidentialApp.AppTokenCache); authResult = await confidentialApp .AcquireTokenForClient(scopes) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); break; case LabAccessAuthenticationType.ClientSecret: var clientIdForSecretAuth = String.IsNullOrEmpty(clientId) ? LabAccessConfidentialClientId : clientId; var clientSecretForLab = String.IsNullOrEmpty(clientId) ? s_secret : clientSecret; confidentialApp = ConfidentialClientApplicationBuilder .Create(clientIdForSecretAuth) .WithAuthority(new Uri(authority), true) .WithClientSecret(clientSecretForLab) .Build(); s_staticCache.Bind(confidentialApp.AppTokenCache); authResult = await confidentialApp .AcquireTokenForClient(scopes) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); break; case LabAccessAuthenticationType.UserCredential: var clientIdForPublicClientAuth = String.IsNullOrEmpty(clientId) ? LabAccessPublicClientId : clientId; var publicApp = PublicClientApplicationBuilder .Create(clientIdForPublicClientAuth) .WithAuthority(new Uri(authority), true) .Build(); s_staticCache.Bind(publicApp.UserTokenCache); authResult = await publicApp .AcquireTokenByIntegratedWindowsAuth(scopes) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); break; default: throw new ArgumentOutOfRangeException(); } return(authResult?.AccessToken); }
public async Task <int> SignAsync ( CommandOption configFile, CommandOption inputFile, CommandOption outputFile, CommandOption fileList, CommandOption clientSecret, CommandOption username, CommandOption name, CommandOption description, CommandOption descriptionUrl ) { try { // verify required parameters if (!configFile.HasValue()) { signCommandLineApplication.Error.WriteLine("--config parameter is required"); return(EXIT_CODES.INVALID_OPTIONS); } if (!inputFile.HasValue()) { signCommandLineApplication.Error.WriteLine("--input parameter is required"); return(EXIT_CODES.INVALID_OPTIONS); } if (!name.HasValue()) { signCommandLineApplication.Error.WriteLine("--name parameter is required"); return(EXIT_CODES.INVALID_OPTIONS); } if (!outputFile.HasValue()) { // use input as the output value outputFile.Values.Add(inputFile.Value()); } var builder = new ConfigurationBuilder() .AddJsonFile(ExpandFilePath(configFile.Value())) .AddEnvironmentVariables(); var configuration = builder.Build(); // Setup Refit var settings = new RefitSettings { AuthorizationHeaderValueGetter = async() => { var authority = $"{configuration["SignClient:AzureAd:AADInstance"]}{configuration["SignClient:AzureAd:TenantId"]}"; var clientId = configuration["SignClient:AzureAd:ClientId"]; var resourceId = configuration["SignClient:Service:ResourceId"]; // See if we have a Username option if (username.HasValue()) { // ROPC flow var pca = PublicClientApplicationBuilder.Create(clientId) .WithAuthority(authority) .Build(); var secret = new NetworkCredential("", clientSecret.Value()).SecurePassword; var tokenResult = await pca.AcquireTokenByUsernamePassword(new[] { $"{resourceId}/user_impersonation" }, username.Value(), secret).ExecuteAsync(); return(tokenResult.AccessToken); } else { var context = ConfidentialClientApplicationBuilder.Create(clientId) .WithAuthority(authority) .WithClientSecret(clientSecret.Value()) .Build(); // Client credential flow var res = await context.AcquireTokenForClient(new[] { $"{resourceId}/.default" }).ExecuteAsync(); return(res.AccessToken); } } }; var client = RestService.For <ISignService>(configuration["SignClient:Service:Url"], settings); client.Client.Timeout = Timeout.InfiniteTimeSpan; // TODO: Make configurable on command line // Prepare input/output file var input = new FileInfo(ExpandFilePath(inputFile.Value())); var output = new FileInfo(ExpandFilePath(outputFile.Value())); Directory.CreateDirectory(output.DirectoryName); // Do action HttpResponseMessage response; response = await client.SignFile(input, fileList.HasValue()?new FileInfo(ExpandFilePath(fileList.Value())) : null, HashMode.Sha256, name.Value(), description.Value(), descriptionUrl.Value()); // Check response if (!response.IsSuccessStatusCode) { Console.Error.WriteLine($"Server returned non Ok response: {(int)response.StatusCode} {response.ReasonPhrase}"); return(-1); } var str = await response.Content.ReadAsStreamAsync(); using (var fs = output.OpenWrite()) { await str.CopyToAsync(fs).ConfigureAwait(false); } } catch (AuthenticationException e) { signCommandLineApplication.Error.WriteLine(e.Message); return(EXIT_CODES.FAILED); } catch (Exception e) { signCommandLineApplication.Error.WriteLine("Exception: " + e); return(EXIT_CODES.FAILED); } return(EXIT_CODES.SUCCESS); }
public async Task <IActionResult> Api() { string responseString; try { // Retrieve the token with the specified scopes var scope = AzureAdB2COptions.ApiScopes.Split(' '); string signedInUserID = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value; IConfidentialClientApplication cca = ConfidentialClientApplicationBuilder.Create(AzureAdB2COptions.ClientId) .WithLogging(MyLoggingMethod, LogLevel.Verbose, enablePiiLogging: false, enableDefaultPlatformLogging: true) .WithRedirectUri(AzureAdB2COptions.RedirectUri) .WithClientSecret(AzureAdB2COptions.ClientSecret) .WithB2CAuthority(AzureAdB2COptions.Authority) .Build(); new MSALStaticCache(signedInUserID, this.HttpContext).EnablePersistence(cca.UserTokenCache); var accounts = await cca.GetAccountsAsync(); AuthenticationResult result = await cca.AcquireTokenSilent(scope, accounts.FirstOrDefault()) .ExecuteAsync(); if (result.AccessToken == null) { ViewData["Title"] = "JWT Token Problem"; ViewData["Payload"] = "The current user session does not have a valid access_token. Most likely the scopes do not match the App registration."; return(View()); } HttpClient client = new HttpClient(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AzureAdB2COptions.ApiUrl); // Add token to the Authorization header and make the request request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken); HttpResponseMessage response = await client.SendAsync(request); // Handle the response switch (response.StatusCode) { case HttpStatusCode.OK: responseString = await response.Content.ReadAsStringAsync(); break; case HttpStatusCode.Unauthorized: responseString = $"Please sign in again. {response.ReasonPhrase}"; break; default: responseString = $"Error calling API. StatusCode=${response.StatusCode}"; break; } client.Dispose(); } catch (MsalUiRequiredException ex) { responseString = $"Session has expired. Please sign in again. {ex.Message}"; } catch (Exception ex) { responseString = $"Error calling API: {ex.Message}"; } ViewData["Payload"] = $"{responseString}"; return(View()); }
public void TestConstructor_WithDebugLoggingCallback() { var cca = ConfidentialClientApplicationBuilder.Create(MsalTestConstants.ClientId).WithDebugLoggingCallback().Build(); Assert.IsNotNull(cca.AppConfig.LoggingCallback); }
public IConfidentialClientApplication CreateMsalConfidentialClient(string tenantId, string clientId, string clientSecret) { return(ConfidentialClientApplicationBuilder.Create(clientId).WithHttpClientFactory(new HttpPipelineClientFactory(HttpPipeline)).WithTenantId(tenantId).WithClientSecret(clientSecret).Build()); }
private static async Task RunAsync() { AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json"); // You can run this sample using ClientSecret or Certificate. The code will differ only when instantiating the IConfidentialClientApplication bool isUsingClientSecret = AppUsesClientSecret(config); // Even if this is a console application here, a daemon application is a confidential client application IConfidentialClientApplication app; if (isUsingClientSecret) { app = ConfidentialClientApplicationBuilder.Create(config.ClientId) .WithClientSecret(config.ClientSecret) .WithAuthority(new Uri(config.Authority)) .Build(); } else { X509Certificate2 certificate = ReadCertificate(config.CertificateName); app = ConfidentialClientApplicationBuilder.Create(config.ClientId) .WithCertificate(certificate) .WithAuthority(new Uri(config.Authority)) .Build(); } // With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the // application permissions need to be set statically (in the portal or by PowerShell), and then granted by // a tenant administrator string[] scopes = new string[] { config.TargetApiScope }; AuthenticationResult result = null; try { result = await app.AcquireTokenForClient(scopes) .ExecuteAsync(); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Token acquired \n"); Console.ResetColor(); } catch (MsalServiceException ex) when(ex.Message.Contains("AADSTS70011")) { // Invalid scope. The scope has to be of the form "https://resourceurl/.default" // Mitigation: change the scope to be as expected Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Scope provided is not supported"); Console.ResetColor(); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(ex); Console.ResetColor(); } if (result != null) { var httpClient = new HttpClient(); var apiCaller = new ProtectedApiCallHelper(httpClient); await apiCaller.CallWebApiAndProcessResultASync($"{config.TargetApiBaseAddress}/WeatherForecast/GetDataByRole", result.AccessToken, Display); } }
//Since this test performs a large number of operations it should not be rerun on other clouds. private async Task RunOnBehalfOfTestWithTokenCacheAsync(LabResponse labResponse) { LabUser user = labResponse.User; string oboHost; string secret; string authority; string publicClientID; string confidentialClientID; string[] oboScope; oboHost = PublicCloudHost; secret = _keyVault.GetSecret(TestConstants.MsalOBOKeyVaultUri).Value; authority = TestConstants.AuthorityOrganizationsTenant; publicClientID = PublicCloudPublicClientIDOBO; confidentialClientID = PublicCloudConfidentialClientIDOBO; oboScope = s_publicCloudOBOServiceScope; //TODO: acquire scenario specific client ids from the lab response SecureString securePassword = new NetworkCredential("", user.GetOrFetchPassword()).SecurePassword; var factory = new HttpSnifferClientFactory(); var msalPublicClient = PublicClientApplicationBuilder.Create(publicClientID) .WithAuthority(authority) .WithRedirectUri(TestConstants.RedirectUri) .WithTestLogging() .WithHttpClientFactory(factory) .Build(); var authResult = await msalPublicClient.AcquireTokenByUsernamePassword(oboScope, user.Upn, securePassword) .ExecuteAsync() .ConfigureAwait(false); var confidentialApp = ConfidentialClientApplicationBuilder .Create(confidentialClientID) .WithAuthority(new Uri(oboHost + authResult.TenantId), true) .WithClientSecret(secret) .WithTestLogging() .BuildConcrete(); var userCacheRecorder = confidentialApp.UserTokenCache.RecordAccess(); UserAssertion userAssertion = new UserAssertion(authResult.AccessToken); string atHash = userAssertion.AssertionHash; authResult = await confidentialApp.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); MsalAssert.AssertAuthResult(authResult, user); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); //Run OBO again. Should get token from cache authResult = await confidentialApp.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.Cache, authResult.AuthenticationResultMetadata.TokenSource); //Expire access tokens TokenCacheHelper.ExpireAllAccessTokens(confidentialApp.UserTokenCacheInternal); //Run OBO again. Should do OBO flow since the AT is expired and RTs aren't cached for normal OBO flow authResult = await confidentialApp.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); AssertLastHttpContent("on_behalf_of"); //creating second app with no refresh tokens var atItems = confidentialApp.UserTokenCacheInternal.Accessor.GetAllAccessTokens(); var confidentialApp2 = ConfidentialClientApplicationBuilder .Create(confidentialClientID) .WithAuthority(new Uri(oboHost + authResult.TenantId), true) .WithClientSecret(secret) .WithTestLogging() .WithHttpClientFactory(factory) .BuildConcrete(); TokenCacheHelper.ExpireAccessToken(confidentialApp2.UserTokenCacheInternal, atItems.FirstOrDefault()); //Should perform OBO flow since the access token is expired and the refresh token does not exist authResult = await confidentialApp2.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); AssertLastHttpContent("on_behalf_of"); TokenCacheHelper.ExpireAllAccessTokens(confidentialApp2.UserTokenCacheInternal); TokenCacheHelper.UpdateUserAssertions(confidentialApp2); //Should perform OBO flow since the access token and the refresh token contains the wrong user assertion hash authResult = await confidentialApp2.AcquireTokenOnBehalfOf(s_scopes, userAssertion) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.IsNotNull(authResult); Assert.IsNotNull(authResult.AccessToken); Assert.IsNotNull(authResult.IdToken); Assert.IsTrue(!userCacheRecorder.LastAfterAccessNotificationArgs.IsApplicationCache); Assert.IsTrue(userCacheRecorder.LastAfterAccessNotificationArgs.HasTokens); Assert.AreEqual(atHash, userCacheRecorder.LastAfterAccessNotificationArgs.SuggestedCacheKey); Assert.AreEqual(TokenSource.IdentityProvider, authResult.AuthenticationResultMetadata.TokenSource); AssertLastHttpContent("on_behalf_of"); }
private async Task AcquireAndAcquireSilent_MultipleKeys_Async(LabResponse labResponse) { var popConfig1 = new PoPAuthenticationConfiguration(new Uri("https://www.contoso.com/path1/path2?queryParam1=a&queryParam2=b")); popConfig1.HttpMethod = HttpMethod.Get; var popConfig2 = new PoPAuthenticationConfiguration(new Uri("https://www.bing.com/path3/path4?queryParam5=c&queryParam6=d")); popConfig2.HttpMethod = HttpMethod.Post; var user = labResponse.User; SecureString securePassword = new NetworkCredential("", user.GetOrFetchPassword()).SecurePassword; var pca = ConfidentialClientApplicationBuilder.Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithTestLogging() .WithAuthority(AadAuthorityAudience.AzureAdMultipleOrgs) .WithClientSecret(s_publicCloudCcaSecret).Build(); ConfigureInMemoryCache(pca); var result = await pca .AcquireTokenForClient(s_keyvaultScope) .WithExtraQueryParameters(GetTestSliceParams()) .WithProofOfPossession(popConfig1) .ExecuteAsync(CancellationToken.None) .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, popConfig1, result).ConfigureAwait(false); // recreate the pca to ensure that the silent call is served from the cache, i.e. the key remains stable pca = ConfidentialClientApplicationBuilder .Create(PublicCloudConfidentialClientID) .WithExperimentalFeatures() .WithClientSecret(s_publicCloudCcaSecret) .WithHttpClientFactory(new NoAccessHttpClientFactory()) // token should be served from the cache, no network access necessary .Build(); ConfigureInMemoryCache(pca); var accounts = await pca.GetAccountsAsync().ConfigureAwait(false); result = await pca .AcquireTokenSilent(s_keyvaultScope, accounts.Single()) .WithProofOfPossession(popConfig1) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, popConfig1, result).ConfigureAwait(false); // Call some other Uri - the same pop assertion can be reused, i.e. no need to call Evo result = await pca .AcquireTokenSilent(s_keyvaultScope, accounts.Single()) .WithProofOfPossession(popConfig2) .ExecuteAsync() .ConfigureAwait(false); Assert.AreEqual("pop", result.TokenType); await VerifyPoPTokenAsync( PublicCloudConfidentialClientID, popConfig2, result).ConfigureAwait(false); }