public ManageSubscriptions(IConfiguration config, IMsalAccountActivityStore msalAccountActivityStore, IServiceProvider serviceProvider, IMsalTokenCacheProvider msalTokenCacheProvider) { _config = config; _msalAccountActivityStore = msalAccountActivityStore; _serviceProvider = serviceProvider; _msalTokenCacheProvider = msalTokenCacheProvider; }
/// <summary> /// Use a token cache and choose the serialization part by adding it to /// the services collection and configuring its options. /// </summary> /// <returns>The confidential client application.</returns> /// <param name="confidentialClientApp">Confidential client application.</param> /// <param name="initializeCaches">Action that you'll use to add a cache serialization /// to the service collection passed as an argument.</param> /// <returns>The application for chaining.</returns> /// <example> /// /// The following code adds a distributed in-memory token cache. /// /// <code> /// app.AddTokenCaches(services => /// { /// // In memory distributed token cache /// // In net472, requires to reference Microsoft.Extensions.Caching.Memory /// services.AddDistributedTokenCache(); /// services.AddDistributedMemoryCache(); /// }); /// </code> /// /// The following code adds a token cache based on REDIS and initializes /// its configuration. /// /// <code> /// app.AddTokenCaches(services => /// { /// services.AddDistributedTokenCache(); /// // Redis token cache /// // Requires to reference Microsoft.Extensions.Caching.StackExchangeRedis /// services.AddStackExchangeRedisCache(options => /// { /// options.Configuration = "localhost"; /// options.InstanceName = "Redis"; /// }); /// }); /// </code> /// If using distributed token caches, use AddDistributedTokenCache. /// </example> /// <remarks>Don't use this method in ASP.NET Core. Just add use the ConfigureServices method /// instead.</remarks> internal static IConfidentialClientApplication AddTokenCaches( this IConfidentialClientApplication confidentialClientApp, Action <IServiceCollection> initializeCaches) { _ = confidentialClientApp ?? throw new ArgumentNullException(nameof(confidentialClientApp)); _ = initializeCaches ?? throw new ArgumentNullException(nameof(initializeCaches)); // try to reuse existing XYZ cache if AddXYZCache was called before, to simulate ASP.NET Core var serviceProvider = s_serviceProviderFromAction.GetOrAdd(initializeCaches.Method, (m) => { lock (s_serviceProviderFromAction) { ServiceCollection services = new ServiceCollection(); initializeCaches(services); services.AddLogging(); return(services.BuildServiceProvider()); } }); IMsalTokenCacheProvider msalTokenCacheProvider = serviceProvider.GetRequiredService <IMsalTokenCacheProvider>(); msalTokenCacheProvider.Initialize(confidentialClientApp.UserTokenCache); msalTokenCacheProvider.Initialize(confidentialClientApp.AppTokenCache); return(confidentialClientApp); }
public Connect(ITokenAcquisition tokenAcquisition, GraphServiceClient graphServiceClient, IConfiguration config, IMsalAccountActivityStore msalAccountActivityStore, IMsalTokenCacheProvider msalTokenCacheProvider) { _tokenAcquisition = tokenAcquisition; _graphServiceClient = graphServiceClient; _config = config; _msalAccountActivityStore = msalAccountActivityStore; _msalTokenCacheProvider = msalTokenCacheProvider; }
/// <summary> /// Constructor of the AppServicesAuthenticationTokenAcquisition. /// </summary> /// <param name="tokenCacheProvider">The App token cache provider.</param> /// <param name="httpContextAccessor">Access to the HttpContext of the request.</param> /// <param name="httpClientFactory">HTTP client factory.</param> public AppServicesAuthenticationTokenAcquisition( IMsalTokenCacheProvider tokenCacheProvider, IHttpContextAccessor httpContextAccessor, IHttpClientFactory httpClientFactory) { _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); _httpClientFactory = new MsalAspNetCoreHttpClientFactory(httpClientFactory); _tokenCacheProvider = tokenCacheProvider; }
/// <summary> /// Constructor of the TokenAcquisition service. This requires the Azure AD Options to /// configure the confidential client application and a token cache provider. /// This constructor is called by ASP.NET Core dependency injection /// </summary> /// <param name="configuration"></param> /// <param name="tokenCacheProvider">The App token cache provider</param> /// <param name="userTokenCacheProvider">The User token cache provider</param> public TokenAcquisition( IMsalTokenCacheProvider tokenCacheProvider, IHttpContextAccessor httpContextAccessor, IOptions <AzureADOptions> azureAdOptions, IOptions <ConfidentialClientApplicationOptions> applicationOptions) { _httpContextAccessor = httpContextAccessor; _azureAdOptions = azureAdOptions.Value; _applicationOptions = applicationOptions.Value; _tokenCacheProvider = tokenCacheProvider; }
/// <summary> /// Constructor of the TokenAcquisition service. This requires the Azure AD Options to /// configure the confidential client application and a token cache provider. /// This constructor is called by ASP.NET Core dependency injection /// </summary> /// <param name="configuration"></param> /// <param name="tokenCacheProvider">The App token cache provider</param> /// <param name="userTokenCacheProvider">The User token cache provider</param> public TokenAcquisition( IMsalTokenCacheProvider tokenCacheProvider, IHttpContextAccessor httpContextAccessor, IOptions <MicrosoftIdentityOptions> microsoftIdentityOptions, IOptions <ConfidentialClientApplicationOptions> applicationOptions) { _httpContextAccessor = httpContextAccessor; _microsoftIdentityOptions = microsoftIdentityOptions.Value; _applicationOptions = applicationOptions.Value; _tokenCacheProvider = tokenCacheProvider; }
private static IMsalTokenCacheProvider CreateTokenCacheSerializer() { IServiceCollection services = new ServiceCollection(); // In memory token cache. Other forms of serialization are possible. // See https://github.com/AzureAD/microsoft-identity-web/wiki/asp-net services.AddInMemoryTokenCaches(); IServiceProvider serviceProvider = services.BuildServiceProvider(); IMsalTokenCacheProvider msalTokenCacheProvider = serviceProvider.GetRequiredService <IMsalTokenCacheProvider>(); return(msalTokenCacheProvider); }
public static async Task <IConfidentialClientApplication> BuildConfidentialClientApplication() { IConfidentialClientApplication clientapp = ConfidentialClientApplicationBuilder.Create(AuthenticationConfig.ClientId) .WithClientSecret(AuthenticationConfig.ClientSecret) .WithRedirectUri(AuthenticationConfig.RedirectUri) .WithAuthority(new Uri(AuthenticationConfig.Authority)) .Build(); // After the ConfidentialClientApplication is created, we overwrite its default UserTokenCache serialization with our implementation IMsalTokenCacheProvider memoryTokenCacheProvider = CreateTokenCacheSerializer(); await memoryTokenCacheProvider.InitializeAsync(clientapp.UserTokenCache); return(clientapp); }
/// <summary> /// Constructor of the TokenAcquisition service. This requires the Azure AD Options to /// configure the confidential client application and a token cache provider. /// This constructor is called by ASP.NET Core dependency injection. /// </summary> /// <param name="tokenCacheProvider">The App token cache provider.</param> /// <param name="httpContextAccessor">Access to the HttpContext of the request.</param> /// <param name="microsoftIdentityOptions">Configuration options.</param> /// <param name="applicationOptions">MSAL.NET configuration options.</param> /// <param name="httpClientFactory">HTTP client factory.</param> /// <param name="logger">Logger.</param> public TokenAcquisition( IMsalTokenCacheProvider tokenCacheProvider, IHttpContextAccessor httpContextAccessor, IOptions <MicrosoftIdentityOptions> microsoftIdentityOptions, IOptions <ConfidentialClientApplicationOptions> applicationOptions, IHttpClientFactory httpClientFactory, ILogger <TokenAcquisition> logger) { _httpContextAccessor = httpContextAccessor; _microsoftIdentityOptions = microsoftIdentityOptions.Value; _applicationOptions = applicationOptions.Value; _tokenCacheProvider = tokenCacheProvider; _httpClientFactory = new MsalAspNetCoreHttpClientFactory(httpClientFactory); _logger = logger; }
public static async Task RemoveAccount() { IConfidentialClientApplication clientapp = ConfidentialClientApplicationBuilder.Create(AuthenticationConfig.ClientId) .WithClientSecret(AuthenticationConfig.ClientSecret) .WithRedirectUri(AuthenticationConfig.RedirectUri) .WithAuthority(new Uri(AuthenticationConfig.Authority)) .Build(); // We only clear the user's tokens. IMsalTokenCacheProvider memoryTokenCacheProvider = CreateTokenCacheSerializer(); await memoryTokenCacheProvider.InitializeAsync(clientapp.UserTokenCache); var userAccount = await clientapp.GetAccountAsync(ClaimsPrincipal.Current.GetAccountId()); if (userAccount != null) { await clientapp.RemoveAsync(userAccount); } }
/// <summary> /// Constructor of the TokenAcquisition service. This requires the Azure AD Options to /// configure the confidential client application and a token cache provider. /// This constructor is called by ASP.NET Core dependency injection. /// </summary> /// <param name="tokenCacheProvider">The App token cache provider.</param> /// <param name="httpContextAccessor">Access to the HttpContext of the request.</param> /// <param name="microsoftIdentityOptions">Configuration options.</param> /// <param name="applicationOptions">MSAL.NET configuration options.</param> /// <param name="httpClientFactory">HTTP client factory.</param> /// <param name="logger">Logger.</param> /// <param name="serviceProvider">Service provider.</param> public TokenAcquisition( IMsalTokenCacheProvider tokenCacheProvider, IHttpContextAccessor httpContextAccessor, IOptions <MicrosoftIdentityOptions> microsoftIdentityOptions, IOptions <ConfidentialClientApplicationOptions> applicationOptions, IHttpClientFactory httpClientFactory, ILogger <TokenAcquisition> logger, IServiceProvider serviceProvider) { _httpContextAccessor = httpContextAccessor; _microsoftIdentityOptions = microsoftIdentityOptions.Value; _applicationOptions = applicationOptions.Value; _tokenCacheProvider = tokenCacheProvider; _httpClientFactory = new MsalAspNetCoreHttpClientFactory(httpClientFactory); _logger = logger; _serviceProvider = serviceProvider; _applicationOptions.ClientId ??= _microsoftIdentityOptions.ClientId; _applicationOptions.Instance ??= _microsoftIdentityOptions.Instance; _applicationOptions.ClientSecret ??= _microsoftIdentityOptions.ClientSecret; _applicationOptions.TenantId ??= _microsoftIdentityOptions.TenantId; }
/// <summary> /// Constructor of the TokenAcquisition service. This requires the Azure AD Options to /// configure the confidential client application and a token cache provider. /// This constructor is called by ASP.NET Core dependency injection. /// </summary> /// <param name="tokenCacheProvider">The App token cache provider.</param> /// <param name="httpContextAccessor">Access to the HttpContext of the request.</param> /// <param name="microsoftIdentityOptions">Configuration options.</param> /// <param name="applicationOptions">MSAL.NET configuration options.</param> /// <param name="httpClientFactory">HTTP client factory.</param> /// <param name="logger">Logger.</param> /// <param name="serviceProvider">Service provider.</param> public TokenAcquisition( IMsalTokenCacheProvider tokenCacheProvider, IHttpContextAccessor httpContextAccessor, IOptions <MicrosoftIdentityOptions> microsoftIdentityOptions, IOptions <ConfidentialClientApplicationOptions> applicationOptions, IHttpClientFactory httpClientFactory, ILogger <TokenAcquisition> logger, IServiceProvider serviceProvider) { _httpContextAccessor = httpContextAccessor; _microsoftIdentityOptions = microsoftIdentityOptions.Value; _applicationOptions = applicationOptions.Value; _tokenCacheProvider = tokenCacheProvider; _httpClientFactory = new MsalAspNetCoreHttpClientFactory(httpClientFactory); _logger = logger; _serviceProvider = serviceProvider; _applicationOptions.ClientId ??= _microsoftIdentityOptions.ClientId; _applicationOptions.Instance ??= _microsoftIdentityOptions.Instance; _applicationOptions.ClientSecret ??= _microsoftIdentityOptions.ClientSecret; _applicationOptions.TenantId ??= _microsoftIdentityOptions.TenantId; _applicationOptions.LegacyCacheCompatibilityEnabled = _microsoftIdentityOptions.LegacyCacheCompatibilityEnabled; DefaultCertificateLoader.UserAssignedManagedIdentityClientId = _microsoftIdentityOptions.UserAssignedManagedIdentityClientId; }
static async Task Main(string[] args) { string clientId = "812287fd-3ea3-49c6-b4ab-e8d41dea1f17"; string clientSecret = "[Enter here the secret register with your application]"; string tenant = "msidentitysamplestesting.onmicrosoft.com"; string[] scopes = new[] { "api://2d96f90e-a1a7-4ef5-b15c-87758986eb1a/.default" }; CacheImplementationDemo cacheImplementation = CacheImplementationDemo.InMemory; // Create the token cache (4 possible implementations) IMsalTokenCacheProvider msalTokenCacheProvider = CreateTokenCache(cacheImplementation); // Create the confidential client application IConfidentialClientApplication app; app = ConfidentialClientApplicationBuilder.Create(clientId) .WithClientSecret(clientSecret) .WithTenantId(tenant) .Build(); await msalTokenCacheProvider.InitializeAsync(app.UserTokenCache); await msalTokenCacheProvider.InitializeAsync(app.AppTokenCache); // 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); }
public void CreateTokenCacheSerializerTest() { IMsalTokenCacheProvider tokenCacheProvider = CreateTokenCacheSerializer(); Assert.NotNull(tokenCacheProvider); }
/// <summary> /// Creates a token cache (implementation of your choice) /// </summary> /// <param name="cacheImplementation">implementation for the token cache</param> /// <returns>An Msal Token cache provider</returns> private static IMsalTokenCacheProvider CreateTokenCache(CacheImplementationDemo cacheImplementation = CacheImplementationDemo.InMemory) { IServiceCollection services = new ServiceCollection(); // (Simulates the configuration, could be a IConfiguration or anything) Dictionary <string, string> Configuration = new Dictionary <string, string>(); switch (cacheImplementation) { case CacheImplementationDemo.InMemory: // In memory token cache // In net472, requires to reference Microsoft.Extensions.Caching.Memory services.AddInMemoryTokenCaches(); break; case CacheImplementationDemo.DistributedMemory: // In memory distributed token cache // In net472, requires to reference Microsoft.Extensions.Caching.Memory services.AddDistributedTokenCaches(); services.AddDistributedMemoryCache(); break; case CacheImplementationDemo.DistributedSqlServer: // SQL Server token cache // Requires to reference Microsoft.Extensions.Caching.SqlServer services.AddDistributedTokenCaches(); services.AddDistributedSqlServerCache(options => { 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); }); break; case CacheImplementationDemo.StackExchangeRedis: // Redis token cache // Requires to reference Microsoft.Extensions.Caching.StackExchangeRedis services.AddDistributedTokenCaches(); services.AddStackExchangeRedisCache(options => { options.Configuration = "localhost"; options.InstanceName = "SampleInstance"; }); break; case CacheImplementationDemo.CosmosDb: // Redis token cache // Requires to reference Microsoft.Extensions.Caching.Cosmos (preview) services.AddDistributedTokenCaches(); services.AddCosmosCache((CosmosCacheOptions cacheOptions) => { cacheOptions.ContainerName = Configuration["CosmosCacheContainer"]; cacheOptions.DatabaseName = Configuration["CosmosCacheDatabase"]; cacheOptions.ClientBuilder = new CosmosClientBuilder(Configuration["CosmosConnectionString"]); cacheOptions.CreateIfNotExists = true; }); break; default: break; } IServiceProvider serviceProvider = services.BuildServiceProvider(); IMsalTokenCacheProvider msalTokenCacheProvider = serviceProvider.GetRequiredService <IMsalTokenCacheProvider>(); return(msalTokenCacheProvider); }
/// <summary> /// Creates a token cache (implementation of your choice) /// </summary> /// <param name="cacheImplementation">implementation for the token cache</param> /// <returns>An Msal Token cache provider</returns> private static IMsalTokenCacheProvider CreateTokenCache(CacheImplementationDemo cacheImplementation = CacheImplementationDemo.InMemory) { IServiceCollection services = new ServiceCollection(); // (Simulates the configuration, could be a IConfiguration or anything) Dictionary <string, string> Configuration = new Dictionary <string, string>(); switch (cacheImplementation) { case CacheImplementationDemo.InMemory: // In memory token cache // In net472, requires to reference Microsoft.Extensions.Caching.Memory services.AddInMemoryTokenCaches(); break; case CacheImplementationDemo.DistributedMemory: // In memory distributed token cache // In net472, requires to reference Microsoft.Extensions.Caching.Memory services.AddDistributedTokenCaches(); services.AddDistributedMemoryCache(); break; case CacheImplementationDemo.DistributedSqlServer: // SQL Server token cache // Requires to reference Microsoft.Extensions.Caching.SqlServer services.AddDistributedTokenCaches(); services.AddDistributedSqlServerCache(options => { 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"; }); break; case CacheImplementationDemo.StackExchangeRedis: // Redis token cache // Requires to reference Microsoft.Extensions.Caching.StackExchangeRedis services.AddDistributedTokenCaches(); services.AddStackExchangeRedisCache(options => { options.Configuration = "localhost"; options.InstanceName = "SampleInstance"; }); break; case CacheImplementationDemo.CosmosDb: // Redis token cache // Requires to reference Microsoft.Extensions.Caching.Cosmos (preview) services.AddCosmosCache((CosmosCacheOptions cacheOptions) => { cacheOptions.ContainerName = Configuration["CosmosCacheContainer"]; cacheOptions.DatabaseName = Configuration["CosmosCacheDatabase"]; cacheOptions.ClientBuilder = new CosmosClientBuilder(Configuration["CosmosConnectionString"]); cacheOptions.CreateIfNotExists = true; }); break; default: break; } IServiceProvider serviceProvider = services.BuildServiceProvider(); IMsalTokenCacheProvider msalTokenCacheProvider = serviceProvider.GetRequiredService <IMsalTokenCacheProvider>(); return(msalTokenCacheProvider); }
internal static async Task ManageSubscription(SubscriptionActivity currentSubscriptionActivity, string oid, string tid, string upn, IConfiguration config, IMsalAccountActivityStore msalAccountActivityStore, GraphServiceClient _graphServiceClient, IMsalTokenCacheProvider msalTokenCacheProvider) { string subscriptionId = null; string changeToken = null; string notiticationUrl = config.GetValue <string>("Files:SubscriptionService"); int subscriptionLifeTimeInMinutes = config.GetValue <int>("Files:SubscriptionLifeTime"); if (subscriptionLifeTimeInMinutes == 0) { subscriptionLifeTimeInMinutes = 15; } // Load the current subscription (if any) var currentSubscriptions = await _graphServiceClient.Subscriptions.Request().GetAsync(); var currentOneDriveSubscription = currentSubscriptions.FirstOrDefault(p => p.Resource == "me/drive/root"); // If present and still using the same subscription host then update the subscription expiration date if (currentOneDriveSubscription != null && currentOneDriveSubscription.NotificationUrl.Equals(notiticationUrl, StringComparison.InvariantCultureIgnoreCase)) { // Extend the expiration of the current subscription subscriptionId = currentOneDriveSubscription.Id; currentOneDriveSubscription.ExpirationDateTime = DateTimeOffset.UtcNow.AddMinutes(subscriptionLifeTimeInMinutes); currentOneDriveSubscription.ClientState = Constants.FilesSubscriptionServiceClientState; await _graphServiceClient.Subscriptions[currentOneDriveSubscription.Id].Request().UpdateAsync(currentOneDriveSubscription); // Check if the last change token was populated if (currentSubscriptionActivity == null) { currentSubscriptionActivity = await msalAccountActivityStore.GetSubscriptionActivityForUserSubscription(oid, tid, upn, subscriptionId); } if (currentSubscriptionActivity != null) { changeToken = currentSubscriptionActivity.LastChangeToken; } } else { // Add a new subscription var newSubscription = await _graphServiceClient.Subscriptions.Request().AddAsync(new Subscription() { ChangeType = "updated", NotificationUrl = notiticationUrl, Resource = "me/drive/root", ExpirationDateTime = DateTimeOffset.UtcNow.AddMinutes(subscriptionLifeTimeInMinutes), ClientState = Constants.FilesSubscriptionServiceClientState, LatestSupportedTlsVersion = "v1_2" }); subscriptionId = newSubscription.Id; } // Store the user principal name with the subscriptionid as that's the mechanism needed to connect change event with tenant/user var cacheEntriesToRemove = await msalAccountActivityStore.UpdateSubscriptionId(subscriptionId, oid, tid, upn); // If we've found old MSAL cache entries for which we've removed the respective MsalActivity records we should also // drop these from the MSAL cache itself if (cacheEntriesToRemove.Any()) { foreach (var cacheEntry in cacheEntriesToRemove) { await(msalTokenCacheProvider as IntegratedTokenCacheAdapter).RemoveKeyFromCache(cacheEntry); } } if (changeToken == null) { // Initialize the subscription and get the latest change token, to avoid getting back all the historical changes IDriveItemDeltaCollectionPage deltaCollection = await _graphServiceClient.Me.Drive.Root.Delta("latest").Request().GetAsync(); var deltaLink = deltaCollection.AdditionalData["@odata.deltaLink"]; if (!string.IsNullOrEmpty(deltaLink.ToString())) { changeToken = ProcessChanges.GetChangeTokenFromUrl(deltaLink.ToString()); } } // Store a record per user/subscription to track future delta queries if (currentSubscriptionActivity == null) { currentSubscriptionActivity = new SubscriptionActivity(oid, tid, upn) { SubscriptionId = subscriptionId, LastChangeToken = changeToken }; } currentSubscriptionActivity.LastChangeToken = changeToken; currentSubscriptionActivity.SubscriptionId = subscriptionId; await msalAccountActivityStore.UpsertSubscriptionActivity(currentSubscriptionActivity); }