public async Task FromX509Certificate2() { var tenantId = TestEnvironment.ServicePrincipalTenantId; var clientId = TestEnvironment.ServicePrincipalClientId; var cert = new X509Certificate2(TestEnvironment.ServicePrincipalCertificatePfxPath); var options = InstrumentClientOptions(new TokenCredentialOptions()); var credential = new ClientCertificateCredential(tenantId, clientId, cert, options); var tokenRequestContext = new TokenRequestContext(new[] { AzureAuthorityHosts.GetDefaultScope(AzureAuthorityHosts.AzurePublicCloud) }); // ensure we can initially acquire a token AccessToken token = await credential.GetTokenAsync(tokenRequestContext); Assert.IsNotNull(token.Token); // ensure subsequent calls before the token expires are served from the token cache AccessToken cachedToken = await credential.GetTokenAsync(tokenRequestContext); Assert.AreEqual(token.Token, cachedToken.Token); // ensure new credentials don't share tokens from the cache var credential2 = new ClientCertificateCredential(tenantId, clientId, cert, options); AccessToken token2 = await credential2.GetTokenAsync(tokenRequestContext); // this assert is conditional because the access token is scrubbed in the recording so they will never be different if (Mode != RecordedTestMode.Playback && Mode != RecordedTestMode.None) { Assert.AreNotEqual(token.Token, token2.Token); } }
public async Task VerifyClientCertificateRequestFailedAsync(bool usePemFile) { var response = new MockResponse(400); response.SetContent($"{{ \"error_code\": \"InvalidSecret\", \"message\": \"The specified client_secret is incorrect\" }}"); var mockTransport = new MockTransport(response); var options = new TokenCredentialOptions() { Transport = mockTransport }; var expectedTenantId = Guid.NewGuid().ToString(); var expectedClientId = Guid.NewGuid().ToString(); var certificatePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pfx"); var certificatePathPem = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pem"); var mockCert = new X509Certificate2(certificatePath, "password"); ClientCertificateCredential credential = InstrumentClient( usePemFile ? new ClientCertificateCredential(expectedTenantId, expectedClientId, certificatePathPem, options) : new ClientCertificateCredential(expectedTenantId, expectedClientId, mockCert, options) ); Assert.ThrowsAsync <AuthenticationFailedException>(async() => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default))); await Task.CompletedTask; }
public async Task VerifyClientCertificateRequestAsync(bool usePemFile) { 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 TokenCredentialOptions() { Transport = mockTransport }; var expectedTenantId = Guid.NewGuid().ToString(); var expectedClientId = Guid.NewGuid().ToString(); var certificatePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pfx"); var certificatePathPem = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pem"); var mockCert = new X509Certificate2(certificatePath, "password"); ClientCertificateCredential credential = InstrumentClient( usePemFile ? new ClientCertificateCredential(expectedTenantId, expectedClientId, certificatePathPem, options) : new ClientCertificateCredential(expectedTenantId, expectedClientId, mockCert, options) ); AccessToken actualToken = await credential.GetTokenAsync(new TokenRequestContext(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); }
//MSAL doesn't cache Service Principal into msal.cache public override Task <IAccessToken> Authenticate(AuthenticationParameters parameters, CancellationToken cancellationToken) { var spParameters = parameters as ServicePrincipalParameters; var onPremise = spParameters.Environment.OnPremise; var tenantId = onPremise ? AdfsTenant : (string.Equals(parameters.TenantId, OrganizationsTenant, StringComparison.OrdinalIgnoreCase) ? null : parameters.TenantId); var resource = spParameters.Environment.GetEndpoint(spParameters.ResourceId) ?? spParameters.ResourceId; var scopes = AuthenticationHelpers.GetScope(onPremise, resource); var clientId = spParameters.ApplicationId; var authority = spParameters.Environment.ActiveDirectoryAuthority; var requestContext = new TokenRequestContext(scopes); var options = new ClientCertificateCredentialOptions() { AuthorityHost = new Uri(authority) }; if (!string.IsNullOrEmpty(spParameters.Thumbprint)) { //Service Principal with Certificate ClientCertificateCredential certCredential; if (!ClientCertCredentialMap.TryGetValue(spParameters.ApplicationId, out certCredential)) { //first time login var certificate = AzureSession.Instance.DataStore.GetCertificate(spParameters.Thumbprint); certCredential = new ClientCertificateCredential(tenantId, spParameters.ApplicationId, certificate, options); var tokenTask = certCredential.GetTokenAsync(requestContext, cancellationToken); return(MsalAccessToken.GetAccessTokenAsync(tokenTask, () => { ClientCertCredentialMap[spParameters.ApplicationId] = certCredential; }, spParameters.TenantId, spParameters.ApplicationId)); } else { var tokenTask = certCredential.GetTokenAsync(requestContext, cancellationToken); return(MsalAccessToken.GetAccessTokenAsync(tokenTask, spParameters.TenantId, spParameters.ApplicationId)); } } else if (spParameters.Secret != null) { // service principal with secret var secretCredential = new ClientSecretCredential(tenantId, spParameters.ApplicationId, spParameters.Secret.ConvertToString(), options); var tokenTask = secretCredential.GetTokenAsync(requestContext, cancellationToken); return(MsalAccessToken.GetAccessTokenAsync( tokenTask, spParameters.TenantId, spParameters.ApplicationId)); } else { throw new MsalException(MsalError.AuthenticationFailed, string.Format(AuthenticationFailedMessage, clientId)); } }
public void IncorrectCertificate() { var tenantId = TestEnvironment.ServicePrincipalTenantId; var clientId = TestEnvironment.ServicePrincipalClientId; var certPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pfx"); var options = InstrumentClientOptions(new TokenCredentialOptions()); var credential = new ClientCertificateCredential(tenantId, clientId, new X509Certificate2(certPath), options); var tokenRequestContext = new TokenRequestContext(new[] { AzureAuthorityHosts.GetDefaultScope(AzureAuthorityHosts.AzurePublicCloud) }); // ensure the incorrect client claim is rejected, handled and wrapped in AuthenticationFailedException Assert.ThrowsAsync <AuthenticationFailedException>(async() => await credential.GetTokenAsync(tokenRequestContext)); }
public async Task AuthnenticateWithAssertionCallback(bool useAsyncCallback) { var tenantId = TestEnvironment.ServicePrincipalTenantId; var clientId = TestEnvironment.ServicePrincipalClientId; var cert = new X509Certificate2(TestEnvironment.ServicePrincipalCertificatePfxPath); var options = InstrumentClientOptions(new ClientAssertionCredentialOptions()); ClientAssertionCredential credential; if (useAsyncCallback) { Func <CancellationToken, Task <string> > assertionCallback = (ct) => Task.FromResult(CreateClientAssertionJWT(options.AuthorityHost, clientId, tenantId, cert)); credential = InstrumentClient(new ClientAssertionCredential(tenantId, clientId, assertionCallback, options)); } else { Func <string> assertionCallback = () => CreateClientAssertionJWT(options.AuthorityHost, clientId, tenantId, cert); credential = InstrumentClient(new ClientAssertionCredential(tenantId, clientId, assertionCallback, options)); } var tokenRequestContext = new TokenRequestContext(new[] { AzureAuthorityHosts.GetDefaultScope(new Uri(TestEnvironment.AuthorityHostUrl)) }); // ensure we can initially acquire a token AccessToken token = await credential.GetTokenAsync(tokenRequestContext); Assert.IsNotNull(token.Token); // ensure subsequent calls before the token expires are served from the token cache AccessToken cachedToken = await credential.GetTokenAsync(tokenRequestContext); Assert.AreEqual(token.Token, cachedToken.Token); // ensure new credentials don't share tokens from the cache var credential2 = new ClientCertificateCredential(tenantId, clientId, cert, options); AccessToken token2 = await credential2.GetTokenAsync(tokenRequestContext); // this assert is conditional because the access token is scrubbed in the recording so they will never be different if (Mode != RecordedTestMode.Playback && Mode != RecordedTestMode.None) { Assert.AreNotEqual(token.Token, token2.Token); } }
public async Task IncludeX5CCliamHeader() { var tenantId = TestEnvironment.ServicePrincipalTenantId; var clientId = TestEnvironment.ServicePrincipalClientId; var certPath = TestEnvironment.ServicePrincipalSniCertificatePath; var options = InstrumentClientOptions(new ClientCertificateCredentialOptions { IncludeX5CCliamHeader = true }); var credential = new ClientCertificateCredential(tenantId, clientId, certPath, options); var tokenRequestContext = new TokenRequestContext(new[] { AzureAuthorityHosts.GetDefaultScope(AzureAuthorityHosts.AzurePublicCloud) }); // ensure we can initially acquire a token AccessToken token = await credential.GetTokenAsync(tokenRequestContext); Assert.IsNotNull(token.Token); }
public void VerifyBadCertificateFileBehavior() { var tenantId = Guid.NewGuid().ToString(); var clientId = Guid.NewGuid().ToString(); TokenRequestContext tokenContext = new TokenRequestContext(MockScopes.Default); ClientCertificateCredential missingFileCredential = new ClientCertificateCredential(tenantId, clientId, Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "notfound.pem")); ClientCertificateCredential invalidPemCredential = new ClientCertificateCredential(tenantId, clientId, Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert-invalid-data.pem")); ClientCertificateCredential unknownFormatCredential = new ClientCertificateCredential(tenantId, clientId, Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.unknown")); ClientCertificateCredential encryptedCredential = new ClientCertificateCredential(tenantId, clientId, Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pfx")); Assert.Throws <CredentialUnavailableException>(() => missingFileCredential.GetToken(tokenContext)); Assert.Throws <CredentialUnavailableException>(() => invalidPemCredential.GetToken(tokenContext)); Assert.Throws <CredentialUnavailableException>(() => unknownFormatCredential.GetToken(tokenContext)); Assert.Throws <CredentialUnavailableException>(() => encryptedCredential.GetToken(tokenContext)); Assert.ThrowsAsync <CredentialUnavailableException>(async() => await missingFileCredential.GetTokenAsync(tokenContext)); Assert.ThrowsAsync <CredentialUnavailableException>(async() => await invalidPemCredential.GetTokenAsync(tokenContext)); Assert.ThrowsAsync <CredentialUnavailableException>(async() => await unknownFormatCredential.GetTokenAsync(tokenContext)); Assert.ThrowsAsync <CredentialUnavailableException>(async() => await encryptedCredential.GetTokenAsync(tokenContext)); }
public async Task VerifyClientCertificateCredentialExceptionAsync(bool usePemFile) { string expectedInnerExMessage = Guid.NewGuid().ToString(); var mockAadClient = new MockAadIdentityClient(() => { throw new MockClientException(expectedInnerExMessage); }); var expectedTenantId = Guid.NewGuid().ToString(); var expectedClientId = Guid.NewGuid().ToString(); var certificatePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pfx"); var certificatePathPem = Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "cert.pem"); var mockCert = new X509Certificate2(certificatePath, "password"); ClientCertificateCredential credential = InstrumentClient( usePemFile ? new ClientCertificateCredential(expectedTenantId, expectedClientId, certificatePathPem, CredentialPipeline.GetInstance(null), mockAadClient) : new ClientCertificateCredential(expectedTenantId, expectedClientId, mockCert, CredentialPipeline.GetInstance(null), mockAadClient) ); var ex = Assert.ThrowsAsync <AuthenticationFailedException>(async() => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default))); Assert.IsNotNull(ex.InnerException); Assert.IsInstanceOf(typeof(MockClientException), ex.InnerException); Assert.AreEqual(expectedInnerExMessage, ex.InnerException.Message); await Task.CompletedTask; }
public async override ValueTask <AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken) { return(await _credential.GetTokenAsync(requestContext, cancellationToken)); }