From d49ca7d06bcf387c94f8539a9f6f4975027ca0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=ADdia=20Tarcza?= <100163235+diatrcz@users.noreply.github.com> Date: Thu, 4 Jun 2026 14:40:56 +0200 Subject: [PATCH 1/2] feat(auth): add support for new service version in VPC Instant Auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lídia Tarcza <100163235+diatrcz@users.noreply.github.com> --- .../sdk/core/security/Authenticator.java | 1 + .../security/VpcInstanceAuthenticator.java | 104 +++++++++++++++++- .../VpcInstanceAuthenticatorTest.java | 67 +++++++++++ 3 files changed, 166 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/ibm/cloud/sdk/core/security/Authenticator.java b/src/main/java/com/ibm/cloud/sdk/core/security/Authenticator.java index 68cfa2f2..9add2632 100644 --- a/src/main/java/com/ibm/cloud/sdk/core/security/Authenticator.java +++ b/src/main/java/com/ibm/cloud/sdk/core/security/Authenticator.java @@ -60,6 +60,7 @@ public interface Authenticator { String PROPNAME_IAM_PROFILE_ID = "IAM_PROFILE_ID"; String PROPNAME_IAM_PROFILE_NAME = "IAM_PROFILE_NAME"; String PROPNAME_IAM_ACCOUNT_ID = "IAM_ACCOUNT_ID"; + String PROPNAME_VPC_IMS_VERSION = "VPC_IMS_VERSION"; String PROPNAME_SCOPE_COLLECTION_TYPE = "SCOPE_COLLECTION_TYPE"; String PROPNAME_SCOPE_ID = "SCOPE_ID"; String PROPNAME_INCLUDE_BUILTIN_ACTIONS = "INCLUDE_BUILTIN_ACTIONS"; diff --git a/src/main/java/com/ibm/cloud/sdk/core/security/VpcInstanceAuthenticator.java b/src/main/java/com/ibm/cloud/sdk/core/security/VpcInstanceAuthenticator.java index 59e27852..6d21fb00 100644 --- a/src/main/java/com/ibm/cloud/sdk/core/security/VpcInstanceAuthenticator.java +++ b/src/main/java/com/ibm/cloud/sdk/core/security/VpcInstanceAuthenticator.java @@ -50,6 +50,8 @@ public class VpcInstanceAuthenticator private String iamProfileCrn; private String iamProfileId; private String url; + private String serviceVersion; + private int tokenLifetime; /** @@ -59,6 +61,8 @@ public static class Builder { private String iamProfileCrn; private String iamProfileId; private String url; + private String serviceVersion; + private int tokenLifetime; // Default ctor. public Builder() { @@ -69,6 +73,8 @@ private Builder(VpcInstanceAuthenticator obj) { this.iamProfileCrn = obj.iamProfileCrn; this.iamProfileId = obj.iamProfileId; this.url = obj.url; + this.serviceVersion = obj.serviceVersion; + this.tokenLifetime = obj.tokenLifetime; } /** @@ -121,6 +127,27 @@ public Builder url(String url) { this.url = url; return this; } + + /** + * Sets the serviceVersion Property. + * + * @param serviceVersion the base service version to use with the service. + * @return the Builder + */ + public Builder serviceVersion(String serviceVersion) { + this.serviceVersion = serviceVersion; + return this; + } + + /** + * Sets the tokenLifetime Property. + * @param tokenLifetime the base token lifetime to use. + * @return the Builder + */ + public Builder tokenLifetime(int tokenLifetime) { + this.tokenLifetime = tokenLifetime; + return this; + } } // The default ctor is hidden to force the use of the non-default ctors. @@ -139,6 +166,8 @@ protected VpcInstanceAuthenticator(Builder builder) { this.iamProfileCrn = builder.iamProfileCrn; this.iamProfileId = builder.iamProfileId; this.url = builder.url; + this.serviceVersion = StringUtils.isEmpty(builder.serviceVersion) ? metadataServiceVersion : builder.serviceVersion; + this.tokenLifetime = builder.tokenLifetime == 0 ? instanceIdentityTokenLifetime : builder.tokenLifetime; this.validate(); } @@ -161,7 +190,8 @@ public Builder newBuilder() { */ public static VpcInstanceAuthenticator fromConfiguration(Map config) { return new Builder().iamProfileCrn(config.get(PROPNAME_IAM_PROFILE_CRN)) - .iamProfileId(config.get(PROPNAME_IAM_PROFILE_ID)).url(config.get(PROPNAME_URL)).build(); + .iamProfileId(config.get(PROPNAME_IAM_PROFILE_ID)).url(config.get(PROPNAME_URL)) + .serviceVersion(config.get(PROPNAME_VPC_IMS_VERSION)).build(); } /** @@ -236,10 +266,71 @@ protected void setURL(String url) { this.url = url; } + /** + * @return the VPC ServiceVersion configured in this Authenticator. + */ + public String getServiceVersion() { + return this.serviceVersion; + } + + /** + * Sets the ServiceVersion in this Authenticator. + * + * @return the VPC ServiceVersion + */ + protected void setServiceVersion(String serviceVersion) { + if (StringUtils.isEmpty(serviceVersion)) { + serviceVersion = metadataServiceVersion; + } + this.serviceVersion = serviceVersion; + } + + /** + * @return the TokenLifetime configured on this Authenticator. + */ + public int getTokenLifetime() { + return this.tokenLifetime; + } + + /** + * Sets the TokenLifetime in this Authenticator. + * @param tokenLifetime + */ + protected void setTokenLifetime(int tokenLifetime) { + if (tokenLifetime == 0) { + tokenLifetime = instanceIdentityTokenLifetime; + } + this.tokenLifetime = tokenLifetime; + } + private String getImsEndpoint() { return (StringUtils.isEmpty(this.url) ? defaultIMSEndpoint : this.url); } + /** + * Gets the operation path for creating an access token based on the service version. + * + * @return the correct access token path + */ + public String getCreateAccessTokenPath() { + if (this.serviceVersion.equals("2025-08-26")) { + return "/identity/v1/token"; + } + return operationPathCreateAccessToken; + } + + /** + * Gets the operation path for creating an IAM token based on the service version. + * + * @return the correct IAM token path + */ + public String getCreateIamTokenPath() { + if (this.serviceVersion.equals("2025-08-26")) { + return "/identity/v1/iam_tokens"; + } + return operationPathCreateIamToken; + } + /** * Fetches an IAM access token using the authenticator's configuration. * @@ -271,15 +362,15 @@ public String retrieveInstanceIdentityToken() throws Throwable { try { // Create a PUT request to retrieve the instance identity token. RequestBuilder builder = RequestBuilder - .put(RequestBuilder.resolveRequestUrl(getImsEndpoint(), operationPathCreateAccessToken)); + .put(RequestBuilder.resolveRequestUrl(getImsEndpoint(), this.getCreateAccessTokenPath())); // Set the params and request body. - builder.query("version", metadataServiceVersion); + builder.query("version", this.getServiceVersion()); builder.header(HttpHeaders.ACCEPT, HttpMediaType.APPLICATION_JSON); builder.header(HttpHeaders.CONTENT_TYPE, HttpMediaType.APPLICATION_JSON); builder.header("Metadata-Flavor", metadataFlavor); - String requestBody = String.format("{\"expires_in\": %d}", instanceIdentityTokenLifetime); + String requestBody = String.format("{\"expires_in\": %d}", this.getTokenLifetime()); builder.bodyContent(requestBody, HttpMediaType.APPLICATION_JSON); // Invoke the VPC IMDS "create_access_token" operation. @@ -306,10 +397,10 @@ public IamToken retrieveIamAccessToken(String instanceIdentityToken) { try { // Create a POST request to retrieve the IAM access token. RequestBuilder builder = - RequestBuilder.post(RequestBuilder.resolveRequestUrl(getImsEndpoint(), operationPathCreateIamToken)); + RequestBuilder.post(RequestBuilder.resolveRequestUrl(getImsEndpoint(), this.getCreateIamTokenPath())); // Set the params and request body. - builder.query("version", metadataServiceVersion); + builder.query("version", this.getServiceVersion()); builder.header(HttpHeaders.ACCEPT, HttpMediaType.APPLICATION_JSON); builder.header(HttpHeaders.CONTENT_TYPE, HttpMediaType.APPLICATION_JSON); builder.header(HttpHeaders.AUTHORIZATION, "Bearer " + instanceIdentityToken); @@ -347,3 +438,4 @@ public IamToken retrieveIamAccessToken(String instanceIdentityToken) { return iamToken; } } + diff --git a/src/test/java/com/ibm/cloud/sdk/core/test/security/VpcInstanceAuthenticatorTest.java b/src/test/java/com/ibm/cloud/sdk/core/test/security/VpcInstanceAuthenticatorTest.java index 55fa4794..00238555 100644 --- a/src/test/java/com/ibm/cloud/sdk/core/test/security/VpcInstanceAuthenticatorTest.java +++ b/src/test/java/com/ibm/cloud/sdk/core/test/security/VpcInstanceAuthenticatorTest.java @@ -623,4 +623,71 @@ public void testAuthenticateResponseError2() throws Throwable { fail("Expected RuntimeException, not " + t.getClass().getSimpleName()); } } + + @Test + public void testVpcAuthServiceVersionDefaults() { + VpcInstanceAuthenticator authenticator = new VpcInstanceAuthenticator.Builder() + .build(); + assertNotNull(authenticator); + + assertEquals(authenticator.getServiceVersion(), "2022-03-01"); + assertEquals(authenticator.getTokenLifetime(), 300); + + assertEquals("/instance_identity/v1/token", authenticator.getCreateAccessTokenPath()); + assertEquals("/instance_identity/v1/iam_token", authenticator.getCreateIamTokenPath()); + } + + @Test + public void testVpcAuthServiceVersionBuilder() { + VpcInstanceAuthenticator authenticator = new VpcInstanceAuthenticator.Builder() + .serviceVersion("2025-08-26") + .tokenLifetime(600) + .build(); + assertNotNull(authenticator); + + assertEquals(authenticator.getServiceVersion(), "2025-08-26"); + assertEquals(authenticator.getTokenLifetime(), 600); + + assertEquals("/identity/v1/token", authenticator.getCreateAccessTokenPath()); + assertEquals("/identity/v1/iam_tokens", authenticator.getCreateIamTokenPath()); + } + + @Test + public void testVpcAuthServiceVersionFromMap() { + Map properties = new HashMap<>(); + properties.put(Authenticator.PROPNAME_VPC_IMS_VERSION, "2025-08-26"); + + VpcInstanceAuthenticator authenticator = VpcInstanceAuthenticator.fromConfiguration(properties); + assertNotNull(authenticator); + + assertEquals(authenticator.getServiceVersion(), "2025-08-26"); + + assertEquals("/identity/v1/token", authenticator.getCreateAccessTokenPath()); + assertEquals("/identity/v1/iam_tokens", authenticator.getCreateIamTokenPath()); + } + + @Test + public void testVpcAuthServiceVersionOldVersion() { + VpcInstanceAuthenticator authenticator = new VpcInstanceAuthenticator.Builder() + .serviceVersion("2022-03-01") + .build(); + assertNotNull(authenticator); + + assertEquals(authenticator.getServiceVersion(), "2022-03-01"); + + assertEquals("/instance_identity/v1/token", authenticator.getCreateAccessTokenPath()); + assertEquals("/instance_identity/v1/iam_token", authenticator.getCreateIamTokenPath()); + } + + @Test + public void testVpcAuthServiceVersionCustomVersion() { + VpcInstanceAuthenticator authenticator = new VpcInstanceAuthenticator.Builder() + .serviceVersion("2024-01-01") + .build(); + assertNotNull(authenticator); + + assertEquals(authenticator.getServiceVersion(), "2024-01-01"); + assertEquals("/instance_identity/v1/token", authenticator.getCreateAccessTokenPath()); + assertEquals("/instance_identity/v1/iam_token", authenticator.getCreateIamTokenPath()); + } } From 0d0bedfa87297eca47e52f21f89cf9f535652e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=ADdia=20Tarcza?= <100163235+diatrcz@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:45:02 +0200 Subject: [PATCH 2/2] docs: add documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lídia Tarcza <100163235+diatrcz@users.noreply.github.com> --- Authentication.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Authentication.md b/Authentication.md index ea2df1dd..1240bf9a 100644 --- a/Authentication.md +++ b/Authentication.md @@ -464,6 +464,13 @@ The IAM access token is added to each outbound request in the `Authorization` he The default value of this property is `http://169.254.169.254`. However, if the VPC Instance Metadata Service is configured with the HTTP Secure Protocol setting (`https`), then you should configure this property to be `https://api.metadata.cloud.ibm.com`. +- serviceVersion: (optional) The VPC Instance Metadata Service version to use. +The default value is `2022-03-01`. When set to `2025-08-26`, the authenticator will use the new API paths +(`/identity/v1/token` and `/identity/v1/iam_tokens`) instead of the legacy paths. + +- tokenLifetime: (optional) The lifetime (in seconds) of the instance identity token. +The default value is `300` seconds. This property can only be configured programmatically (not via environment variables). + Usage Notes: 1. At most one of `iamProfileCrn` or `iamProfileId` may be specified. The specified value must map to a trusted IAM profile that has been linked to the compute resource (virtual server instance). @@ -491,12 +498,27 @@ ExampleService service = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, // 'service' can now be used to invoke operations. ``` +To use the new service version with custom token lifetime: +```java +// Create the authenticator with new service version. +VpcInstanceAuthenticator authenticator = new VpcInstanceAuthenticator.Builder() + .serviceVersion("2025-08-26") + .tokenLifetime(600) + .build(); +``` + ### Configuration example External configuration: ``` export EXAMPLE_SERVICE_AUTH_TYPE=vpc export EXAMPLE_SERVICE_IAM_PROFILE_CRN=crn:iam-profile-123 ``` +To use the new service version: +``` +export EXAMPLE_SERVICE_AUTH_TYPE=vpc +export EXAMPLE_SERVICE_IAM_PROFILE_CRN=crn:iam-profile-123 +export EXAMPLE_SERVICE_VPC_IMS_VERSION=2025-08-26 +``` Application code: ```java import .ExampleService.v1.ExampleService;