/// <summary> /// Returns an access token depending on the type requested for testing. /// </summary> /// <returns></returns> private Task <AppAuthenticationResult> AcquireTokenAsync() { AppAuthenticationResult authResult; switch (_mockAuthenticationContextTestType) { case MockAuthenticationContextTestType.AcquireTokenSilentAsyncFail: case MockAuthenticationContextTestType.AcquireTokenAsyncClientCertificateFail: case MockAuthenticationContextTestType.AcquireTokenAsyncClientCredentialFail: case MockAuthenticationContextTestType.AcquireTokenAsyncUserCredentialFail: return(Task.FromResult <AppAuthenticationResult>(null)); case MockAuthenticationContextTestType.AcquireInvalidTokenAsyncFail: authResult = AppAuthenticationResult.Create(TokenHelper.GetInvalidAppToken()); return(Task.FromResult(authResult)); case MockAuthenticationContextTestType.AcquireTokenAsyncException: throw new Exception(Constants.AdalException); case MockAuthenticationContextTestType.AcquireTokenSilentAsyncSuccess: case MockAuthenticationContextTestType.AcquireTokenAsyncUserCredentialSuccess: authResult = AppAuthenticationResult.Create(TokenHelper.GetUserToken()); return(Task.FromResult(authResult)); case MockAuthenticationContextTestType.AcquireTokenAsyncClientCertificateSuccess: case MockAuthenticationContextTestType.AcquireTokenAsyncClientCredentialSuccess: authResult = AppAuthenticationResult.Create(TokenHelper.GetAppToken()); return(Task.FromResult(authResult)); } return(null); }
public void CanGetJwtTokenWithForceRefresh(bool forceRefreshInput) { var authResult = new AppAuthenticationResult(); var tokenProvider = new Mock <AzureServiceTokenProvider>(TestConnectionString, TestAzureAdInstance); tokenProvider .Setup(p => p.GetAuthenticationResultAsync( It.IsAny <string>(), It.IsAny <bool>(), It.IsAny <CancellationToken>())) .Returns <string, bool, CancellationToken>((resource, forceRefresh, cancellationToken) => { Assert.Equal(forceRefreshInput, forceRefresh); return(Task.FromResult(authResult)); }); var tokenProviderFactory = new Mock <IJwtTokenProviderFactory>(); tokenProviderFactory .Setup(f => f.CreateAzureServiceTokenProvider(It.IsAny <string>(), It.IsAny <HttpClient>())) .Returns <string, HttpClient>((appId, customHttpClient) => tokenProvider.Object); var sut = new ManagedIdentityAuthenticator(TestAppId, TestAudience, tokenProviderFactory.Object); var token = sut.GetTokenAsync(forceRefreshInput).GetAwaiter().GetResult(); Assert.Equal(authResult.AccessToken, token.AccessToken); Assert.Equal(authResult.ExpiresOn, token.ExpiresOn); }
public override async ValueTask <AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken) { var authProvider = new AzureServiceTokenProvider(); AppAuthenticationResult result = await authProvider.GetAuthenticationResultAsync(Resource, _managedIdentityTenantId); return(new AccessToken(result.AccessToken, result.ExpiresOn)); }
private async Task <NewTokenAndFrequency> TokenRenewerAsync( object state, CancellationToken cancellationToken) { string StorageResource = BlobServiceURI; AppAuthenticationResult authResult = await((AzureServiceTokenProvider)state).GetAuthenticationResultAsync(StorageResource, (string)null, new CancellationToken()); TimeSpan next = authResult.ExpiresOn - DateTimeOffset.UtcNow - TimeSpan.FromMinutes(5.0); if (next.Ticks < 0L) { next = new TimeSpan(); } return(new NewTokenAndFrequency(authResult.AccessToken, new TimeSpan?(next))); }
public override async ValueTask <AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken) { var authProvider = new AzureServiceTokenProvider(); string tenantId = _managedIdentityTenantId; if (tenantId != null && tenantId.Length == 0) { tenantId = null; //We want to clearly indicate to the provider if we do not specify a tenant, so no empty strings } AppAuthenticationResult result = await authProvider.GetAuthenticationResultAsync(Resource, tenantId); return(new AccessToken(result.AccessToken, result.ExpiresOn)); }
private static async Task <NewTokenAndFrequency> TokenRenewerAsync(object state, CancellationToken cancellationToken) { // Specify the resource ID for requesting Azure AD tokens for Azure Storage. const string StorageResource = "https://storage.azure.com/"; // Use the same token provider to request a new token. AppAuthenticationResult authResult = await((AzureServiceTokenProvider)state).GetAuthenticationResultAsync(StorageResource, cancellationToken: cancellationToken); // Renew the token 5 minutes before it expires. var next = (authResult.ExpiresOn - DateTimeOffset.UtcNow) - TimeSpan.FromMinutes(5); if (next.Ticks < 0) { next = default(TimeSpan); Debug.WriteLine("Renewing token..."); } // Return the new token and the next refresh time. return(new NewTokenAndFrequency(authResult.AccessToken, next)); }
public async Task GetTokenUsingServicePrincipalWithClientSecretTest() { GraphHelper graphHelper = new GraphHelper(_tenantId); string secret = Convert.ToBase64String(Encoding.ASCII.GetBytes(Guid.NewGuid().ToString())); Application app = await graphHelper.CreateApplicationAsync(secret); // Get token using service principal AppAuthenticationResult authResult = null; int count = 5; Environment.SetEnvironmentVariable(Constants.ConnectionStringEnvironmentVariableName, $"RunAs=App;TenantId={_tenantId};AppId={app.AppId};AppKey={secret}"); AzureServiceTokenProvider astp = new AzureServiceTokenProvider(); while (authResult == null && count > 0) { try { authResult = await astp.GetAuthenticationResultAsync(Constants.SqlAzureResourceId, _tenantId); await astp.GetAccessTokenAsync(Constants.KeyVaultResourceId); } catch { // It takes time for Azure AD to realize a new application has been added. await Task.Delay(15000); count--; } } // Delete the application await graphHelper.DeleteApplicationAsync(app); Environment.SetEnvironmentVariable(Constants.ConnectionStringEnvironmentVariableName, null); Validator.ValidateToken(authResult.AccessToken, astp.PrincipalUsed, Constants.AppType, _tenantId, app.AppId, expiresOn: authResult.ExpiresOn); }
public async Task GetAppAuthResultCacheTest() { // Create two instances of AzureServiceTokenProvider based on AzureCliAccessTokenProvider. MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(azureCliAccessTokenProvider); AzureServiceTokenProvider azureServiceTokenProvider1 = new AzureServiceTokenProvider(azureCliAccessTokenProvider); List <Task> tasks = new List <Task>(); // ManualResetEvent will enable testing of SemaphoreSlim used in AzureServiceTokenProvider. ManualResetEvent manualResetEvent = new ManualResetEvent(false); // Use AzureServiceTokenProviders to get tokens in parallel. for (int i = 0; i < 5; i++) { Task task = Task.Run(async delegate { // This will prevent the next line from running, until manualResetEvent.Set() is called. // This will ensure all GetAccessTokenAsync calls are made at once. manualResetEvent.WaitOne(); await azureServiceTokenProvider.GetAccessTokenAsync(Constants.KeyVaultResourceId); }); tasks.Add(task); Task task1 = Task.Run(async delegate { manualResetEvent.WaitOne(); // This is using the other instance of AzureServiceTokenProvider. await azureServiceTokenProvider1.GetAccessTokenAsync(Constants.KeyVaultResourceId); }); tasks.Add(task1); } // This will cause GetAccessTokenAsync calls to be made concurrently. manualResetEvent.Set(); await Task.WhenAll(tasks); // Even though multiple calls are made to get token concurrently, using two different instances, the process manager should only be called once. // This test tells us that the cache is working as intended. Assert.Equal(1, mockProcessManager.HitCount); // Get the token again. This will test if the cache call before semaphore use is working as intended. for (int i = 0; i < 5; i++) { tasks.Add(azureServiceTokenProvider.GetAccessTokenAsync(Constants.KeyVaultResourceId)); } await Task.WhenAll(tasks); // The hit count should still be 1, since the token should be fetched from cache. Assert.Equal(1, mockProcessManager.HitCount); // Update the cache entry, to simulate token expiration. This updated token will expire in just less than 5 minutes. // In a real scenario, the token will expire after some time. // AppAuthResultCache should not return this, since it is about to expire. var tokenResponse = TokenResponse.Parse(TokenHelper.GetUserTokenResponse(5 * 60 - 2)); var authResult = AppAuthenticationResult.Create(tokenResponse, TokenResponse.DateFormat.DateTimeString); AppAuthResultCache.AddOrUpdate("ConnectionString:;Authority:;Resource:https://vault.azure.net/", new Tuple <AppAuthenticationResult, Principal>(authResult, null)); // Get the token again. for (int i = 0; i < 5; i++) { tasks.Add(azureServiceTokenProvider.GetAccessTokenAsync(Constants.KeyVaultResourceId)); } await Task.WhenAll(tasks); // Hit count should be 2 now, since new token should have been aquired. Assert.Equal(2, mockProcessManager.HitCount); }
/// <summary> /// One must be logged in using Azure CLI and set AppAuthenticationTestCertUrl to a secret URL for a certificate in Key Vault before running this test. /// The test creates a new Azure AD application and service principal, uses the cert as the credential, and then uses the library to authenticate to it, using the cert. /// After the cert, the Azure AD application is deleted. Cert is removed from current user store on the machine. /// </summary> /// <param name="certIdentifierType"></param> /// <returns></returns> private async Task GetTokenUsingServicePrincipalWithCertTestImpl(CertIdentifierType certIdentifierType) { string testCertUrl = Environment.GetEnvironmentVariable(Constants.TestCertUrlEnv); // Get a certificate from key vault. // For security, this certificate is not hard coded in the test case, since it gets added as app credential in Azure AD, and may not get removed. AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(Constants.AzureCliConnectionString); KeyVaultHelper keyVaultHelper = new KeyVaultHelper(new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback))); string certAsString = await keyVaultHelper.ExportCertificateAsBlob(testCertUrl).ConfigureAwait(false); X509Certificate2 cert = new X509Certificate2(Convert.FromBase64String(certAsString), string.Empty); // Create an application GraphHelper graphHelper = new GraphHelper(_tenantId); Application app = await graphHelper.CreateApplicationAsync(cert).ConfigureAwait(false); // Get token using service principal, this will cache the cert AppAuthenticationResult authResult = null; int count = 5; string thumbprint = cert.Thumbprint?.ToLower(); // Construct connection string using client id and cert info string connectionString = null; switch (certIdentifierType) { case CertIdentifierType.SubjectName: case CertIdentifierType.Thumbprint: string thumbprintOrSubjectName = (certIdentifierType == CertIdentifierType.Thumbprint) ? $"CertificateThumbprint={thumbprint}" : $"CertificateSubjectName={cert.Subject}"; connectionString = $"RunAs=App;AppId={app.AppId};TenantId={_tenantId};{thumbprintOrSubjectName};CertificateStoreLocation={Constants.CurrentUserStore};"; break; case CertIdentifierType.KeyVaultCertificateSecretIdentifier: connectionString = $"RunAs=App;AppId={app.AppId};TenantId={_tenantId};CertificateKeyVaultCertificateSecretIdentifier={testCertUrl};"; break; } AzureServiceTokenProvider astp = new AzureServiceTokenProvider(connectionString); if (certIdentifierType == CertIdentifierType.SubjectName || certIdentifierType == CertIdentifierType.Thumbprint) { // Import the certificate CertUtil.ImportCertificate(cert); } while (authResult == null && count > 0) { try { await astp.GetAccessTokenAsync(Constants.KeyVaultResourceId); authResult = await astp.GetAuthenticationResultAsync(Constants.SqlAzureResourceId, _tenantId); } catch { // It takes time for Azure AD to realize a new application has been added. await Task.Delay(15000); count--; } } if (certIdentifierType == CertIdentifierType.SubjectName || certIdentifierType == CertIdentifierType.Thumbprint) { // Delete the cert CertUtil.DeleteCertificate(cert.Thumbprint); var deletedCert = CertUtil.GetCertificate(cert.Thumbprint); Assert.Null(deletedCert); } // Get token again using a cert which is deleted, but in the cache await astp.GetAccessTokenAsync(Constants.SqlAzureResourceId, _tenantId); // Delete the application and service principal await graphHelper.DeleteApplicationAsync(app); Validator.ValidateToken(authResult.AccessToken, astp.PrincipalUsed, Constants.AppType, _tenantId, app.AppId, cert.Thumbprint, expiresOn: authResult.ExpiresOn); }