public async Task VerifyMSIRequest() { var pingResponse = new MockResponse(400); var response = new MockResponse(200); var expectedToken = "mock-msi-access-token"; response.SetContent($"{{ \"access_token\": \"{expectedToken}\", \"expires_on\": 3600 }}"); var mockTransport = new MockTransport(pingResponse, response); var options = new IdentityClientOptions() { Transport = mockTransport }; var credential = new ManagedIdentityCredential(options: options); AccessToken actualToken = await credential.GetTokenAsync(MockScopes.Default); Assert.AreEqual(expectedToken, actualToken.Token); MockRequest request = mockTransport.Requests[1]; string query = request.UriBuilder.Query; Assert.IsTrue(query.Contains("api-version=2018-02-01")); Assert.IsTrue(query.Contains($"resource={Uri.EscapeDataString(ScopeUtilities.ScopesToResource(MockScopes.Default))}")); Assert.IsTrue(request.Headers.TryGetValue("Metadata", out string metadataValue)); Assert.AreEqual("true", metadataValue); }
public async Task AuthenticateWithDeviceCodeMockVerifyCallbackCancellationAsync() { var expectedCode = Guid.NewGuid().ToString(); var expectedToken = Guid.NewGuid().ToString(); var mockTransport = new MockTransport(request => ProcessMockRequest(request, expectedCode, expectedToken)); var options = new IdentityClientOptions() { Transport = mockTransport }; var cancelSource = new CancellationTokenSource(1000); DeviceCodeCredential cred = InstrumentClient(new DeviceCodeCredential(ClientId, VerifyDeviceCodeCallbackCancellationToken, options)); Task <AccessToken> getTokenTask = cred.GetTokenAsync(new string[] { "https://vault.azure.net/.default" }, cancelSource.Token); try { AccessToken token = await getTokenTask; Assert.Fail(); } catch (TaskCanceledException) { } }
public Startup(IConfiguration configuration) { _configuration = configuration; _apiClientOptions = _configuration.GetSection("ApiClient").Get <ApiClientOptions>(); _identityClientOptions = _configuration.GetSection("IdentityClient").Get <IdentityClientOptions>(); _openIdConnectOptions = _configuration.GetSection("OpenIdConnect").Get <OpenIdConnectOptions>(); }
public async Task AuthenticateWithAuthCodeMockAsync() { var expectedToken = Guid.NewGuid().ToString(); var authCode = Guid.NewGuid().ToString(); var clientId = Guid.NewGuid().ToString(); var tenantId = Guid.NewGuid().ToString(); var clientSecret = Guid.NewGuid().ToString(); MockResponse response = CreateAuthorizationResponse(expectedToken); var mockTransport = new MockTransport(request => ProcessMockRequest(request, tenantId, expectedToken)); var options = new IdentityClientOptions() { Transport = mockTransport }; AuthorizationCodeCredential cred = InstrumentClient(new AuthorizationCodeCredential(tenantId, clientId, clientSecret, authCode, options)); AccessToken token = await cred.GetTokenAsync(new TokenRequest(new string[] { "https://vault.azure.net/.default" })); Assert.AreEqual(token.Token, expectedToken); AccessToken token2 = await cred.GetTokenAsync(new TokenRequest(new string[] { "https://managemnt.azure.com/.default" })); Assert.AreEqual(token.Token, expectedToken); }
public IdentityClient( HttpClient client, IHttpContextAccessor accessor, IOptions <IdentityClientOptions> identityOptions ) : base(client, accessor) { _identityOptions = identityOptions.Value; }
public static void AddApiClients( this IServiceCollection services, IdentityClientOptions identityClientOptions, ClientOptions clientOptions) { services.AddHttpClient(); services.AddIdentityClient(identityClientOptions); services.AddPlayerClient(clientOptions); services.AddTransient <AuthenticatingHandler>(); }
public async Task VerifyClientCertificateRequestAsync() { var response = new MockResponse(200); var expectedToken = "mock-msi-access-token"; response.SetContent($"{{ \"access_token\": \"{expectedToken}\", \"expires_in\": 3600 }}"); var mockTransport = new MockTransport(response); var options = new IdentityClientOptions() { Transport = mockTransport }; var expectedTenantId = Guid.NewGuid().ToString(); var expectedClientId = Guid.NewGuid().ToString(); var mockCert = new X509Certificate2("./Data/cert.pfx", "password"); var credential = new ClientCertificateCredential(expectedTenantId, expectedClientId, mockCert, options: options); AccessToken actualToken = await credential.GetTokenAsync(MockScopes.Default); Assert.AreEqual(expectedToken, actualToken.Token); MockRequest request = mockTransport.SingleRequest; Assert.IsTrue(request.Content.TryComputeLength(out long contentLen)); var content = new byte[contentLen]; await request.Content.WriteToAsync(new MemoryStream(content), default); Assert.IsTrue(TryParseFormEncodedBody(content, out Dictionary <string, string> parsedBody)); Assert.IsTrue(parsedBody.TryGetValue("response_type", out string responseType) && responseType == "token"); Assert.IsTrue(parsedBody.TryGetValue("grant_type", out string grantType) && grantType == "client_credentials"); Assert.IsTrue(parsedBody.TryGetValue("client_assertion_type", out string assertionType) && assertionType == "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); Assert.IsTrue(parsedBody.TryGetValue("client_id", out string actualClientId) && actualClientId == expectedClientId); Assert.IsTrue(parsedBody.TryGetValue("scope", out string actualScope) && actualScope == MockScopes.Default.ToString()); Assert.IsTrue(parsedBody.TryGetValue("client_assertion", out string clientAssertion)); // var header VerifyClientAssertion(clientAssertion, expectedTenantId, expectedClientId, mockCert); }
public AuthenticatingHandler(IAuthenticationService authenticationService, IdentityClientOptions clientOptions, ILogger <AuthenticatingHandler> logger) { _authenticationService = authenticationService; _logger = logger; // Create a policy that tries to renew the access token if a 401 Unauthorized is received. _policy = Policy.HandleResult <HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.Unauthorized) .WaitAndRetryAsync(5, retryAttempt => { _logger.LogError($"Retrying connection after 401"); Authenticate(true); return(TimeSpan.FromSeconds(Math.Min(Math.Pow(2, retryAttempt), clientOptions.MaxRetryDelaySeconds))); }); }
public async Task VerifyCloudShellMsiRequestWithClientIdMockAsync() { using (new TestEnvVar("MSI_ENDPOINT", "https://mock.msi.endpoint/")) using (new TestEnvVar("MSI_SECRET", null)) { var response = new MockResponse(200); var expectedToken = "mock-msi-access-token"; response.SetContent($"{{ \"access_token\": \"{expectedToken}\", \"expires_on\": {(DateTimeOffset.UtcNow + TimeSpan.FromSeconds(3600)).ToUnixTimeSeconds()} }}"); var mockTransport = new MockTransport(response); var options = new IdentityClientOptions() { Transport = mockTransport }; ManagedIdentityClient client = InstrumentClient(new ManagedIdentityClient(options)); AccessToken actualToken = await client.AuthenticateAsync(MockScopes.Default, "mock-client-id"); Assert.AreEqual(expectedToken, actualToken.Token); MockRequest request = mockTransport.Requests[0]; Assert.IsTrue(request.UriBuilder.ToString().StartsWith("https://mock.msi.endpoint/")); Assert.IsTrue(request.Content.TryComputeLength(out long contentLen)); var content = new byte[contentLen]; MemoryStream contentBuff = new MemoryStream(content); request.Content.WriteTo(contentBuff, default); string body = Encoding.UTF8.GetString(content); Assert.IsTrue(body.Contains($"resource={Uri.EscapeDataString(ScopeUtilities.ScopesToResource(MockScopes.Default))}")); Assert.IsTrue(body.Contains($"client_id=mock-client-id")); Assert.IsTrue(request.Headers.TryGetValue("Metadata", out string actMetadata)); Assert.AreEqual("true", actMetadata); } }
public async Task VerifyClientClientSecretRequestAsync() { var response = new MockResponse(200); var expectedToken = "mock-msi-access-token"; response.SetContent($"{{ \"access_token\": \"{expectedToken}\", \"expires_in\": 3600 }}"); var mockTransport = new MockTransport(response); var options = new IdentityClientOptions() { Transport = mockTransport }; var expectedTenantId = Guid.NewGuid().ToString(); var expectedClientId = Guid.NewGuid().ToString(); var expectedClientSecret = "secret"; var client = InstrumentClient(new AadIdentityClient(options: options)); AccessToken actualToken = await client.AuthenticateAsync(expectedTenantId, expectedClientId, expectedClientSecret, MockScopes.Default); Assert.AreEqual(expectedToken, actualToken.Token); MockRequest request = mockTransport.SingleRequest; Assert.IsTrue(request.Content.TryComputeLength(out long contentLen)); var content = new byte[contentLen]; await request.Content.WriteToAsync(new MemoryStream(content), default); Assert.IsTrue(TryParseFormEncodedBody(content, out Dictionary <string, string> parsedBody)); Assert.IsTrue(parsedBody.TryGetValue("response_type", out string responseType) && responseType == "token"); Assert.IsTrue(parsedBody.TryGetValue("grant_type", out string grantType) && grantType == "client_credentials"); Assert.IsTrue(parsedBody.TryGetValue("client_id", out string actualClientId) && actualClientId == expectedClientId); Assert.IsTrue(parsedBody.TryGetValue("client_secret", out string actualClientSecret) && actualClientSecret == "secret"); Assert.IsTrue(parsedBody.TryGetValue("scope", out string actualScope) && actualScope == MockScopes.Default.ToString()); }
public async Task AuthenticateWithDeviceCodeMockAsync2() { var expectedCode = Guid.NewGuid().ToString(); var expectedToken = Guid.NewGuid().ToString(); var mockTransport = new MockTransport(request => ProcessMockRequest(request, expectedCode, expectedToken)); var options = new IdentityClientOptions() { Transport = mockTransport }; DeviceCodeCredential cred = InstrumentClient(new DeviceCodeCredential(ClientId, (code, cancelToken) => VerifyDeviceCode(code, expectedCode), options)); AccessToken token = await cred.GetTokenAsync(new string[] { "https://vault.azure.net/.default" }); Assert.AreEqual(token.Token, expectedToken); }
public void AuthenticateWithDeviceCodeCallbackThrowsAsync() { var expectedCode = Guid.NewGuid().ToString(); var expectedToken = Guid.NewGuid().ToString(); var cancelSource = new CancellationTokenSource(); var mockTransport = new MockTransport(request => ProcessMockRequest(request, expectedCode, expectedToken)); var options = new IdentityClientOptions() { Transport = mockTransport }; DeviceCodeCredential cred = InstrumentClient(new DeviceCodeCredential(ClientId, ThrowingDeviceCodeCallback, options)); Assert.ThrowsAsync <MockException>(async() => await cred.GetTokenAsync(new string[] { "https://vault.azure.net/.default" }, cancelSource.Token)); }
public void AuthenticateWithDeviceCodeMockVerifyMsalCancellationAsync() { var expectedCode = Guid.NewGuid().ToString(); var expectedToken = Guid.NewGuid().ToString(); var cancelSource = new CancellationTokenSource(); var mockTransport = new MockTransport(request => ProcessMockRequest(request, expectedCode, expectedToken)); var options = new IdentityClientOptions() { Transport = mockTransport }; var cred = InstrumentClient(new DeviceCodeCredential((code, cancelToken) => VerifyDeviceCodeAndCancel(code, expectedCode, cancelSource), ClientId, options: options)); Assert.ThrowsAsync <OperationCanceledException>(async() => await cred.GetTokenAsync(new TokenRequest(new string[] { "https://vault.azure.net/.default" }), cancelSource.Token)); }
public async Task VerifyAppServiceMsiRequestWithClientIdMockAsync() { using (new TestEnvVar("MSI_ENDPOINT", "https://mock.msi.endpoint/")) using (new TestEnvVar("MSI_SECRET", "mock-msi-secret")) { var response = new MockResponse(200); var expectedToken = "mock-msi-access-token"; response.SetContent($"{{ \"access_token\": \"{expectedToken}\", \"expires_on\": \"{DateTimeOffset.UtcNow.ToString()}\" }}"); var mockTransport = new MockTransport(response); var options = new IdentityClientOptions() { Transport = mockTransport }; ManagedIdentityClient client = InstrumentClient(new ManagedIdentityClient(options)); AccessToken actualToken = await client.AuthenticateAsync(MockScopes.Default, "mock-client-id"); Assert.AreEqual(expectedToken, actualToken.Token); MockRequest request = mockTransport.Requests[0]; Assert.IsTrue(request.UriBuilder.ToString().StartsWith("https://mock.msi.endpoint/")); string query = request.UriBuilder.Query; Assert.IsTrue(query.Contains("api-version=2017-09-01")); Assert.IsTrue(query.Contains($"resource={Uri.EscapeDataString(ScopeUtilities.ScopesToResource(MockScopes.Default))}")); Assert.IsTrue(query.Contains($"client_id=mock-client-id")); Assert.IsTrue(request.Headers.TryGetValue("secret", out string actSecretValue)); Assert.AreEqual("mock-msi-secret", actSecretValue); } }
public async Task RefreshLogicDefaultAsync() { TimeSpan refreshBuffer = new IdentityClientOptions().RefreshBuffer; var refreshCred1 = new MockRefreshAzureCredential(DateTime.UtcNow); var refreshCred2 = new MockRefreshAzureCredential(DateTime.UtcNow + refreshBuffer); var notRefreshCred1 = new MockRefreshAzureCredential(DateTime.UtcNow + refreshBuffer + TimeSpan.FromMinutes(2)); foreach (var cred in new AzureCredential[] { refreshCred1, refreshCred2, notRefreshCred1 }) { await cred.GetTokenAsync(new string[] { "mockscope" }); await cred.GetTokenAsync(new string[] { "mockscope" }); } Assert.AreEqual(2, refreshCred1.AuthCount); Assert.AreEqual(2, refreshCred2.AuthCount); Assert.AreEqual(1, notRefreshCred1.AuthCount); }
public async Task VerifyImdsRequestWithClientIdMockAsync() { using (new TestEnvVar("MSI_ENDPOINT", null)) using (new TestEnvVar("MSI_SECRET", null)) { var response = new MockResponse(200); var expectedToken = "mock-msi-access-token"; response.SetContent($"{{ \"access_token\": \"{expectedToken}\", \"expires_on\": \"3600\" }}"); var mockTransport = new MockTransport(response, response); var options = new IdentityClientOptions() { Transport = mockTransport }; ManagedIdentityClient client = InstrumentClient(new ManagedIdentityClient(options)); AccessToken actualToken = await client.AuthenticateAsync(MockScopes.Default, clientId : "mock-client-id"); Assert.AreEqual(expectedToken, actualToken.Token); MockRequest request = mockTransport.Requests[mockTransport.Requests.Count - 1]; string query = request.UriBuilder.Query; Assert.IsTrue(query.Contains("api-version=2018-02-01")); Assert.IsTrue(query.Contains($"resource={Uri.EscapeDataString(ScopeUtilities.ScopesToResource(MockScopes.Default))}")); Assert.IsTrue(query.Contains($"client_id=mock-client-id")); Assert.IsTrue(request.Headers.TryGetValue("Metadata", out string metadataValue)); Assert.AreEqual("true", metadataValue); } }
private static void AddIdentityClient( this IServiceCollection services, IdentityClientOptions identityClientOptions) { services.AddHttpClient("identity"); }
public IdentityAccessTokenAccessor(IdentityClientOptions options, HttpClient httpClient) { _httpClient = httpClient; _options = options; }
public IdentityAccessTokenAccessor(IdentityClientOptions options) : this(options, new HttpClient()) { }
internal static TokenCredential CreateCredential(IConfiguration configuration, IdentityClientOptions identityClientOptions = null) { var clientId = configuration["clientId"]; var tenantId = configuration["tenantId"]; var clientSecret = configuration["clientSecret"]; var certificate = configuration["clientCertificate"]; var certificateStoreName = configuration["clientCertificateStoreName"]; var certificateStoreLocation = configuration["clientCertificateStoreLocation"]; if (!string.IsNullOrWhiteSpace(tenantId) && !string.IsNullOrWhiteSpace(clientId) && !string.IsNullOrWhiteSpace(clientSecret)) { return(new ClientSecretCredential(tenantId, clientId, clientSecret, identityClientOptions)); } if (!string.IsNullOrWhiteSpace(tenantId) && !string.IsNullOrWhiteSpace(clientId) && !string.IsNullOrWhiteSpace(certificate)) { StoreLocation storeLocation = StoreLocation.CurrentUser; if (!string.IsNullOrWhiteSpace(certificateStoreLocation)) { storeLocation = (StoreLocation)Enum.Parse(typeof(StoreLocation), certificateStoreLocation, true); } if (string.IsNullOrWhiteSpace(certificateStoreName)) { certificateStoreName = "MY"; // MY is the default used in X509Store } using var store = new X509Store(certificateStoreName, storeLocation); store.Open(OpenFlags.ReadOnly); X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindByThumbprint, certificate, false); if (certs.Count == 0) { throw new InvalidOperationException($"Unable to find a certificate with thumbprint '{certificate}'"); } var credential = new ClientCertificateCredential(tenantId, clientId, certs[0], identityClientOptions); store.Close(); return(credential); } // TODO: More logging return(null); }