Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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 <sdk_base_package>.ExampleService.v1.ExampleService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public class VpcInstanceAuthenticator
private String iamProfileCrn;
private String iamProfileId;
private String url;
private String serviceVersion;
private int tokenLifetime;


/**
Expand All @@ -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() {
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -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.
Expand All @@ -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();
}

Expand All @@ -161,7 +190,8 @@ public Builder newBuilder() {
*/
public static VpcInstanceAuthenticator fromConfiguration(Map<String, String> 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();
}

/**
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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.
Expand All @@ -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);
Expand Down Expand Up @@ -347,3 +438,4 @@ public IamToken retrieveIamAccessToken(String instanceIdentityToken) {
return iamToken;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> 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());
}
}
Loading