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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }
예제 #5
0
        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;
                    }
                }
            }
        }
예제 #6
0
        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;
                }
            }
        }