public void TestLoadAllCertificates( CertificateSource certificateSource, string container, string referenceOrValue) { List <CertificateDescription> certDescriptions = CreateCertificateDescriptions( certificateSource, container, referenceOrValue).ToList(); certDescriptions.Add(new CertificateDescription { SourceType = certificateSource, Container = container, ReferenceOrValue = referenceOrValue, }); certDescriptions.Add(CertificateDescription.FromCertificate(null)); IEnumerable <X509Certificate2> certificates = DefaultCertificateLoader.LoadAllCertificates(certDescriptions); Assert.NotNull(certificates); Assert.Equal(2, certificates.Count()); Assert.Equal(3, certDescriptions.Count); Assert.Equal("CN=ACS2ClientCertificate", certificates.First().Issuer); Assert.Equal("CN=ACS2ClientCertificate", certificates.Last().Issuer); Assert.Null(certDescriptions.ElementAt(2).Certificate); }
private static async Task RunAsync() { AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json"); // Load the certificate ICertificateLoader certificateLoader = new DefaultCertificateLoader(); certificateLoader.LoadIfNeeded(config.Certificate); // Even if this is a console application here, a daemon application is a confidential client application IConfidentialClientApplication app; app = ConfidentialClientApplicationBuilder.Create(config.ClientId) .WithCertificate(config.Certificate.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); } }
public void TestLoadFirstCertificate( CertificateSource certificateSource, string container, string referenceOrValue) { IEnumerable <CertificateDescription> certDescriptions = CreateCertificateDescriptions( certificateSource, container, referenceOrValue); X509Certificate2 certificate = DefaultCertificateLoader.LoadFirstCertificate(certDescriptions); Assert.NotNull(certificate); Assert.Equal("CN=ACS2ClientCertificate", certificate.Issuer); }
public void TestDefaultCertificateLoader(CertificateSource certificateSource, string container, string referenceOrValue) { CertificateDescription certificateDescription; switch (certificateSource) { case CertificateSource.KeyVault: certificateDescription = CertificateDescription.FromKeyVault(container, referenceOrValue); break; case CertificateSource.Base64Encoded: certificateDescription = CertificateDescription.FromBase64Encoded(referenceOrValue); break; case CertificateSource.Path: certificateDescription = CertificateDescription.FromPath(container, referenceOrValue); break; case CertificateSource.StoreWithThumbprint: certificateDescription = new CertificateDescription() { SourceType = CertificateSource.StoreWithThumbprint }; certificateDescription.CertificateThumbprint = referenceOrValue; certificateDescription.CertificateStorePath = container; break; case CertificateSource.StoreWithDistinguishedName: certificateDescription = new CertificateDescription() { SourceType = CertificateSource.StoreWithDistinguishedName }; certificateDescription.CertificateDistinguishedName = referenceOrValue; certificateDescription.CertificateStorePath = container; break; default: certificateDescription = null; break; } ICertificateLoader loader = new DefaultCertificateLoader(); loader.LoadIfNeeded(certificateDescription); Assert.NotNull(certificateDescription.Certificate); }
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 = IsAppUsingClientSecret(config); // Even if this is a console application here, a daemon application is a confidential client application IConfidentialClientApplication app; if (isUsingClientSecret) { // Even if this is a console application here, a daemon application is a confidential client application app = ConfidentialClientApplicationBuilder.Create(config.ClientId) .WithClientSecret(config.ClientSecret) .WithAuthority(new Uri(config.Authority)) .Build(); } else { ICertificateLoader certificateLoader = new DefaultCertificateLoader(); certificateLoader.LoadIfNeeded(config.Certificate); app = ConfidentialClientApplicationBuilder.Create(config.ClientId) .WithCertificate(config.Certificate.Certificate) .WithAuthority(new Uri(config.Authority)) .Build(); } app.AddInMemoryTokenCache(); // 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" }; // Generates a scope -> "https://graph.microsoft.com/.default" // Call MS graph using the Graph SDK await CallMSGraphUsingGraphSDK(app, scopes); // Call MS Graph REST API directly await CallMSGraph(config, app, scopes); }
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); }