Exemple #1
0
        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);
        }
Exemple #2
0
        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)
            {
            }
        }
Exemple #3
0
 public Startup(IConfiguration configuration)
 {
     _configuration         = configuration;
     _apiClientOptions      = _configuration.GetSection("ApiClient").Get <ApiClientOptions>();
     _identityClientOptions = _configuration.GetSection("IdentityClient").Get <IdentityClientOptions>();
     _openIdConnectOptions  = _configuration.GetSection("OpenIdConnect").Get <OpenIdConnectOptions>();
 }
Exemple #4
0
        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);
        }
Exemple #5
0
 public IdentityClient(
     HttpClient client,
     IHttpContextAccessor accessor,
     IOptions <IdentityClientOptions> identityOptions
     ) : base(client, accessor)
 {
     _identityOptions = identityOptions.Value;
 }
Exemple #6
0
 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)));
            });
        }
Exemple #9
0
        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());
        }
Exemple #11
0
        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);
        }
Exemple #12
0
        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));
        }
Exemple #13
0
        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));
        }
Exemple #14
0
        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);
                }
        }
Exemple #15
0
        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);
        }
Exemple #16
0
        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);
                }
        }
Exemple #17
0
 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())
 {
 }
Exemple #20
0
        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);
        }