Skip to content

feat(auth): add support for MCSP V2 authentication #236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 30, 2025
Merged
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
8 changes: 4 additions & 4 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# semantic-release is also run to create a new release (if
# warranted by the new commits being built).

name: Build/Test
name: build

on:
push:
Expand All @@ -16,7 +16,7 @@ on:
jobs:
detect-secrets:
if: "!contains(github.event.head_commit.message, '[skip ci]')"
name: Detect-Secrets
name: detect-secrets
runs-on: ubuntu-latest

steps:
Expand All @@ -38,8 +38,8 @@ jobs:
detect-secrets -v audit --report --fail-on-unaudited --fail-on-live --fail-on-audited-real .secrets.baseline

build:
name: build-test (java ${{matrix.java-version}})
needs: detect-secrets
name: Build/Test (Java ${{matrix.java-version}})
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -60,8 +60,8 @@ jobs:
run: mvn -B clean package

create-release:
name: semantic-release
needs: build
name: Semantic-Release
if: "github.ref_name == 'main' && github.event_name != 'pull_request'"
runs-on: ubuntu-latest

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# - building and publishing javadocs to the git repository.
# It is triggered when a new release is created.

name: Publish
name: publish

on:
release:
Expand All @@ -13,7 +13,7 @@ on:

jobs:
publish:
name: Publish Release
name: publish-release
runs-on: ubuntu-latest

steps:
Expand Down
68 changes: 57 additions & 11 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"files": "package-lock.json|^.secrets.baseline$",
"lines": null
},
"generated_at": "2025-01-08T20:47:36Z",
"generated_at": "2025-05-27T21:34:41Z",
"plugins_used": [
{
"name": "AWSKeyDetector"
Expand Down Expand Up @@ -80,55 +80,55 @@
"hashed_secret": "91dfd9ddb4198affc5c194cd8ce6d338fde470e2",
"is_secret": false,
"is_verified": false,
"line_number": 73,
"line_number": 74,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "4f51cde3ac0a5504afa4bc06859b098366592c19",
"is_secret": false,
"is_verified": false,
"line_number": 222,
"line_number": 223,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "e87559ed7decb62d0733ae251ae58d42a55291d8",
"is_secret": false,
"is_verified": false,
"line_number": 224,
"line_number": 225,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "12f4a68ed3d0863e56497c9cdb1e2e4e91d5cb68",
"is_secret": false,
"is_verified": false,
"line_number": 288,
"line_number": 289,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "c837b75d7cd93ef9c2243ca28d6e5156259fd253",
"is_secret": false,
"is_verified": false,
"line_number": 292,
"line_number": 293,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "98635b2eaa2379f28cd6d72a38299f286b81b459",
"is_secret": false,
"is_verified": false,
"line_number": 526,
"line_number": 529,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "47fcf185ee7e15fe05cae31fbe9e4ebe4a06a40d",
"is_secret": false,
"is_verified": false,
"line_number": 629,
"line_number": 729,
"type": "Secret Keyword",
"verified_result": null
}
Expand All @@ -146,23 +146,23 @@
"hashed_secret": "d4c3d66fd0c38547a3c7a4c6bdc29c36911bc030",
"is_secret": false,
"is_verified": false,
"line_number": 47,
"line_number": 48,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "8318df9ecda039deac9868adf1944a29a95c7114",
"is_secret": false,
"is_verified": false,
"line_number": 50,
"line_number": 51,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "9a66213cc16d178fdbf9f4da6b7bd92497fda404",
"is_secret": false,
"is_verified": false,
"line_number": 56,
"line_number": 57,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down Expand Up @@ -245,6 +245,16 @@
"verified_result": null
}
],
"src/test/java/com/ibm/cloud/sdk/core/test/security/MCSPV2AuthenticatorTest.java": [
{
"hashed_secret": "d3576f76725ff2040fe425790fe8b5ccf61994a2",
"is_secret": false,
"is_verified": false,
"line_number": 63,
"type": "Secret Keyword",
"verified_result": null
}
],
"src/test/java/com/ibm/cloud/sdk/core/util/CredentialUtilsTest.java": [
{
"hashed_secret": "1beb7496ebbe82c61151be093956d83dac625c13",
Expand Down Expand Up @@ -301,6 +311,24 @@
"verified_result": null
}
],
"src/test/resources/mcspv2_token.json": [
{
"hashed_secret": "3c1e5e4bcfb74c7ba031f00ee4a1ebe685fbf57a",
"is_secret": false,
"is_verified": false,
"line_number": 2,
"type": "JSON Web Token",
"verified_result": null
},
{
"hashed_secret": "e196c8eb696e0f04be7eb863abbf17a8b2adf993",
"is_secret": false,
"is_verified": false,
"line_number": 2,
"type": "SoftLayer Credentials",
"verified_result": null
}
],
"src/test/resources/my-credentials.env": [
{
"hashed_secret": "edbd5e119f94badb9f99a67ac6ff4c7a5204ad61",
Expand Down Expand Up @@ -389,6 +417,24 @@
"verified_result": null
}
],
"src/test/resources/refreshed_mcspv2_token.json": [
{
"hashed_secret": "d9b5dacb7cabd9bb3369f1cc45e237d9658d5efc",
"is_secret": false,
"is_verified": false,
"line_number": 2,
"type": "JSON Web Token",
"verified_result": null
},
{
"hashed_secret": "e196c8eb696e0f04be7eb863abbf17a8b2adf993",
"is_secret": false,
"is_verified": false,
"line_number": 2,
"type": "SoftLayer Credentials",
"verified_result": null
}
],
"src/test/resources/vcap_services.json": [
{
"hashed_secret": "0ee6f3a69b36c1bcac73c25350a7414a53397ecd",
Expand Down
110 changes: 106 additions & 4 deletions Authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ The java-sdk-core project supports the following types of authentication:
- Container Authentication
- VPC Instance Authentication
- Cloud Pak for Data Authentication
- Multi-Cloud Saas Platform (MCSP) Authentication
- Multi-Cloud Saas Platform (MCSP) V1 Authentication
- Multi-Cloud Saas Platform (MCSP) V2 Authentication
- No Authentication (for testing)

The SDK user configures the appropriate type of authentication for use with service instances.
Expand Down Expand Up @@ -575,11 +576,11 @@ ExampleService service = ExampleService.newInstance("example_service");
```


## Multi-Cloud Saas Platform (MCSP) Authentication
## Multi-Cloud Saas Platform (MCSP) V1 Authentication
The `MCSPAuthenticator` can be used in scenarios where an application needs to
interact with an IBM Cloud service that has been deployed to a non-IBM Cloud environment (e.g. AWS).
It accepts a user-supplied apikey and performs the necessary interactions with the
Multi-Cloud Saas Platform token service to obtain a suitable MCSP access token (a bearer token)
It accepts a user-supplied apikey and invokes the Multi-Cloud Saas Platform token service's
`POST /siusermgr/api/1.0/apikeys/token` operation to obtain a suitable MCSP access token (a bearer token)
for the specified apikey.
The authenticator will also obtain a new bearer token when the current token expires.
The bearer token is then added to each outbound request in the `Authorization` header in the
Expand Down Expand Up @@ -643,6 +644,107 @@ ExampleService service = ExampleService.newInstance("example_service");
```


## Multi-Cloud Saas Platform (MCSP) V2 Authentication
The `MCSPV2Authenticator` can be used in scenarios where an application needs to
interact with an IBM Cloud service that has been deployed to a non-IBM Cloud environment (e.g. AWS).
It accepts a user-supplied apikey and invokes the Multi-Cloud Saas Platform token service's
`POST /api/2.0/{scopeCollectionType}/{scopeId}/apikeys/token` operation to obtain a suitable MCSP access token (a bearer token)
for the specified apikey.
The authenticator will also obtain a new bearer token when the current token expires.
The bearer token is then added to each outbound request in the `Authorization` header in the
form:
```
Authorization: Bearer <bearer-token>
```

### Properties

- apikey: (required) the apikey to be used to obtain an MCSP access token.

- url: (required) The URL representing the MCSP token service endpoint's base URL string. Do not include the
operation path (e.g. `/siusermgr/api/1.0/apikeys/token`) as part of this property's value.

- scopeCollectionType: (required) The scope collection type of item(s).
The valid values are: `accounts`, `subscriptions`, `services`.

- scopeId: (required) The scope identifier of item(s).

- includeBuiltinActions: (optional) A flag to include builtin actions in the `actions` claim in the MCSP token (default: false).

- includeCustomActions: (optional) A flag to include custom actions in the `actions` claim in the MCSP token (default: false).

- includeRoles: (optional) A flag to include the `roles` claim in the MCSP token (default: true).

- prefixRoles: (optional) A flag to add a prefix with the scope level where
the role is defined in the `roles` claim (default: false).

- callerExtClaim: (optional) A map containing keys and values to be injected into the returned access token
as the `callerExt` claim. The keys used in this map must be enabled in the apikey by setting the
`callerExtClaimNames` property when the apikey is created.
This property is typically only used in scenarios involving an apikey with identityType `SERVICEID`.

- disableSSLVerification: (optional) A flag that indicates whether verification of the server's SSL
certificate should be disabled or not. The default value is `false`.

- headers: (optional) A set of key/value pairs that will be sent as HTTP headers in requests
made to the MCSP token service.

### Usage Notes
- When constructing an MCSPV2Authenticator instance, the apikey, url, scopeCollectionType, and scopeId properties are required.

- If you specify the callerExtClaim map, the keys used in the map must have been previously enabled in the apikey
by setting the `callerExtClaimNames` property when you created the apikey.
The entries contained in this map will appear in the `callerExt` field (claim) of the returned access token.

- The authenticator will invoke the token server's `POST /api/2.0/{scopeCollectionType}/{scopeId}/apikeys/token` operation to
exchange the apikey for an MCSP access token (the bearer token).

### Programming example
```java
import com.ibm.cloud.sdk.core.security.MCSPV2Authenticator;
import <sdk_base_package>.ExampleService.v1.ExampleService;
...
Map<String, String> callerExtClaim = Map.of("productID", "prod-123");

// Create the authenticator.
MCSPV2Authenticator authenticator = new MCSPV2Authenticator.Builder()
.apikey("myapikey")
.url("https://example.mcspv2.token-exchange.com")
.scopeCollectionType("accounts")
.scopeId("20250519-2128-3755-60b3-103e01c509e8")
.includeBuiltinActions(true)
.callerExtClaim(callerExtClaim)
.build();

// Create the service instance.
ExampleService service = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, authenticator);

// 'service' can now be used to invoke operations.
```

### Configuration example
External configuration:
```
export EXAMPLE_SERVICE_AUTH_TYPE=mcspv2
export EXAMPLE_SERVICE_APIKEY=myapikey
export EXAMPLE_SERVICE_AUTH_URL=https://example.mcspv2.token-exchange.com
export EXAMPLE_SERVICE_SCOPE_COLLECTION_TYPE=accounts
export EXAMPLE_SERVICE_SCOPE_ID=20250519-2128-3755-60b3-103e01c509e8
export EXAMPLE_SERVICE_INCLUDE_BUILTIN_ACTIONS=true
export EXAMPLE_SERVICE_CALLER_EXT_CLAIM={"productID":"prod-123"}
```
Application code:
```java
import <sdk_base_package>.ExampleService.v1.ExampleService;
...

// Create the service instance.
ExampleService service = ExampleService.newInstance("example_service");

// 'service' can now be used to invoke operations.
```


## No Auth Authentication
The `NoAuthAuthenticator` is a placeholder authenticator which performs no actual authentication function.
It can be used in situations where authentication needs to be bypassed, perhaps while developing
Expand Down
4 changes: 2 additions & 2 deletions debug-logging.properties
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#
# Copyright 2024 IBM Corporation.
# Copyright 2024, 2025 IBM Corporation.
# SPDX-License-Identifier: Apache2.0
#

# This file contains a java.util.logging configuration that enables debug
# logging (level = FINE) in the Java SDK core library.
#
# To use this file, you can add the "-Djava.util.logging.config.file=logging.properties"
# To use this file, you can add the "-Djava.util.logging.config.file=debug-logging.properties"
# option to your java command line.
# For more information on java.util.logging, please see:
# https://docs.oracle.com/en/java/javase/11/core/java-logging-overview.html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public interface Authenticator {
String AUTHTYPE_CONTAINER = "container";
String AUTHTYPE_VPC = "vpc";
String AUTHTYPE_MCSP = "mcsp";
String AUTHTYPE_MCSPV2 = "mcspv2";

/**
* Constants which define the names of external config propreties (credential file, environment variable, etc.).
Expand All @@ -59,6 +60,13 @@ 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_SCOPE_COLLECTION_TYPE = "SCOPE_COLLECTION_TYPE";
String PROPNAME_SCOPE_ID = "SCOPE_ID";
String PROPNAME_INCLUDE_BUILTIN_ACTIONS = "INCLUDE_BUILTIN_ACTIONS";
String PROPNAME_INCLUDE_CUSTOM_ACTIONS = "INCLUDE_CUSTOM_ACTIONS";
String PROPNAME_INCLUDE_ROLES = "INCLUDE_ROLES";
String PROPNAME_PREFIX_ROLES = "PREFIX_ROLES";
String PROPNAME_CALLER_EXT_CLAIM = "CALLER_EXT_CLAIM";

/**
* Validates the current set of configuration information in the Authenticator.
Expand Down
Loading