public async Task GetAccountAsync_CallsGetAccount_ReturnsAccount() { var account = new MsalAccount(); var js = new Mock <IJSRuntime>(); js.Setup(j => j.InvokeAsync <MsalAccount>(It.IsAny <string>(), It.IsAny <object[]>())) .Returns(new ValueTask <MsalAccount>(Task.FromResult(account))); var navigation = new TestNavigationManager(); IMsalConfig config = new TestConfig(); var configurator = new Mock <IConfigProvider <IMsalConfig> >(); configurator .Setup(x => x.GetConfigurationAsync()) .Returns(Task.FromResult(config)); var msal = new Msal(js.Object, navigation, configurator.Object); var result = await msal.GetAccountAsync(); js.Verify(j => j.InvokeAsync <object>("azuread.getAccount", It.IsAny <object[]>())); Assert.Same(account, result); Assert.True(msal.IsInitialized); }
public async Task GetAccessToken_WithTokenResponse_ReturnsToken() { var account = new MsalAccount() { Name = "Des", Username = "******", AccountIdentifier = "123" }; var token = new MsalToken(); var msal = new Mock <IMsal>(); msal.Setup(x => x.AcquireTokenAsync(It.IsAny <string[]>())) .ReturnsAsync(token); msal.Setup(x => x.GetAccountAsync()) .ReturnsAsync(account); var provider = new MsalAuthenticationStateProvider(msal.Object); var scopes = new string[0]; var result = await provider.GetAccessTokenAsync(scopes); msal.Verify(x => x.AcquireTokenAsync(scopes)); Assert.Same(token, result); }
public async Task GetAuthenticationState_WithAccount_ReturnsAuthenticated() { var account = new MsalAccount() { Name = "Des", Username = "******", AccountIdentifier = "123" }; var msal = new Mock <IMsal>(); msal.Setup(x => x.GetAccountAsync()) .Returns(Task.FromResult(account)); var provider = new MsalAuthenticationStateProvider(msal.Object); var state = await provider.GetAuthenticationStateAsync(); Assert.True(state.User.Identity.IsAuthenticated); Assert.Equal(account.Username, state.User.Identity.Name); }
public async Task SignIn_WithSuccess_RaisesAuthenticationChanged() { var account = new MsalAccount() { Name = "Des", Username = "******", AccountIdentifier = "123" }; var msal = new Mock <IMsal>(); msal.SetupSequence(x => x.GetAccountAsync()) // first call returns null .Returns(Task.FromResult <MsalAccount>(null)) // second and third calls returns account .Returns(Task.FromResult(account)) .Returns(Task.FromResult(account)); var provider = new MsalAuthenticationStateProvider(msal.Object); bool eventRaised = false; Task <AuthenticationState> stateAwaiter = null; provider.AuthenticationStateChanged += s => { eventRaised = true; stateAwaiter = s; }; await provider.SignInAsync(); Assert.True(eventRaised); Assert.NotNull(stateAwaiter); var state = await stateAwaiter; Assert.Equal(account.Username, state.User.Identity.Name); }
public async Task Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer, ILogger log) { log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}"); if (myTimer.IsPastDue) { log.LogInformation("Skipping past due invocation, waiting for next scheduled run"); return; } var subscriptions = await _msalAccountActivityStore.GetSubscriptionActivities(); foreach (var subscription in subscriptions) { // Get the most recently updated msal account information for this subscription var account = await _msalAccountActivityStore.GetMsalAccountActivityForSubscription(subscription.SubscriptionId); if (account != null) { // Configure the confidential client to get an access token for the needed resource var app = ConfidentialClientApplicationBuilder.Create(_config.GetValue <string>("AzureAd:ClientId")) .WithClientSecret(_config.GetValue <string>("AzureAd:ClientSecret")) .WithAuthority($"{_config.GetValue<string>("AzureAd:Instance")}{_config.GetValue<string>("AzureAd:TenantId")}") .Build(); // Initialize the MSAL cache for the specific account var msalCache = new BackgroundWorkerTokenCacheAdapter(account.AccountCacheKey, _serviceProvider.GetService <IDistributedCache>(), _serviceProvider.GetService <ILogger <MsalDistributedTokenCacheAdapter> >(), _serviceProvider.GetService <IOptions <MsalDistributedTokenCacheAdapterOptions> >()); await msalCache.InitializeAsync(app.UserTokenCache); // Prepare an MsalAccount instance representing the user we want to get a token for var hydratedAccount = new MsalAccount { HomeAccountId = new AccountId( account.AccountIdentifier, account.AccountObjectId, account.AccountTenantId) }; try { // Use the confidential MSAL client to get a token for the user we need to impersonate var result = await app.AcquireTokenSilent(Constants.BasePermissionScopes, hydratedAccount) .ExecuteAsync() .ConfigureAwait(false); // Configure the Graph SDK to use an auth provider that takes the token we've just requested var authenticationProvider = new DelegateAuthenticationProvider( (requestMessage) => { requestMessage.Headers.Authorization = new AuthenticationHeaderValue(CoreConstants.Headers.Bearer, result.AccessToken); return(Task.FromResult(0)); }); var graphClient = new GraphServiceClient(authenticationProvider); // Add/renew the subscriptions await SubscriptionManagement.ManageSubscription(subscription, account.AccountObjectId, account.AccountTenantId, account.UserPrincipalName, _config, _msalAccountActivityStore, graphClient, _msalTokenCacheProvider); } catch (MsalUiRequiredException ex) { /* * If MsalUiRequiredException is thrown for an account, it means that a user interaction is required * thus the background worker wont be able to acquire a token silently for it. * The user of that account will have to access the web app to perform this interaction. * Examples that could cause this: MFA requirement, token expired or revoked, token cache deleted, etc */ await _msalAccountActivityStore.HandleIntegratedTokenAcquisitionFailure(account); log.LogError($"Could not acquire token for account {account.UserPrincipalName}."); log.LogError($"Error: {ex.Message}"); } catch (Exception ex) { throw ex; } } } }
public async Task Run([QueueTrigger(Constants.OneDriveFileNotificationsQueue)] string myQueueItem, ILogger log) { log.LogInformation($"C# Queue trigger function processed: {myQueueItem}"); var notification = JsonSerializer.Deserialize <ChangeNotification>(myQueueItem); // Get the most recently updated msal account information for this subscription var account = await _msalAccountActivityStore.GetMsalAccountActivityForSubscription(notification.SubscriptionId); if (account != null) { // Configure the confidential client to get an access token for the needed resource var app = ConfidentialClientApplicationBuilder.Create(_config.GetValue <string>("AzureAd:ClientId")) .WithClientSecret(_config.GetValue <string>("AzureAd:ClientSecret")) .WithAuthority($"{_config.GetValue<string>("AzureAd:Instance")}{_config.GetValue<string>("AzureAd:TenantId")}") .Build(); // Initialize the MSAL cache for the specific account var msalCache = new BackgroundWorkerTokenCacheAdapter(account.AccountCacheKey, _serviceProvider.GetService <IDistributedCache>(), _serviceProvider.GetService <ILogger <MsalDistributedTokenCacheAdapter> >(), _serviceProvider.GetService <IOptions <MsalDistributedTokenCacheAdapterOptions> >()); await msalCache.InitializeAsync(app.UserTokenCache); // Prepare an MsalAccount instance representing the user we want to get a token for var hydratedAccount = new MsalAccount { HomeAccountId = new AccountId( account.AccountIdentifier, account.AccountObjectId, account.AccountTenantId) }; try { // Use the confidential MSAL client to get a token for the user we need to impersonate var result = await app.AcquireTokenSilent(Constants.BasePermissionScopes, hydratedAccount) .ExecuteAsync() .ConfigureAwait(false); //log.LogInformation($"Token acquired: {result.AccessToken}"); // Configure the Graph SDK to use an auth provider that takes the token we've just requested var authenticationProvider = new DelegateAuthenticationProvider( (requestMessage) => { requestMessage.Headers.Authorization = new AuthenticationHeaderValue(CoreConstants.Headers.Bearer, result.AccessToken); return(Task.FromResult(0)); }); var graphClient = new GraphServiceClient(authenticationProvider); // Retrieve the last used subscription activity information for this user+subscription var subscriptionActivity = await _msalAccountActivityStore.GetSubscriptionActivityForUserSubscription(account.AccountObjectId, account.AccountTenantId, account.UserPrincipalName, notification.SubscriptionId); // Make graph call on behalf of the user: do the delta query providing the last used change token so only new changes are returned IDriveItemDeltaCollectionPage deltaCollection = await graphClient.Me.Drive.Root.Delta(subscriptionActivity.LastChangeToken).Request().GetAsync(); bool morePagesAvailable = false; do { // If there is a NextPageRequest, there are more pages morePagesAvailable = deltaCollection.NextPageRequest != null; foreach (var driveItem in deltaCollection.CurrentPage) { await ProcessDriveItemChanges(driveItem, log); } if (morePagesAvailable) { // Get the next page of results deltaCollection = await deltaCollection.NextPageRequest.GetAsync(); } }while (morePagesAvailable); // Get the last used change token var deltaLink = deltaCollection.AdditionalData["@odata.deltaLink"]; if (!string.IsNullOrEmpty(deltaLink.ToString())) { var token = GetChangeTokenFromUrl(deltaLink.ToString()); subscriptionActivity.LastChangeToken = token; } // Persist back the last used change token await _msalAccountActivityStore.UpsertSubscriptionActivity(subscriptionActivity); } catch (MsalUiRequiredException ex) { /* * If MsalUiRequiredException is thrown for an account, it means that a user interaction is required * thus the background worker wont be able to acquire a token silently for it. * The user of that account will have to access the web app to perform this interaction. * Examples that could cause this: MFA requirement, token expired or revoked, token cache deleted, etc */ await _msalAccountActivityStore.HandleIntegratedTokenAcquisitionFailure(account); log.LogError($"Could not acquire token for account {account.UserPrincipalName}."); log.LogError($"Error: {ex.Message}"); } catch (Exception ex) { throw ex; } } }