Skip to content

Commit f8e838c

Browse files
committed
feat(auth): add support for MCSP V2 authentication
This commit adds the MCSPV2Authenticator implementation. This authenticator will invoke the MCSP V2 POST /api/2.0/{scopeCollectionType}/{scopeId}/apikeys/token operation to obtain an access token for a user-supplied apikey. Signed-off-by: Phil Adams <[email protected]>
1 parent bb2d72b commit f8e838c

17 files changed

+1479
-27
lines changed

.github/workflows/build.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# semantic-release is also run to create a new release (if
44
# warranted by the new commits being built).
55

6-
name: Build/Test
6+
name: build
77

88
on:
99
push:
@@ -16,7 +16,7 @@ on:
1616
jobs:
1717
detect-secrets:
1818
if: "!contains(github.event.head_commit.message, '[skip ci]')"
19-
name: Detect-Secrets
19+
name: detect-secrets
2020
runs-on: ubuntu-latest
2121

2222
steps:
@@ -38,8 +38,8 @@ jobs:
3838
detect-secrets -v audit --report --fail-on-unaudited --fail-on-live --fail-on-audited-real .secrets.baseline
3939
4040
build:
41+
name: build-test (java ${{matrix.java-version}})
4142
needs: detect-secrets
42-
name: Build/Test (Java ${{matrix.java-version}})
4343
runs-on: ubuntu-latest
4444
strategy:
4545
matrix:
@@ -60,8 +60,8 @@ jobs:
6060
run: mvn -B clean package
6161

6262
create-release:
63+
name: semantic-release
6364
needs: build
64-
name: Semantic-Release
6565
if: "github.ref_name == 'main' && github.event_name != 'pull_request'"
6666
runs-on: ubuntu-latest
6767

.github/workflows/publish.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# - building and publishing javadocs to the git repository.
44
# It is triggered when a new release is created.
55

6-
name: Publish
6+
name: publish
77

88
on:
99
release:
@@ -13,7 +13,7 @@ on:
1313

1414
jobs:
1515
publish:
16-
name: Publish Release
16+
name: publish-release
1717
runs-on: ubuntu-latest
1818

1919
steps:

.secrets.baseline

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "package-lock.json|^.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2025-01-08T20:47:36Z",
6+
"generated_at": "2025-05-23T19:16:27Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -80,55 +80,55 @@
8080
"hashed_secret": "91dfd9ddb4198affc5c194cd8ce6d338fde470e2",
8181
"is_secret": false,
8282
"is_verified": false,
83-
"line_number": 73,
83+
"line_number": 74,
8484
"type": "Secret Keyword",
8585
"verified_result": null
8686
},
8787
{
8888
"hashed_secret": "4f51cde3ac0a5504afa4bc06859b098366592c19",
8989
"is_secret": false,
9090
"is_verified": false,
91-
"line_number": 222,
91+
"line_number": 223,
9292
"type": "Secret Keyword",
9393
"verified_result": null
9494
},
9595
{
9696
"hashed_secret": "e87559ed7decb62d0733ae251ae58d42a55291d8",
9797
"is_secret": false,
9898
"is_verified": false,
99-
"line_number": 224,
99+
"line_number": 225,
100100
"type": "Secret Keyword",
101101
"verified_result": null
102102
},
103103
{
104104
"hashed_secret": "12f4a68ed3d0863e56497c9cdb1e2e4e91d5cb68",
105105
"is_secret": false,
106106
"is_verified": false,
107-
"line_number": 288,
107+
"line_number": 289,
108108
"type": "Secret Keyword",
109109
"verified_result": null
110110
},
111111
{
112112
"hashed_secret": "c837b75d7cd93ef9c2243ca28d6e5156259fd253",
113113
"is_secret": false,
114114
"is_verified": false,
115-
"line_number": 292,
115+
"line_number": 293,
116116
"type": "Secret Keyword",
117117
"verified_result": null
118118
},
119119
{
120120
"hashed_secret": "98635b2eaa2379f28cd6d72a38299f286b81b459",
121121
"is_secret": false,
122122
"is_verified": false,
123-
"line_number": 526,
123+
"line_number": 529,
124124
"type": "Secret Keyword",
125125
"verified_result": null
126126
},
127127
{
128128
"hashed_secret": "47fcf185ee7e15fe05cae31fbe9e4ebe4a06a40d",
129129
"is_secret": false,
130130
"is_verified": false,
131-
"line_number": 629,
131+
"line_number": 729,
132132
"type": "Secret Keyword",
133133
"verified_result": null
134134
}
@@ -146,23 +146,23 @@
146146
"hashed_secret": "d4c3d66fd0c38547a3c7a4c6bdc29c36911bc030",
147147
"is_secret": false,
148148
"is_verified": false,
149-
"line_number": 47,
149+
"line_number": 48,
150150
"type": "Secret Keyword",
151151
"verified_result": null
152152
},
153153
{
154154
"hashed_secret": "8318df9ecda039deac9868adf1944a29a95c7114",
155155
"is_secret": false,
156156
"is_verified": false,
157-
"line_number": 50,
157+
"line_number": 51,
158158
"type": "Secret Keyword",
159159
"verified_result": null
160160
},
161161
{
162162
"hashed_secret": "9a66213cc16d178fdbf9f4da6b7bd92497fda404",
163163
"is_secret": false,
164164
"is_verified": false,
165-
"line_number": 56,
165+
"line_number": 57,
166166
"type": "Secret Keyword",
167167
"verified_result": null
168168
}
@@ -245,6 +245,16 @@
245245
"verified_result": null
246246
}
247247
],
248+
"src/test/java/com/ibm/cloud/sdk/core/test/security/MCSPV2AuthenticatorTest.java": [
249+
{
250+
"hashed_secret": "d3576f76725ff2040fe425790fe8b5ccf61994a2",
251+
"is_secret": false,
252+
"is_verified": false,
253+
"line_number": 63,
254+
"type": "Secret Keyword",
255+
"verified_result": null
256+
}
257+
],
248258
"src/test/java/com/ibm/cloud/sdk/core/util/CredentialUtilsTest.java": [
249259
{
250260
"hashed_secret": "1beb7496ebbe82c61151be093956d83dac625c13",
@@ -301,6 +311,24 @@
301311
"verified_result": null
302312
}
303313
],
314+
"src/test/resources/mcspv2_token.json": [
315+
{
316+
"hashed_secret": "3c1e5e4bcfb74c7ba031f00ee4a1ebe685fbf57a",
317+
"is_secret": false,
318+
"is_verified": false,
319+
"line_number": 2,
320+
"type": "JSON Web Token",
321+
"verified_result": null
322+
},
323+
{
324+
"hashed_secret": "e196c8eb696e0f04be7eb863abbf17a8b2adf993",
325+
"is_secret": false,
326+
"is_verified": false,
327+
"line_number": 2,
328+
"type": "SoftLayer Credentials",
329+
"verified_result": null
330+
}
331+
],
304332
"src/test/resources/my-credentials.env": [
305333
{
306334
"hashed_secret": "edbd5e119f94badb9f99a67ac6ff4c7a5204ad61",
@@ -389,6 +417,24 @@
389417
"verified_result": null
390418
}
391419
],
420+
"src/test/resources/refreshed_mcspv2_token.json": [
421+
{
422+
"hashed_secret": "d9b5dacb7cabd9bb3369f1cc45e237d9658d5efc",
423+
"is_secret": false,
424+
"is_verified": false,
425+
"line_number": 2,
426+
"type": "JSON Web Token",
427+
"verified_result": null
428+
},
429+
{
430+
"hashed_secret": "e196c8eb696e0f04be7eb863abbf17a8b2adf993",
431+
"is_secret": false,
432+
"is_verified": false,
433+
"line_number": 2,
434+
"type": "SoftLayer Credentials",
435+
"verified_result": null
436+
}
437+
],
392438
"src/test/resources/vcap_services.json": [
393439
{
394440
"hashed_secret": "0ee6f3a69b36c1bcac73c25350a7414a53397ecd",

Authentication.md

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ The java-sdk-core project supports the following types of authentication:
77
- Container Authentication
88
- VPC Instance Authentication
99
- Cloud Pak for Data Authentication
10-
- Multi-Cloud Saas Platform (MCSP) Authentication
10+
- Multi-Cloud Saas Platform (MCSP) V1 Authentication
11+
- Multi-Cloud Saas Platform (MCSP) V2 Authentication
1112
- No Authentication (for testing)
1213

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

577578

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

645646

647+
## Multi-Cloud Saas Platform (MCSP) V2 Authentication
648+
The `MCSPV2Authenticator` can be used in scenarios where an application needs to
649+
interact with an IBM Cloud service that has been deployed to a non-IBM Cloud environment (e.g. AWS).
650+
It accepts a user-supplied apikey and invokes the Multi-Cloud Saas Platform token service's
651+
`POST /api/2.0/{scopeCollectionType}/{scopeId}/apikeys/token` operation to obtain a suitable MCSP access token (a bearer token)
652+
for the specified apikey.
653+
The authenticator will also obtain a new bearer token when the current token expires.
654+
The bearer token is then added to each outbound request in the `Authorization` header in the
655+
form:
656+
```
657+
Authorization: Bearer <bearer-token>
658+
```
659+
660+
### Properties
661+
662+
- apikey: (required) the apikey to be used to obtain an MCSP access token.
663+
664+
- url: (required) The URL representing the MCSP token service endpoint's base URL string. Do not include the
665+
operation path (e.g. `/siusermgr/api/1.0/apikeys/token`) as part of this property's value.
666+
667+
- scopeCollectionType: (required) The scope collection type of item(s).
668+
The valid values are: `accounts`, `subscriptions`, `services`.
669+
670+
- scopeId: (required) The scope identifier of item(s).
671+
672+
- includeBuiltinActions: (optional) A flag to include builtin actions in the `actions` claim in the MCSP token (default: false).
673+
674+
- includeCustomActions: (optional) A flag to include custom actions in the `actions` claim in the MCSP token (default: false).
675+
676+
- includeRoles: (optional) A flag to include the `roles` claim in the MCSP token (default: true).
677+
678+
- prefixRoles: (optional) A flag to add a prefix with the scope level where
679+
the role is defined in the `roles` claim (default: false).
680+
681+
- callerExtClaim: (optional) A map containing keys and values to be injected into the returned access token
682+
as the `callerExt` claim. The keys used in this map must be enabled in the apikey by setting the
683+
`callerExtClaimNames` property when the apikey is created.
684+
This property is typically only used in scenarios involving an apikey with identityType `SERVICEID`.
685+
686+
- disableSSLVerification: (optional) A flag that indicates whether verification of the server's SSL
687+
certificate should be disabled or not. The default value is `false`.
688+
689+
- headers: (optional) A set of key/value pairs that will be sent as HTTP headers in requests
690+
made to the MCSP token service.
691+
692+
### Usage Notes
693+
- When constructing an MCSPV2Authenticator instance, the apikey, url, scopeCollectionType, and scopeId properties are required.
694+
695+
- If you specify the callerExtClaim map, the keys used in the map must have been previously enabled in the apikey
696+
by setting the `callerExtClaimNames` property when you created the apikey.
697+
The entries contained in this map will appear in the `callerExt` field (claim) of the returned access token.
698+
699+
- The authenticator will invoke the token server's `POST /api/2.0/{scopeCollectionType}/{scopeId}/apikeys/token` operation to
700+
exchange the apikey for an MCSP access token (the bearer token).
701+
702+
### Programming example
703+
```java
704+
import com.ibm.cloud.sdk.core.security.MCSPV2Authenticator;
705+
import <sdk_base_package>.ExampleService.v1.ExampleService;
706+
...
707+
Map<String, String> callerExtClaim = Map.of("productID", "prod-123");
708+
709+
// Create the authenticator.
710+
MCSPV2Authenticator authenticator = new MCSPV2Authenticator.Builder()
711+
.apikey("myapikey")
712+
.url("https://example.mcspv2.token-exchange.com")
713+
.scopeCollectionType("accounts")
714+
.scopeId("20250519-2128-3755-60b3-103e01c509e8")
715+
.includeBuiltinActions(true)
716+
.callerExtClaim(callerExtClaim)
717+
.build();
718+
719+
// Create the service instance.
720+
ExampleService service = new ExampleService(ExampleService.DEFAULT_SERVICE_NAME, authenticator);
721+
722+
// 'service' can now be used to invoke operations.
723+
```
724+
725+
### Configuration example
726+
External configuration:
727+
```
728+
export EXAMPLE_SERVICE_AUTH_TYPE=mcspv2
729+
export EXAMPLE_SERVICE_APIKEY=myapikey
730+
export EXAMPLE_SERVICE_AUTH_URL=https://example.mcspv2.token-exchange.com
731+
export EXAMPLE_SERVICE_SCOPE_COLLECTION_TYPE=accounts
732+
export EXAMPLE_SERVICE_SCOPE_ID=20250519-2128-3755-60b3-103e01c509e8
733+
export EXAMPLE_SERVICE_INCLUDE_BUILTIN_ACTIONS=true
734+
export EXAMPLE_SERVICE_CALLER_EXT_CLAIM={"productID":"prod-123"}
735+
```
736+
Application code:
737+
```java
738+
import <sdk_base_package>.ExampleService.v1.ExampleService;
739+
...
740+
741+
// Create the service instance.
742+
ExampleService service = ExampleService.newInstance("example_service");
743+
744+
// 'service' can now be used to invoke operations.
745+
```
746+
747+
646748
## No Auth Authentication
647749
The `NoAuthAuthenticator` is a placeholder authenticator which performs no actual authentication function.
648750
It can be used in situations where authentication needs to be bypassed, perhaps while developing

debug-logging.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#
2-
# Copyright 2024 IBM Corporation.
2+
# Copyright 2024, 2025 IBM Corporation.
33
# SPDX-License-Identifier: Apache2.0
44
#
55

66
# This file contains a java.util.logging configuration that enables debug
77
# logging (level = FINE) in the Java SDK core library.
88
#
9-
# To use this file, you can add the "-Djava.util.logging.config.file=logging.properties"
9+
# To use this file, you can add the "-Djava.util.logging.config.file=debug-logging.properties"
1010
# option to your java command line.
1111
# For more information on java.util.logging, please see:
1212
# https://docs.oracle.com/en/java/javase/11/core/java-logging-overview.html

src/main/java/com/ibm/cloud/sdk/core/security/Authenticator.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public interface Authenticator {
3434
String AUTHTYPE_CONTAINER = "container";
3535
String AUTHTYPE_VPC = "vpc";
3636
String AUTHTYPE_MCSP = "mcsp";
37+
String AUTHTYPE_MCSPV2 = "mcspv2";
3738

3839
/**
3940
* Constants which define the names of external config propreties (credential file, environment variable, etc.).
@@ -59,6 +60,13 @@ public interface Authenticator {
5960
String PROPNAME_IAM_PROFILE_ID = "IAM_PROFILE_ID";
6061
String PROPNAME_IAM_PROFILE_NAME = "IAM_PROFILE_NAME";
6162
String PROPNAME_IAM_ACCOUNT_ID = "IAM_ACCOUNT_ID";
63+
String PROPNAME_SCOPE_COLLECTION_TYPE = "SCOPE_COLLECTION_TYPE";
64+
String PROPNAME_SCOPE_ID = "SCOPE_ID";
65+
String PROPNAME_INCLUDE_BUILTIN_ACTIONS = "INCLUDE_BUILTIN_ACTIONS";
66+
String PROPNAME_INCLUDE_CUSTOM_ACTIONS = "INCLUDE_CUSTOM_ACTIONS";
67+
String PROPNAME_INCLUDE_ROLES = "INCLUDE_ROLES";
68+
String PROPNAME_PREFIX_ROLES = "PREFIX_ROLES";
69+
String PROPNAME_CALLER_EXT_CLAIM = "CALLER_EXT_CLAIM";
6270

6371
/**
6472
* Validates the current set of configuration information in the Authenticator.

0 commit comments

Comments
 (0)