Skip to content

Commit aed57bb

Browse files
Add ability to authenticate with Amster credentials (#533)
* Add ability to authenticate with Amster credentials * Merge Amster PR * update snapshots --------- Co-authored-by: Volker Scheuber <vscheuber@gmail.com>
1 parent cee28bb commit aed57bb

235 files changed

Lines changed: 2368 additions & 413 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Updated to Frodo Lib 4.0.0
13+
14+
### Added
15+
16+
- Added `frodo config-manager` (`fr-config-manager`) commands.
17+
- Added support for Node Designer Nodes through several API and Ops functions to allow for doing exports, imports, deletes, etc. with custom node configurations.<br><br>
18+
Just like with journeys, custom nodes get exported and imported in the same way as they do from AIC/AM, so you can import Frodo exported custom nodes into AIC/AM and vice versa.<br><br>
19+
Additionally, journeys were updated to include custom node dependencies during exports. Even if a journey is exported with Frodo and contains these dependencies in the export JSON, they can still be imported into AIC/AM using the admin UI as it should ignore the custom node dependencies (since AIC/AM doesn't support exporting them yet).
20+
- Added `--retry <strategy>` option to all commands.
21+
- Added the ability to authenticate to an AM classic deployment using Amster credentials (i.e. a public/private key pair). The private key can be in a variety of formats such as PKCS, JWK, and OpenSSH, but is ultimately stored in PKCS#8 format. You can also use encrypted private keys by providing the passphrase when creating the connection profile.
22+
23+
### Fixed
24+
25+
- \#XXX:
26+
1027
## [4.0.0-2] - 2026-01-27
1128

1229
## [4.0.0-1] - 2026-01-26

package-lock.json

Lines changed: 4 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
]
9696
},
9797
"devDependencies": {
98-
"@rockcarver/frodo-lib": "4.0.0-2",
98+
"@rockcarver/frodo-lib": "4.0.0-3",
9999
"@types/colors": "^1.2.1",
100100
"@types/fs-extra": "^11.0.1",
101101
"@types/jest": "^29.2.3",

src/cli/FrodoCommand.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const {
2121
RETRY_STRATEGIES,
2222
RETRY_NOTHING_KEY,
2323
} = frodo.utils.constants;
24+
const { convertPrivateKeyToPem } = frodo.utils.crypto;
2425

2526
const hostArgument = new Argument(
2627
'[host]',
@@ -68,6 +69,16 @@ const serviceAccountJwkFileOption = new Option(
6869
'File containing the JSON Web Key (JWK) associated with the the service account.'
6970
);
7071

72+
const amsterPrivateKeyPassphraseOption = new Option(
73+
'--passphrase <passphrase>',
74+
'The passphrase for the Amster private key if it is encrypted.'
75+
);
76+
77+
const amsterPrivateKeyFileOption = new Option(
78+
'--private-key <file>',
79+
'File containing the private key for authenticating with Amster. Supported formats include PEM (both PKCS#1 and PKCS#8 variants), OpenSSH, DNSSEC, and JWK.'
80+
);
81+
7182
const deploymentOption = new Option(
7283
'-m, --type <type>',
7384
'Override auto-detected deployment type. Valid values for type: \n\
@@ -135,6 +146,8 @@ const defaultOpts = [
135146
loginRedirectUri,
136147
serviceAccountIdOption,
137148
serviceAccountJwkFileOption,
149+
amsterPrivateKeyPassphraseOption,
150+
amsterPrivateKeyFileOption,
138151
deploymentOption,
139152
directoryOption,
140153
insecureOption,
@@ -171,6 +184,35 @@ const stateMap = {
171184
);
172185
}
173186
},
187+
[amsterPrivateKeyPassphraseOption.attributeName()]: (passphrase: string) => {
188+
// This is needed in the case the passphrase is an option, but the private key is an environment variable.
189+
process.env.FRODO_AMSTER_PASSPHRASE = passphrase;
190+
},
191+
[amsterPrivateKeyFileOption.attributeName()]: (
192+
file: string,
193+
options: Record<string, string | boolean>
194+
) => {
195+
const passphrase =
196+
(options[amsterPrivateKeyPassphraseOption.attributeName()] as string) ||
197+
process.env.FRODO_AMSTER_PASSPHRASE;
198+
try {
199+
// Store as PEM format (PKCS#8 variant specifically) since Jose supports PEM and since PKCS#8 supports more algorithms than PKCS#1
200+
state.setAmsterPrivateKey(
201+
convertPrivateKeyToPem(
202+
fs.readFileSync(file, 'utf8'),
203+
passphrase,
204+
file
205+
.replaceAll('\\', '/')
206+
.substring(file.replaceAll('\\', '/').lastIndexOf('/') + 1)
207+
)
208+
);
209+
} catch (error) {
210+
printMessage(
211+
`Error parsing private key from file ${file}: ${error.message}`,
212+
'error'
213+
);
214+
}
215+
},
174216
[deploymentOption.attributeName()]: (type: string) =>
175217
state.setDeploymentType(type),
176218
[directoryOption.attributeName()]: (directory: string) =>
@@ -298,6 +340,8 @@ export class FrodoCommand extends FrodoStubCommand {
298340
` FRODO_LOGIN_REDIRECT_URI: Redirect Uri for custom OAuth2 client id. Overridden by '--login-redirect-uri' option.\n` +
299341
` FRODO_SA_ID: Service account uuid. Overridden by '--sa-id' option.\n` +
300342
` FRODO_SA_JWK: Service account JWK. Overridden by '--sa-jwk-file' option but takes the actual JWK as a value, not a file name.\n` +
343+
` FRODO_AMSTER_PASSPHRASE: Passphrase for the Amster private key if it is encrypted. Overridden by '--passphrase' option.\n` +
344+
` FRODO_AMSTER_PRIVATE_KEY: Amster private key. Overridden by '--private-key' option but takes the actual private key as a value (i.e. the file contents), not a file name. Supported formats include PEM (both PKCS#1 and PKCS#8 variants), OpenSSH, DNSSEC, and JWK.\n` +
301345
` FRODO_NO_CACHE: Disable token cache. Same as '--no-cache' option.\n` +
302346
` FRODO_TOKEN_CACHE_PATH: Use this token cache file instead of '~/.frodo/TokenCache.json'.\n` +
303347
('frodo conn save' === this.name()
@@ -309,7 +353,7 @@ export class FrodoCommand extends FrodoStubCommand {
309353
` FRODO_LOG_SECRET: Log API secret. Overridden by 'password' argument.\n`
310354
: ``) +
311355
` FRODO_CONNECTION_PROFILES_PATH: Use this connection profiles file instead of '~/.frodo/Connections.json'.\n` +
312-
` FRODO_AUTHENTICATION_SERVICE: Name of a login journey to use.\n` +
356+
` FRODO_AUTHENTICATION_SERVICE: Name of a login journey to use. When using an Amster private key, specifies which journey to use for Amster authentication as opposed to the default 'amsterService' journey.\n` +
313357
` FRODO_DEBUG: Set to any value to enable debug output. Same as '--debug'.\n` +
314358
` FRODO_MASTER_KEY_PATH: Use this master key file instead of '~/.frodo/masterkey.key' file.\n` +
315359
` FRODO_MASTER_KEY: Use this master key instead of what's in '~/.frodo/masterkey.key'. Takes precedence over FRODO_MASTER_KEY_PATH.\n`
@@ -359,7 +403,7 @@ export class FrodoCommand extends FrodoStubCommand {
359403
);
360404
// eslint-disable-next-line @typescript-eslint/no-explicit-any
361405
const handler: any = stateMap[k];
362-
handler(v);
406+
handler(v, options);
363407
} else {
364408
debugMessage(
365409
`FrodoCommand.handleDefaultArgsAndOpts: Ignoring non-default option '${k}'.`

src/cli/conn/conn-save.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ export default function setup() {
5757
` $ frodo conn save ${s.amBaseUrl} ${s.username} '${s.password}'\n`[
5858
'brightCyan'
5959
] +
60+
` Create a connection profile using Amster private key credentials (PingAM classic deployments only):\n` +
61+
` $ frodo conn save --private-key ${s.amsterPrivateKey} ${s.amClassicBaseUrl}\n`[
62+
'brightCyan'
63+
] +
6064
` Save an existing service account to an existing or new connection profile:\n` +
6165
` $ frodo conn save --sa-id ${s.saId} --sa-jwk-file ${s.saJwkFile} ${s.amBaseUrl}\n`[
6266
'brightCyan'
@@ -72,6 +76,10 @@ export default function setup() {
7276
` Update an existing connection profile with a custom header override for a freshly Proxy Connect-protected PingOne Advanced Identity Cloud environment:\n` +
7377
` $ frodo conn save --authentication-header-overrides '{"MY-SECRET-HEADER": "proxyconnect secret header value"}' ${s.connId}\n`[
7478
'brightCyan'
79+
] +
80+
` Update an existing connection profile to use Amster private key credentials with a custom Amster journey (PingAM classic deployments only):\n` +
81+
` $ frodo conn save --private-key ${s.amsterPrivateKey} --authentication-service ${s.customAmsterService} ${s.classicConnId}\n`[
82+
'brightCyan'
7583
]
7684
)
7785
.action(
@@ -94,6 +102,7 @@ export default function setup() {
94102
JSON.parse(options.authenticationHeaderOverrides)
95103
);
96104
}
105+
const needAmsterLogin = !!options.privateKey;
97106
const needSa =
98107
options.sa &&
99108
!state.getServiceAccountId() &&
@@ -103,7 +112,7 @@ export default function setup() {
103112
!state.getLogApiKey() &&
104113
!state.getLogApiSecret() &&
105114
needSa;
106-
const forceLoginAsUser = needSa || needLogApiKey;
115+
const forceLoginAsUser = !needAmsterLogin && (needSa || needLogApiKey);
107116
if (
108117
(options.validate && (await getTokens(forceLoginAsUser))) ||
109118
!options.validate

src/help/SampleData.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@ export const connId2 = 'zion';
99
export const username2 = 'neo@nebuchadnezzar.zion.com';
1010
export const password2 = 'R3dP!ll3d';
1111
export const realm = '/alpha';
12+
export const amClassicBaseUrl = 'https://am.example.com:8443/am';
13+
export const classicConnId = 'am.example';
14+
export const amsterPrivateKey =
15+
'/home/trinity/am/security/keys/amster/amster_rsa';
16+
export const customAmsterService = 'AmsterLogin';

src/ops/ConnectionProfileOps.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@ export function listConnectionProfiles(long: boolean = false): void {
3434
'Service Account',
3535
'Username',
3636
'Log API Key',
37+
'Authentication Service',
3738
]);
3839
Object.keys(connectionsData).forEach((c) => {
3940
table.push([
4041
c,
4142
connectionsData[c].svcacctName || connectionsData[c].svcacctId,
4243
connectionsData[c].username,
4344
connectionsData[c].logApiKey,
45+
connectionsData[c].authenticationService,
4446
]);
4547
});
4648
printMessage(table.toString(), 'data');
@@ -76,10 +78,12 @@ export async function describeConnectionProfile(
7678
debugMessage(profile);
7779
const present = '[present]';
7880
const jwk = profile.svcacctJwk;
81+
const privateKey = profile.amsterPrivateKey;
7982
if (!showSecrets) {
8083
if (profile.password) profile.password = present;
8184
if (profile.logApiSecret) profile.logApiSecret = present;
8285
if (profile.svcacctJwk) (profile as unknown)['svcacctJwk'] = present;
86+
if (profile.amsterPrivateKey) profile.amsterPrivateKey = present;
8387
}
8488
if (!profile.idmHost) {
8589
delete profile.idmHost;
@@ -122,9 +126,15 @@ export async function describeConnectionProfile(
122126
// do nothing
123127
}
124128
}
129+
if (!profile.amsterPrivateKey) {
130+
delete profile.amsterPrivateKey;
131+
}
125132
if (showSecrets && jwk) {
126133
(profile as unknown)['svcacctJwk'] = 'see below';
127134
}
135+
if (showSecrets && privateKey) {
136+
profile.amsterPrivateKey = 'see below';
137+
}
128138
if (!profile.authenticationService) {
129139
delete profile.authenticationService;
130140
}
@@ -141,12 +151,16 @@ export async function describeConnectionProfile(
141151
svcacctId: 'Service Account Id',
142152
svcacctJwk: 'Service Account JWK',
143153
svcacctScope: 'Service Account Scope',
154+
amsterPrivateKey: 'Amster Private Key',
144155
};
145156
const table = createObjectTable(profile, keyMap);
146157
printMessage(table.toString(), 'data');
147158
if (showSecrets && jwk) {
148159
printMessage(JSON.stringify(jwk), 'data');
149160
}
161+
if (showSecrets && privateKey) {
162+
printMessage(privateKey, 'data');
163+
}
150164
} else {
151165
printMessage(`No connection profile ${host} found`);
152166
}

test/client_cli/en/__snapshots__/admin-add-autoid-static-user-mapping.test.js.snap

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ Options:
7171
(choices: "classic", "cloud",
7272
"forgeops")
7373
--no-cache Disable token cache for this operation.
74+
--passphrase <passphrase> The passphrase for the Amster private
75+
key if it is encrypted.
76+
--private-key <file> File containing the private key for
77+
authenticating with Amster. Supported
78+
formats include PEM (both PKCS#1 and
79+
PKCS#8 variants), OpenSSH, DNSSEC, and
80+
JWK.
7481
--retry <strategy> Retry failed operations. Valid values
7582
for strategy:
7683
everything: Retry all failed operations.
@@ -101,10 +108,12 @@ Environment Variables:
101108
FRODO_LOGIN_REDIRECT_URI: Redirect Uri for custom OAuth2 client id. Overridden by '--login-redirect-uri' option.
102109
FRODO_SA_ID: Service account uuid. Overridden by '--sa-id' option.
103110
FRODO_SA_JWK: Service account JWK. Overridden by '--sa-jwk-file' option but takes the actual JWK as a value, not a file name.
111+
FRODO_AMSTER_PASSPHRASE: Passphrase for the Amster private key if it is encrypted. Overridden by '--passphrase' option.
112+
FRODO_AMSTER_PRIVATE_KEY: Amster private key. Overridden by '--private-key' option but takes the actual private key as a value (i.e. the file contents), not a file name. Supported formats include PEM (both PKCS#1 and PKCS#8 variants), OpenSSH, DNSSEC, and JWK.
104113
FRODO_NO_CACHE: Disable token cache. Same as '--no-cache' option.
105114
FRODO_TOKEN_CACHE_PATH: Use this token cache file instead of '~/.frodo/TokenCache.json'.
106115
FRODO_CONNECTION_PROFILES_PATH: Use this connection profiles file instead of '~/.frodo/Connections.json'.
107-
FRODO_AUTHENTICATION_SERVICE: Name of a login journey to use.
116+
FRODO_AUTHENTICATION_SERVICE: Name of a login journey to use. When using an Amster private key, specifies which journey to use for Amster authentication as opposed to the default 'amsterService' journey.
108117
FRODO_DEBUG: Set to any value to enable debug output. Same as '--debug'.
109118
FRODO_MASTER_KEY_PATH: Use this master key file instead of '~/.frodo/masterkey.key' file.
110119
FRODO_MASTER_KEY: Use this master key instead of what's in '~/.frodo/masterkey.key'. Takes precedence over FRODO_MASTER_KEY_PATH.

0 commit comments

Comments
 (0)