From f9216e0e9c8881a6d50c84b20010213f658ec38c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:47:40 +0000 Subject: [PATCH 01/13] Initial plan From c094cd31ca86f361ffc3c9a31e2b8ed9d15b6ccf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:52:57 +0000 Subject: [PATCH 02/13] Add bearer token authentication support with azure.keyvault.access-token property Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../azure-security-keyvault-jca/README.md | 30 +++++++++++++ .../keyvault/jca/KeyVaultKeyStore.java | 7 +-- .../jca/KeyVaultLoadStoreParameter.java | 36 ++++++++++++++-- .../jca/implementation/KeyVaultClient.java | 43 ++++++++++++++++--- .../certificates/KeyVaultCertificates.java | 27 +++++++++++- .../jca/implementation/model/AccessToken.java | 17 ++++++++ 6 files changed, 146 insertions(+), 14 deletions(-) diff --git a/sdk/keyvault/azure-security-keyvault-jca/README.md b/sdk/keyvault/azure-security-keyvault-jca/README.md index b9dfaa71856c..1794f6badff5 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/README.md +++ b/sdk/keyvault/azure-security-keyvault-jca/README.md @@ -84,6 +84,7 @@ The JCA library supports configuring the following options: * `azure.keyvault.client-id`: The client/application ID used for authentication. * `azure.keyvault.client-secret`: The client secret for authentication when using client credentials. * `azure.keyvault.managed-identity`: Indicates whether Managed Identity authentication is enabled. +* `azure.keyvault.access-token`: The access token for authentication. This allows using a pre-obtained bearer token instead of client credentials. * `azure.cert-path.well-known`: The path where the well-known certificate is stored. * `azure.cert-path.custom`: The path where the custom certificate is stored. * `azure.keyvault.jca.refresh-certificates-when-have-un-trust-certificate`: Indicates whether to refresh certificates when have untrusted certificate. @@ -91,6 +92,12 @@ The JCA library supports configuring the following options: * `azure.keyvault.jca.certificates-refresh-interval-in-ms`: The refresh interval time. * `azure.keyvault.disable-challenge-resource-verification`: Indicates whether to disable verification that the authentication challenge resource matches the Key Vault or Managed HSM domain. +**Authentication Priority:** +When multiple authentication methods are configured, they are used in the following priority order: +1. Managed Identity (`azure.keyvault.managed-identity`) +2. Access Token (`azure.keyvault.access-token`) +3. Client Credentials (`azure.keyvault.client-id` and `azure.keyvault.client-secret`) + You can configure these properties using: ```java System.setProperty("azure.keyvault.uri", ""); @@ -189,6 +196,29 @@ System.out.println(result); Note if you want to use Azure managed identity, you should set the value of `azure.keyvault.uri`, and the rest of the parameters would be `null`. +#### Authentication with Access Token +If you want to use a pre-obtained bearer token for authentication (e.g., for multi-factor authentication scenarios), you can use the `azure.keyvault.access-token` property: + +```java +// First, obtain your access token through your authentication flow +String accessToken = ""; + +System.setProperty("azure.keyvault.uri", ""); +System.setProperty("azure.keyvault.access-token", accessToken); + +KeyVaultJcaProvider provider = new KeyVaultJcaProvider(); +Security.addProvider(provider); + +KeyStore keyStore = KeyVaultKeyStore.getKeyVaultKeyStoreBySystemProperty(); + +// Use the keyStore as needed for your SSL/TLS operations +``` + +This approach allows you to programmatically implement multi-factor authentication by: +1. Authenticating your service principal with a certificate (something you have) +2. Requesting a temporary access token +3. Using that temporary access token (something you know) for Key Vault operations + ### mTLS #### Server side mTLS If you are looking to integrate the JCA provider to create an SSLServerSocket see the example below. diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java index 4a9fd191562a..ee2d5c23cf7c 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultKeyStore.java @@ -127,6 +127,7 @@ public KeyVaultKeyStore() { String clientId = System.getProperty("azure.keyvault.client-id"); String clientSecret = System.getProperty("azure.keyvault.client-secret"); String managedIdentity = System.getProperty("azure.keyvault.managed-identity"); + String accessToken = System.getProperty("azure.keyvault.access-token"); boolean disableChallengeResourceVerification = Boolean.parseBoolean(System.getProperty("azure.keyvault.disable-challenge-resource-verification")); long refreshInterval = getRefreshInterval(); @@ -146,7 +147,7 @@ public KeyVaultKeyStore() { LOGGER.log(FINE, String.format("Loaded custom certificates: %s.", customCertificates.getAliases())); keyVaultCertificates = new KeyVaultCertificates(refreshInterval, keyVaultUri, tenantId, clientId, clientSecret, - managedIdentity, disableChallengeResourceVerification); + managedIdentity, accessToken, disableChallengeResourceVerification); LOGGER.log(FINE, String.format("Loaded Key Vault certificates: %s.", keyVaultCertificates.getAliases())); classpathCertificates = new ClasspathCertificates(); @@ -184,7 +185,7 @@ public static KeyStore getKeyVaultKeyStoreBySystemProperty() KeyVaultLoadStoreParameter keyVaultLoadStoreParameter = new KeyVaultLoadStoreParameter( System.getProperty("azure.keyvault.uri"), System.getProperty("azure.keyvault.tenant-id"), System.getProperty("azure.keyvault.client-id"), System.getProperty("azure.keyvault.client-secret"), - System.getProperty("azure.keyvault.managed-identity")); + System.getProperty("azure.keyvault.managed-identity"), System.getProperty("azure.keyvault.access-token")); if (Boolean.parseBoolean(System.getProperty("azure.keyvault.disable-challenge-resource-verification"))) { keyVaultLoadStoreParameter.disableChallengeResourceVerification(); @@ -399,7 +400,7 @@ public void engineLoad(KeyStore.LoadStoreParameter param) { keyVaultCertificates.updateKeyVaultClient(parameter.getUri(), parameter.getTenantId(), parameter.getClientId(), parameter.getClientSecret(), parameter.getManagedIdentity(), - parameter.isChallengeResourceVerificationDisabled()); + parameter.getAccessToken(), parameter.isChallengeResourceVerificationDisabled()); } classpathCertificates.loadCertificatesFromClasspath(); diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java index 5a7f8dbe9762..1b75896e2a22 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java @@ -36,6 +36,11 @@ public final class KeyVaultLoadStoreParameter implements KeyStore.LoadStoreParam */ private final String managedIdentity; + /** + * Stores the access token. + */ + private final String accessToken; + /** * Stores a flag indicating if challenge resource verification shall be disabled. */ @@ -47,7 +52,7 @@ public final class KeyVaultLoadStoreParameter implements KeyStore.LoadStoreParam * @param keyVaultUri The Azure Key Vault URI. */ public KeyVaultLoadStoreParameter(String keyVaultUri) { - this(keyVaultUri, null, null, null, null); + this(keyVaultUri, null, null, null, null, null); } /** @@ -57,7 +62,7 @@ public KeyVaultLoadStoreParameter(String keyVaultUri) { * @param managedIdentity The managed identity. */ public KeyVaultLoadStoreParameter(String keyVaultUri, String managedIdentity) { - this(keyVaultUri, null, null, null, managedIdentity); + this(keyVaultUri, null, null, null, managedIdentity, null); } /** @@ -69,7 +74,7 @@ public KeyVaultLoadStoreParameter(String keyVaultUri, String managedIdentity) { * @param clientSecret The client secret. */ public KeyVaultLoadStoreParameter(String keyVaultUri, String tenantId, String clientId, String clientSecret) { - this(keyVaultUri, tenantId, clientId, clientSecret, null); + this(keyVaultUri, tenantId, clientId, clientSecret, null, null); } /** @@ -83,12 +88,28 @@ public KeyVaultLoadStoreParameter(String keyVaultUri, String tenantId, String cl */ public KeyVaultLoadStoreParameter(String keyVaultUri, String tenantId, String clientId, String clientSecret, String managedIdentity) { + this(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, null); + } + + /** + * Constructor. + * + * @param keyVaultUri The Azure Key Vault URI. + * @param tenantId The tenant id. + * @param clientId The client id. + * @param clientSecret The client secret. + * @param managedIdentity The managed identity. + * @param accessToken The access token. + */ + public KeyVaultLoadStoreParameter(String keyVaultUri, String tenantId, String clientId, String clientSecret, + String managedIdentity, String accessToken) { this.keyVaultUri = keyVaultUri; this.tenantId = tenantId; this.clientId = clientId; this.clientSecret = clientSecret; this.managedIdentity = managedIdentity; + this.accessToken = accessToken; } /** @@ -128,6 +149,15 @@ public String getManagedIdentity() { return managedIdentity; } + /** + * Get the access token. + * + * @return The access token. + */ + public String getAccessToken() { + return accessToken; + } + /** * Get the tenant id. * diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java index af460d746385..197266a767cc 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java @@ -89,6 +89,11 @@ public class KeyVaultClient { */ private String managedIdentity; + /** + * Stores the provided access token. + */ + private String providedAccessToken; + /** * Stores the token. */ @@ -106,7 +111,7 @@ public class KeyVaultClient { * @param managedIdentity The user-assigned managed identity object ID. */ KeyVaultClient(String keyVaultUri, String managedIdentity) { - this(keyVaultUri, null, null, null, managedIdentity, false); + this(keyVaultUri, null, null, null, managedIdentity, null, false); } /** @@ -118,7 +123,7 @@ public class KeyVaultClient { * @param clientSecret The client secret. */ public KeyVaultClient(String keyVaultUri, String tenantId, String clientId, String clientSecret) { - this(keyVaultUri, tenantId, clientId, clientSecret, null, false); + this(keyVaultUri, tenantId, clientId, clientSecret, null, null, false); } /** @@ -133,6 +138,22 @@ public KeyVaultClient(String keyVaultUri, String tenantId, String clientId, Stri */ public KeyVaultClient(String keyVaultUri, String tenantId, String clientId, String clientSecret, String managedIdentity, boolean disableChallengeResourceVerification) { + this(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, null, disableChallengeResourceVerification); + } + + /** + * Constructor. + * + * @param keyVaultUri The Azure Key Vault URI. + * @param tenantId The tenant ID. + * @param clientId The client ID. + * @param clientSecret The client secret. + * @param managedIdentity The user-assigned managed identity object ID. + * @param providedAccessToken The access token for authentication. + * @param disableChallengeResourceVerification Indicates if the challenge resource verification should be disabled. + */ + public KeyVaultClient(String keyVaultUri, String tenantId, String clientId, String clientSecret, + String managedIdentity, String providedAccessToken, boolean disableChallengeResourceVerification) { LOGGER.log(INFO, "Using Azure Key Vault: {0}", keyVaultUri); @@ -147,6 +168,7 @@ public KeyVaultClient(String keyVaultUri, String tenantId, String clientId, Stri this.clientId = clientId; this.clientSecret = clientSecret; this.managedIdentity = managedIdentity; + this.providedAccessToken = providedAccessToken; this.disableChallengeResourceVerification = disableChallengeResourceVerification; } @@ -156,10 +178,11 @@ public static KeyVaultClient createKeyVaultClientBySystemProperty() { String clientId = System.getProperty("azure.keyvault.client-id"); String clientSecret = System.getProperty("azure.keyvault.client-secret"); String managedIdentity = System.getProperty("azure.keyvault.managed-identity"); + String accessToken = System.getProperty("azure.keyvault.access-token"); boolean disableChallengeResourceVerification = Boolean.parseBoolean(System.getProperty("azure.keyvault.disable-challenge-resource-verification")); - return new KeyVaultClient(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, + return new KeyVaultClient(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, accessToken, disableChallengeResourceVerification); } @@ -199,13 +222,21 @@ private AccessToken getAccessTokenByHttpRequest() { managedIdentity = URLEncoder.encode(managedIdentity, "UTF-8"); } - if (tenantId != null && clientId != null && clientSecret != null) { + // Priority: 1. Managed Identity, 2. Provided Access Token, 3. Client ID/Secret + if (managedIdentity != null) { + LOGGER.info("Using managed identity for authentication"); + accessToken = AccessTokenUtil.getAccessToken(resource, managedIdentity); + } else if (providedAccessToken != null) { + LOGGER.info("Using provided access token for authentication"); + // Create an AccessToken object from the provided token string + // We set an expiration far in the future since we don't know the actual expiration + accessToken = new AccessToken(providedAccessToken, Long.MAX_VALUE); + } else if (tenantId != null && clientId != null && clientSecret != null) { + LOGGER.info("Using client credentials (client ID/secret) for authentication"); String aadAuthenticationUri = getLoginUri(keyVaultUri + "certificates" + API_VERSION_POSTFIX, disableChallengeResourceVerification); accessToken = AccessTokenUtil.getAccessToken(resource, aadAuthenticationUri, tenantId, clientId, clientSecret); - } else { - accessToken = AccessTokenUtil.getAccessToken(resource, managedIdentity); } } catch (UnsupportedEncodingException e) { LOGGER.log(WARNING, "Could not obtain access token to authenticate with.", e); diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java index f9075d6eebe1..6684b474acac 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java @@ -51,10 +51,16 @@ public final class KeyVaultCertificates implements AzureCertificates { public KeyVaultCertificates(long refreshInterval, String keyVaultUri, String tenantId, String clientId, String clientSecret, String managedIdentity, boolean disableChallengeResourceVerification) { + this(refreshInterval, keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, null, + disableChallengeResourceVerification); + } + + public KeyVaultCertificates(long refreshInterval, String keyVaultUri, String tenantId, String clientId, + String clientSecret, String managedIdentity, String accessToken, boolean disableChallengeResourceVerification) { this.refreshInterval = refreshInterval; - updateKeyVaultClient(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, + updateKeyVaultClient(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, accessToken, disableChallengeResourceVerification); } @@ -75,10 +81,27 @@ public KeyVaultCertificates(long refreshInterval, KeyVaultClient keyVaultClient) */ public void updateKeyVaultClient(String keyVaultUri, String tenantId, String clientId, String clientSecret, String managedIdentity, boolean disableChallengeResourceVerification) { + updateKeyVaultClient(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, null, + disableChallengeResourceVerification); + } + + /** + * Update KeyVaultClient. + * + * @param keyVaultUri Key Vault URI. + * @param tenantId Tenant ID. + * @param clientId Client ID. + * @param clientSecret Client secret. + * @param managedIdentity Managed identity. + * @param accessToken Access token. + * @param disableChallengeResourceVerification Indicates if the challenge resource verification should be disabled. + */ + public void updateKeyVaultClient(String keyVaultUri, String tenantId, String clientId, String clientSecret, + String managedIdentity, String accessToken, boolean disableChallengeResourceVerification) { if (keyVaultUri != null) { keyVaultClient = new KeyVaultClient(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, - disableChallengeResourceVerification); + accessToken, disableChallengeResourceVerification); } else { keyVaultClient = null; } diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/AccessToken.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/AccessToken.java index 5fdd65669d98..f05676679bd0 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/AccessToken.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/model/AccessToken.java @@ -24,6 +24,23 @@ public class AccessToken implements JsonSerializable { */ private long expiresIn; + /** + * Default constructor. + */ + public AccessToken() { + } + + /** + * Constructor with access token and expiration. + * + * @param accessToken The access token. + * @param expiresIn The life duration of the access token in seconds. + */ + public AccessToken(String accessToken, long expiresIn) { + this.accessToken = accessToken; + this.expiresIn = expiresIn; + } + /** * Get the life duration of the access token in seconds. * From 8312b07eb6e1b946c8c15b8bba49b328aeb949e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:56:43 +0000 Subject: [PATCH 03/13] Add tests for bearer token authentication and fix checkstyle issues Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../jca/implementation/KeyVaultClient.java | 5 +- .../implementation/KeyVaultClientTest.java | 66 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java index 197266a767cc..87f11fb6cb07 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java @@ -92,7 +92,7 @@ public class KeyVaultClient { /** * Stores the provided access token. */ - private String providedAccessToken; + private final String providedAccessToken; /** * Stores the token. @@ -138,7 +138,8 @@ public KeyVaultClient(String keyVaultUri, String tenantId, String clientId, Stri */ public KeyVaultClient(String keyVaultUri, String tenantId, String clientId, String clientSecret, String managedIdentity, boolean disableChallengeResourceVerification) { - this(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, null, disableChallengeResourceVerification); + this(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, null, + disableChallengeResourceVerification); } /** diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java index a294686b164f..a87ec74c0238 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java @@ -205,4 +205,70 @@ public void testCacheTokenExpired() { tokenUtilMockedStatic.verify(() -> AccessTokenUtil.getAccessToken(anyString(), anyString()), times(2)); } } + + @Test + public void testAccessTokenAuthentication() { + try (MockedStatic httpUtilMockedStatic = Mockito.mockStatic(HttpUtil.class)) { + httpUtilMockedStatic.when(() -> HttpUtil.validateUri(anyString(), anyString())).thenCallRealMethod(); + httpUtilMockedStatic.when(() -> HttpUtil.addTrailingSlashIfRequired(anyString())).thenCallRealMethod(); + + CertificateItem fakeCertificateItem = new CertificateItem(); + fakeCertificateItem.setId("certificates/fakeCertificateItem"); + + CertificateListResult certificateListResult = new CertificateListResult(); + certificateListResult.setValue(Arrays.asList(fakeCertificateItem)); + + String certificateListResultString = JsonConverterUtil.toJson(certificateListResult); + httpUtilMockedStatic.when(() -> HttpUtil.get(anyString(), anyMap())) + .thenReturn(certificateListResultString); + + // Create client with access token + String testAccessToken = "test-bearer-token-12345"; + KeyVaultClient keyVaultClient + = new KeyVaultClient(KEY_VAULT_TEST_URI_GLOBAL, null, null, null, null, testAccessToken, false); + + List result = keyVaultClient.getAliases(); + + // Verify that the access token was used + assertEquals(1, result.size()); + assertTrue(result.contains("fakeCertificateItem")); + } + } + + @Test + public void testAuthenticationPriority() { + try (MockedStatic httpUtilMockedStatic = Mockito.mockStatic(HttpUtil.class); + MockedStatic tokenUtilMockedStatic = Mockito.mockStatic(AccessTokenUtil.class)) { + + httpUtilMockedStatic.when(() -> HttpUtil.validateUri(anyString(), anyString())).thenCallRealMethod(); + httpUtilMockedStatic.when(() -> HttpUtil.addTrailingSlashIfRequired(anyString())).thenCallRealMethod(); + + AccessToken accessToken = new AccessToken("fake-token", 3600); + tokenUtilMockedStatic.when(() -> AccessTokenUtil.getAccessToken(anyString(), anyString())) + .thenReturn(accessToken); + + CertificateItem fakeCertificateItem = new CertificateItem(); + fakeCertificateItem.setId("certificates/fakeCertificateItem"); + + CertificateListResult certificateListResult = new CertificateListResult(); + certificateListResult.setValue(Arrays.asList(fakeCertificateItem)); + + String certificateListResultString = JsonConverterUtil.toJson(certificateListResult); + httpUtilMockedStatic.when(() -> HttpUtil.get(anyString(), anyMap())) + .thenReturn(certificateListResultString); + + // Test 1: Managed Identity should take priority over access token + KeyVaultClient client1 + = new KeyVaultClient(KEY_VAULT_TEST_URI_GLOBAL, null, null, null, "managed-id", "bearer-token", false); + client1.getAliases(); + tokenUtilMockedStatic.verify(() -> AccessTokenUtil.getAccessToken(anyString(), eq("managed-id")), times(1)); + + // Test 2: Access token should be used when managed identity is not set + KeyVaultClient client2 + = new KeyVaultClient(KEY_VAULT_TEST_URI_GLOBAL, null, null, null, null, "bearer-token", false); + List result = client2.getAliases(); + assertEquals(1, result.size()); + assertTrue(result.contains("fakeCertificateItem")); + } + } } From c7440f67e9df473a127b10047118601e72288c28 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:57:30 +0000 Subject: [PATCH 04/13] Update CHANGELOG.md with bearer token authentication feature Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md b/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md index c406c10f73fb..57ba0c7bd1dd 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md +++ b/sdk/keyvault/azure-security-keyvault-jca/CHANGELOG.md @@ -3,6 +3,7 @@ ## 2.11.0-beta.1 (Unreleased) ### Features Added +- Added support for bearer token authentication via the `azure.keyvault.access-token` system property. This allows users to provide a pre-obtained access token for authentication, enabling multi-factor authentication scenarios without requiring client ID and client secret. Authentication priority order is: Managed Identity > Access Token > Client Credentials. ### Breaking Changes From 5caa34247d3f8469b528de31e250f141f6cdf2bb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 07:01:32 +0000 Subject: [PATCH 05/13] Address code review feedback: fix token expiration, add validation, improve docs Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- sdk/keyvault/azure-security-keyvault-jca/README.md | 13 ++++++++----- .../keyvault/jca/KeyVaultLoadStoreParameter.java | 11 +++++++++++ .../keyvault/jca/implementation/KeyVaultClient.java | 7 ++++--- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/sdk/keyvault/azure-security-keyvault-jca/README.md b/sdk/keyvault/azure-security-keyvault-jca/README.md index 1794f6badff5..1d97bce1e2c5 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/README.md +++ b/sdk/keyvault/azure-security-keyvault-jca/README.md @@ -197,10 +197,11 @@ System.out.println(result); Note if you want to use Azure managed identity, you should set the value of `azure.keyvault.uri`, and the rest of the parameters would be `null`. #### Authentication with Access Token -If you want to use a pre-obtained bearer token for authentication (e.g., for multi-factor authentication scenarios), you can use the `azure.keyvault.access-token` property: +If you want to use a pre-obtained bearer token for authentication, you can use the `azure.keyvault.access-token` property: ```java // First, obtain your access token through your authentication flow +// For example, authenticate with a certificate or other credential String accessToken = ""; System.setProperty("azure.keyvault.uri", ""); @@ -214,10 +215,12 @@ KeyStore keyStore = KeyVaultKeyStore.getKeyVaultKeyStoreBySystemProperty(); // Use the keyStore as needed for your SSL/TLS operations ``` -This approach allows you to programmatically implement multi-factor authentication by: -1. Authenticating your service principal with a certificate (something you have) -2. Requesting a temporary access token -3. Using that temporary access token (something you know) for Key Vault operations +This approach is useful in scenarios where: +- You need to use a specific authentication flow (e.g., certificate-based authentication) to obtain a token +- You want to manage token lifecycle and refresh independently +- You are working in environments where managed identity or client credentials are not suitable + +**Note:** The provided access token is cached and used for approximately 1 hour. After that time, you will need to provide a fresh token through your application's token refresh mechanism. ### mTLS #### Server side mTLS diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java index 1b75896e2a22..076c967bd38b 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java @@ -65,6 +65,17 @@ public KeyVaultLoadStoreParameter(String keyVaultUri, String managedIdentity) { this(keyVaultUri, null, null, null, managedIdentity, null); } + /** + * Constructor for access token authentication. + * + * @param keyVaultUri The Azure Key Vault URI. + * @param accessToken The access token. + * @param useAccessToken Marker parameter to differentiate from managedIdentity constructor (pass true). + */ + public KeyVaultLoadStoreParameter(String keyVaultUri, String accessToken, boolean useAccessToken) { + this(keyVaultUri, null, null, null, null, accessToken); + } + /** * Constructor. * diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java index 87f11fb6cb07..101d45685203 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java @@ -227,11 +227,12 @@ private AccessToken getAccessTokenByHttpRequest() { if (managedIdentity != null) { LOGGER.info("Using managed identity for authentication"); accessToken = AccessTokenUtil.getAccessToken(resource, managedIdentity); - } else if (providedAccessToken != null) { + } else if (providedAccessToken != null && !providedAccessToken.isEmpty()) { LOGGER.info("Using provided access token for authentication"); // Create an AccessToken object from the provided token string - // We set an expiration far in the future since we don't know the actual expiration - accessToken = new AccessToken(providedAccessToken, Long.MAX_VALUE); + // Set expiration to 1 hour (3600 seconds) as a reasonable default since we don't know the actual expiration + // The token will be treated as expired after 1 hour and the caller will need to provide a new one + accessToken = new AccessToken(providedAccessToken, 3600); } else if (tenantId != null && clientId != null && clientSecret != null) { LOGGER.info("Using client credentials (client ID/secret) for authentication"); String aadAuthenticationUri = getLoginUri(keyVaultUri + "certificates" + API_VERSION_POSTFIX, From b3ebe9ff96644dfd86978184ea3bcb28fbb4c8e6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 07:56:40 +0000 Subject: [PATCH 06/13] Remove unused KeyVaultLoadStoreParameter constructor with boolean parameter Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../keyvault/jca/KeyVaultLoadStoreParameter.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java index 076c967bd38b..1b75896e2a22 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/KeyVaultLoadStoreParameter.java @@ -65,17 +65,6 @@ public KeyVaultLoadStoreParameter(String keyVaultUri, String managedIdentity) { this(keyVaultUri, null, null, null, managedIdentity, null); } - /** - * Constructor for access token authentication. - * - * @param keyVaultUri The Azure Key Vault URI. - * @param accessToken The access token. - * @param useAccessToken Marker parameter to differentiate from managedIdentity constructor (pass true). - */ - public KeyVaultLoadStoreParameter(String keyVaultUri, String accessToken, boolean useAccessToken) { - this(keyVaultUri, null, null, null, null, accessToken); - } - /** * Constructor. * From 6cf79dbee2c337f45e3135f1194274ffea1e6699 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 08:01:31 +0000 Subject: [PATCH 07/13] Remove Authentication with Access Token section from README per review feedback Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../azure-security-keyvault-jca/README.md | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/sdk/keyvault/azure-security-keyvault-jca/README.md b/sdk/keyvault/azure-security-keyvault-jca/README.md index 1d97bce1e2c5..ffc06d246ba4 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/README.md +++ b/sdk/keyvault/azure-security-keyvault-jca/README.md @@ -196,32 +196,6 @@ System.out.println(result); Note if you want to use Azure managed identity, you should set the value of `azure.keyvault.uri`, and the rest of the parameters would be `null`. -#### Authentication with Access Token -If you want to use a pre-obtained bearer token for authentication, you can use the `azure.keyvault.access-token` property: - -```java -// First, obtain your access token through your authentication flow -// For example, authenticate with a certificate or other credential -String accessToken = ""; - -System.setProperty("azure.keyvault.uri", ""); -System.setProperty("azure.keyvault.access-token", accessToken); - -KeyVaultJcaProvider provider = new KeyVaultJcaProvider(); -Security.addProvider(provider); - -KeyStore keyStore = KeyVaultKeyStore.getKeyVaultKeyStoreBySystemProperty(); - -// Use the keyStore as needed for your SSL/TLS operations -``` - -This approach is useful in scenarios where: -- You need to use a specific authentication flow (e.g., certificate-based authentication) to obtain a token -- You want to manage token lifecycle and refresh independently -- You are working in environments where managed identity or client credentials are not suitable - -**Note:** The provided access token is cached and used for approximately 1 hour. After that time, you will need to provide a fresh token through your application's token refresh mechanism. - ### mTLS #### Server side mTLS If you are looking to integrate the JCA provider to create an SSLServerSocket see the example below. From a878e9d9e72e72b21b9eb80eb1c1bee53d78795e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 08:12:10 +0000 Subject: [PATCH 08/13] Remove Authentication Priority section from README and simplify variable naming Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- sdk/keyvault/azure-security-keyvault-jca/README.md | 6 ------ .../jca/implementation/KeyVaultClient.java | 14 +++++++------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/sdk/keyvault/azure-security-keyvault-jca/README.md b/sdk/keyvault/azure-security-keyvault-jca/README.md index ffc06d246ba4..90dbff7311f1 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/README.md +++ b/sdk/keyvault/azure-security-keyvault-jca/README.md @@ -92,12 +92,6 @@ The JCA library supports configuring the following options: * `azure.keyvault.jca.certificates-refresh-interval-in-ms`: The refresh interval time. * `azure.keyvault.disable-challenge-resource-verification`: Indicates whether to disable verification that the authentication challenge resource matches the Key Vault or Managed HSM domain. -**Authentication Priority:** -When multiple authentication methods are configured, they are used in the following priority order: -1. Managed Identity (`azure.keyvault.managed-identity`) -2. Access Token (`azure.keyvault.access-token`) -3. Client Credentials (`azure.keyvault.client-id` and `azure.keyvault.client-secret`) - You can configure these properties using: ```java System.setProperty("azure.keyvault.uri", ""); diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java index 101d45685203..0b532d6514cd 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java @@ -90,7 +90,7 @@ public class KeyVaultClient { private String managedIdentity; /** - * Stores the provided access token. + * Stores the provided access token string. */ private final String providedAccessToken; @@ -214,7 +214,7 @@ private String getAccessToken() { private AccessToken getAccessTokenByHttpRequest() { LOGGER.entering("KeyVaultClient", "getAccessTokenByHttpRequest"); - AccessToken accessToken = null; + AccessToken result = null; try { String resource = URLEncoder.encode(keyVaultBaseUri, "UTF-8"); @@ -226,27 +226,27 @@ private AccessToken getAccessTokenByHttpRequest() { // Priority: 1. Managed Identity, 2. Provided Access Token, 3. Client ID/Secret if (managedIdentity != null) { LOGGER.info("Using managed identity for authentication"); - accessToken = AccessTokenUtil.getAccessToken(resource, managedIdentity); + result = AccessTokenUtil.getAccessToken(resource, managedIdentity); } else if (providedAccessToken != null && !providedAccessToken.isEmpty()) { LOGGER.info("Using provided access token for authentication"); // Create an AccessToken object from the provided token string // Set expiration to 1 hour (3600 seconds) as a reasonable default since we don't know the actual expiration // The token will be treated as expired after 1 hour and the caller will need to provide a new one - accessToken = new AccessToken(providedAccessToken, 3600); + result = new AccessToken(providedAccessToken, 3600); } else if (tenantId != null && clientId != null && clientSecret != null) { LOGGER.info("Using client credentials (client ID/secret) for authentication"); String aadAuthenticationUri = getLoginUri(keyVaultUri + "certificates" + API_VERSION_POSTFIX, disableChallengeResourceVerification); - accessToken + result = AccessTokenUtil.getAccessToken(resource, aadAuthenticationUri, tenantId, clientId, clientSecret); } } catch (UnsupportedEncodingException e) { LOGGER.log(WARNING, "Could not obtain access token to authenticate with.", e); } - LOGGER.exiting("KeyVaultClient", "getAccessTokenByHttpRequest", accessToken); + LOGGER.exiting("KeyVaultClient", "getAccessTokenByHttpRequest", result); - return accessToken; + return result; } /** From 627da8b6792b8f9b7e56bab620a2cbe5d9705d3c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 08:16:56 +0000 Subject: [PATCH 09/13] Set provided token expiration to effectively infinite to prevent failed refresh attempts Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../keyvault/jca/implementation/KeyVaultClient.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java index 0b532d6514cd..b942ed15ceb0 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java @@ -230,9 +230,10 @@ private AccessToken getAccessTokenByHttpRequest() { } else if (providedAccessToken != null && !providedAccessToken.isEmpty()) { LOGGER.info("Using provided access token for authentication"); // Create an AccessToken object from the provided token string - // Set expiration to 1 hour (3600 seconds) as a reasonable default since we don't know the actual expiration - // The token will be treated as expired after 1 hour and the caller will need to provide a new one - result = new AccessToken(providedAccessToken, 3600); + // Set expiration to a very large value since we cannot refresh provided tokens. + // When the token actually expires, Azure will return authentication errors, + // which will inform the user to provide a new token. + result = new AccessToken(providedAccessToken, Long.MAX_VALUE / 1000); } else if (tenantId != null && clientId != null && clientSecret != null) { LOGGER.info("Using client credentials (client ID/secret) for authentication"); String aadAuthenticationUri = getLoginUri(keyVaultUri + "certificates" + API_VERSION_POSTFIX, From 6079106d609a3e20a31f34ae6badc40ffb938cc9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 08:27:45 +0000 Subject: [PATCH 10/13] Rename getAccessTokenByHttpRequest to obtainAccessToken for clarity Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../keyvault/jca/implementation/KeyVaultClient.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java index b942ed15ceb0..045c69c52b68 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/KeyVaultClient.java @@ -197,7 +197,7 @@ private String getAccessToken() { return accessToken.getAccessToken(); } - accessToken = getAccessTokenByHttpRequest(); + accessToken = obtainAccessToken(); if (accessToken == null) { LOGGER.log(WARNING, "Access token not returned."); return null; @@ -207,12 +207,12 @@ private String getAccessToken() { } /** - * Get the access token. + * Obtain the access token. * * @return The access token. */ - private AccessToken getAccessTokenByHttpRequest() { - LOGGER.entering("KeyVaultClient", "getAccessTokenByHttpRequest"); + private AccessToken obtainAccessToken() { + LOGGER.entering("KeyVaultClient", "obtainAccessToken"); AccessToken result = null; @@ -245,7 +245,7 @@ private AccessToken getAccessTokenByHttpRequest() { LOGGER.log(WARNING, "Could not obtain access token to authenticate with.", e); } - LOGGER.exiting("KeyVaultClient", "getAccessTokenByHttpRequest", result); + LOGGER.exiting("KeyVaultClient", "obtainAccessToken", result); return result; } From 923724dbe428478720d659f3b39bb2453dc52fa5 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Thu, 29 Jan 2026 17:04:33 +0800 Subject: [PATCH 11/13] Update unit test --- .../keyvault/jca/KeyVaultClientTest.java | 47 ------------ .../implementation/KeyVaultClientTest.java | 73 +++++++++---------- 2 files changed, 35 insertions(+), 85 deletions(-) delete mode 100644 sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultClientTest.java diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultClientTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultClientTest.java deleted file mode 100644 index 81f927739527..000000000000 --- a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/KeyVaultClientTest.java +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.security.keyvault.jca; - -import com.azure.security.keyvault.jca.implementation.KeyVaultClient; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; - -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@EnabledIfEnvironmentVariable(named = "AZURE_KEYVAULT_CERTIFICATE_NAME", matches = "myalias") -public class KeyVaultClientTest { - private static KeyVaultClient keyVaultClient; - private static String certificateName; - - @BeforeAll - public static void setEnvironmentProperty() { - keyVaultClient = new KeyVaultClient(PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_ENDPOINT"), - PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_TENANT_ID"), - PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_CLIENT_ID"), - PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_CLIENT_SECRET")); - certificateName = PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_CERTIFICATE_NAME"); - } - - @Test - public void testGetAliases() { - assertTrue(keyVaultClient.getAliases().contains(certificateName)); - } - - @Test - public void testGetCertificate() { - assertNotNull(keyVaultClient.getCertificate(certificateName)); - } - - @Test - public void testGetCertificateChain() { - assertNotNull(keyVaultClient.getCertificateChain(certificateName)); - } - - @Test - public void testGetKey() { - assertNotNull(keyVaultClient.getKey(certificateName, null)); - } -} diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java index a87ec74c0238..15777de637db 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java @@ -3,24 +3,23 @@ package com.azure.security.keyvault.jca.implementation; +import com.azure.security.keyvault.jca.PropertyConvertorUtils; import com.azure.security.keyvault.jca.implementation.model.AccessToken; import com.azure.security.keyvault.jca.implementation.model.CertificateItem; import com.azure.security.keyvault.jca.implementation.model.CertificateListResult; import com.azure.security.keyvault.jca.implementation.utils.AccessTokenUtil; import com.azure.security.keyvault.jca.implementation.utils.HttpUtil; import com.azure.security.keyvault.jca.implementation.utils.JsonConverterUtil; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; import org.mockito.MockedStatic; import org.mockito.Mockito; -import java.security.cert.Certificate; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyString; @@ -40,7 +39,7 @@ public void testGetAliasWithCertificateInfoWith0Page() { KeyVaultClient keyVaultClient = mock(KeyVaultClient.class); List result = keyVaultClient.getAliases(); - assertEquals(result.size(), 0); + assertEquals(0, result.size()); } } @@ -64,7 +63,7 @@ public void testGetAliasWithCertificateInfoWith1Page() { KeyVaultClient keyVaultClient = new KeyVaultClient(KEY_VAULT_TEST_URI_GLOBAL, null); List result = keyVaultClient.getAliases(); - assertEquals(result.size(), 1); + assertEquals(1, result.size()); assertTrue(result.contains("fakeCertificateItem1")); } } @@ -104,44 +103,12 @@ public void testGetAliasWithCertificateInfoWith2Pages() { KeyVaultClient keyVaultClient = new KeyVaultClient(KEY_VAULT_TEST_URI_GLOBAL, null); List result = keyVaultClient.getAliases(); - assertEquals(result.size(), 3); + assertEquals(3, result.size()); assertTrue(result .containsAll(Arrays.asList("fakeCertificateItem1", "fakeCertificateItem2", "fakeCertificateItem3"))); } } - @Test - @Disabled - public void testGetAliases() { - List result = getKeyVaultClient().getAliases(); - assertNotNull(result); - } - - @Test - @Disabled - public void testGetCertificate() { - Certificate certificate = getKeyVaultClient().getCertificate("myalias"); - assertNotNull(certificate); - } - - @Test - @Disabled - public void testGetKey() { - assertNull(getKeyVaultClient().getKey("myalias", null)); - } - - private KeyVaultClient getKeyVaultClient() { - String keyVaultUri = System.getProperty("azure.keyvault.uri"); - String tenantId = System.getProperty("azure.keyvault.tenant-id"); - String clientId = System.getProperty("azure.keyvault.client-id"); - String clientSecret = System.getProperty("azure.keyvault.client-secret"); - boolean disableChallengeResourceVerification - = Boolean.parseBoolean(System.getProperty("azure.keyvault.disable-challenge-resource-verification")); - - return new KeyVaultClient(keyVaultUri, tenantId, clientId, clientSecret, null, - disableChallengeResourceVerification); - } - @Test public void testCacheToken() { try (MockedStatic tokenUtilMockedStatic = Mockito.mockStatic(AccessTokenUtil.class); @@ -271,4 +238,34 @@ public void testAuthenticationPriority() { assertTrue(result.contains("fakeCertificateItem")); } } + + @EnabledIfEnvironmentVariable(named = "AZURE_KEYVAULT_CERTIFICATE_NAME", matches = "myalias") + @Test + public void testKeuVaultClients() { + String accessToken = PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_ACCESS_TOKEN"); + KeyVaultClient keyVaultClient; + if (accessToken != null && !accessToken.isEmpty()) { + keyVaultClient = new KeyVaultClient( + PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_ENDPOINT"), + null, + null, + null, + null, + accessToken, + false); + + } else { + keyVaultClient = new KeyVaultClient( + PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_ENDPOINT"), + PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_TENANT_ID"), + PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_CLIENT_ID"), + PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_CLIENT_SECRET")); + } + String certificateName = PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_CERTIFICATE_NAME"); + + assertTrue(keyVaultClient.getAliases().contains(certificateName)); + assertNotNull(keyVaultClient.getCertificate(certificateName)); + assertNotNull(keyVaultClient.getCertificateChain(certificateName)); + assertNotNull(keyVaultClient.getKey(certificateName, null)); + } } From f7c689b51465cec4dec18fb83ac4bf6c253b5174 Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Fri, 30 Jan 2026 17:01:20 +0800 Subject: [PATCH 12/13] Fix typo --- .../keyvault/jca/implementation/KeyVaultClientTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java index 15777de637db..2bc9d7ea174a 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/test/java/com/azure/security/keyvault/jca/implementation/KeyVaultClientTest.java @@ -241,7 +241,7 @@ public void testAuthenticationPriority() { @EnabledIfEnvironmentVariable(named = "AZURE_KEYVAULT_CERTIFICATE_NAME", matches = "myalias") @Test - public void testKeuVaultClients() { + public void testKeyVaultClients() { String accessToken = PropertyConvertorUtils.getPropertyValue("AZURE_KEYVAULT_ACCESS_TOKEN"); KeyVaultClient keyVaultClient; if (accessToken != null && !accessToken.isEmpty()) { From 1e63095a99e9fca4011866831968cae38fbf5f6a Mon Sep 17 00:00:00 2001 From: Rujun Chen Date: Fri, 30 Jan 2026 17:06:12 +0800 Subject: [PATCH 13/13] Remove unused method in 'implementation' package --- .../certificates/KeyVaultCertificates.java | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java index 6684b474acac..f4636abb2e0b 100644 --- a/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java +++ b/sdk/keyvault/azure-security-keyvault-jca/src/main/java/com/azure/security/keyvault/jca/implementation/certificates/KeyVaultCertificates.java @@ -49,12 +49,6 @@ public final class KeyVaultCertificates implements AzureCertificates { private final long refreshInterval; - public KeyVaultCertificates(long refreshInterval, String keyVaultUri, String tenantId, String clientId, - String clientSecret, String managedIdentity, boolean disableChallengeResourceVerification) { - this(refreshInterval, keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, null, - disableChallengeResourceVerification); - } - public KeyVaultCertificates(long refreshInterval, String keyVaultUri, String tenantId, String clientId, String clientSecret, String managedIdentity, String accessToken, boolean disableChallengeResourceVerification) { @@ -69,22 +63,6 @@ public KeyVaultCertificates(long refreshInterval, KeyVaultClient keyVaultClient) this.keyVaultClient = keyVaultClient; } - /** - * Update KeyVaultClient. - * - * @param keyVaultUri Key Vault URI. - * @param tenantId Tenant ID. - * @param clientId Client ID. - * @param clientSecret Client secret. - * @param managedIdentity Managed identity. - * @param disableChallengeResourceVerification Indicates if the challenge resource verification should be disabled. - */ - public void updateKeyVaultClient(String keyVaultUri, String tenantId, String clientId, String clientSecret, - String managedIdentity, boolean disableChallengeResourceVerification) { - updateKeyVaultClient(keyVaultUri, tenantId, clientId, clientSecret, managedIdentity, null, - disableChallengeResourceVerification); - } - /** * Update KeyVaultClient. *