diff --git a/package-lock.json b/package-lock.json index 365320ef2..db036b21d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "fs-extra": "^11.1.1", "jest": "^29.3.1", "jest-jasmine2": "^29.7.0", + "jwk-to-pem": "^2.0.7", "loglevel": "^1.9.1", "map-stream": "^0.0.7", "mock-fs": "^5.2.0", @@ -55,6 +56,7 @@ "rimraf": "^6.0.1", "setup-polly-jest": "^0.11.0", "slugify": "^1.6.5", + "sshpk": "^1.18.0", "ts-jest": "^29.1.2", "tsup": "^8.0.2", "typedoc": "^0.27.9", @@ -3065,6 +3067,39 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", @@ -3361,6 +3396,16 @@ "node": ">=10.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/blueimp-md5": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", @@ -3368,6 +3413,13 @@ "dev": true, "license": "MIT" }, + "node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -3456,6 +3508,13 @@ "node": ">=8" } }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true, + "license": "MIT" + }, "node_modules/browserslist": { "version": "4.24.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", @@ -3986,6 +4045,19 @@ "node": ">= 8" } }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/data-uri-to-buffer": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", @@ -4315,6 +4387,24 @@ "dev": true, "license": "MIT" }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4345,6 +4435,22 @@ "dev": true, "license": "ISC" }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", @@ -5507,15 +5613,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -5735,6 +5842,16 @@ "node": ">= 14" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -5966,6 +6083,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -5979,6 +6107,18 @@ "node": ">= 0.4" } }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -7662,6 +7802,18 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jwk-to-pem": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.7.tgz", + "integrity": "sha512-cSVphrmWr6reVchuKQZdfSs4U9c5Y4hwZggPoz6cbVnTpAVgGRpEuQng86IyqLeGZlhTh+c4MAreB6KbdQDKHQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "asn1.js": "^5.3.0", + "elliptic": "^6.6.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -8017,6 +8169,20 @@ "node": ">=6" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true, + "license": "MIT" + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -8077,9 +8243,9 @@ } }, "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "dev": true, "license": "MIT", "dependencies": { @@ -8087,7 +8253,7 @@ "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", - "on-headers": "~1.0.2" + "on-headers": "~1.1.0" }, "engines": { "node": ">= 0.8.0" @@ -8405,9 +8571,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, "license": "MIT", "engines": { @@ -9902,6 +10068,39 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -10642,6 +10841,13 @@ "node": ">= 8" } }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 5bc41e41c..8a98bcff1 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,7 @@ "fs-extra": "^11.1.1", "jest": "^29.3.1", "jest-jasmine2": "^29.7.0", + "jwk-to-pem": "^2.0.7", "loglevel": "^1.9.1", "map-stream": "^0.0.7", "mock-fs": "^5.2.0", @@ -149,6 +150,7 @@ "rimraf": "^6.0.1", "setup-polly-jest": "^0.11.0", "slugify": "^1.6.5", + "sshpk": "^1.18.0", "ts-jest": "^29.1.2", "tsup": "^8.0.2", "typedoc": "^0.27.9", diff --git a/src/api/AuthenticateApi.test.ts b/src/api/AuthenticateApi.test.ts index 3f94dcf31..5aaa82e07 100644 --- a/src/api/AuthenticateApi.test.ts +++ b/src/api/AuthenticateApi.test.ts @@ -36,6 +36,8 @@ import * as AuthenticateApi from './AuthenticateApi'; import { state } from '../index'; import { autoSetupPolly } from '../utils/AutoSetupPolly'; import { defaultMatchRequestsBy, filterRecording } from '../utils/PollyUtils'; +import { fillCallbacks } from '../ops/CallbackOps'; +import { AuthenticateStep } from './AuthenticateApi'; // need to modify the default matching rules to allow the mocking to work for an authentication flow. const matchConfig = defaultMatchRequestsBy(); @@ -74,13 +76,13 @@ describe('AuthenticateApi', () => { 'X-OpenAM-Password': state.getPassword(), }, }; - const response1 = await AuthenticateApi.step({ + const step = await AuthenticateApi.step({ body: {}, config, realm: state.getRealm(), state, }); - expect(response1).toMatchSnapshot(); + expect(step).toMatchSnapshot(); }); test("2: Two step login journey 'PasswordGrant'", async () => { @@ -93,27 +95,27 @@ describe('AuthenticateApi', () => { state.setAuthenticationService( process.env.FRODO_AUTHENTICATION_SERVICE || 'PasswordGrant' ); - const response1 = await AuthenticateApi.step({ + const step1 = await AuthenticateApi.step({ body: {}, config: {}, realm: state.getRealm(), state, }); - expect(response1).toMatchSnapshot(); - const body = AuthenticateApi.fillCallbacks({ - response: response1, + expect(step1).toMatchSnapshot(); + const body = fillCallbacks({ + step: step1 as AuthenticateStep, map: { IDToken1: state.getUsername() as string, IDToken2: state.getPassword() as string, }, }); - const response2 = await AuthenticateApi.step({ + const step2 = await AuthenticateApi.step({ body, config: {}, realm: state.getRealm(), state, }); - expect(response2).toMatchSnapshot(); + expect(step2).toMatchSnapshot(); }); }); }); diff --git a/src/api/AuthenticateApi.ts b/src/api/AuthenticateApi.ts index 997a698d6..519126fc2 100644 --- a/src/api/AuthenticateApi.ts +++ b/src/api/AuthenticateApi.ts @@ -1,5 +1,7 @@ +import { AxiosRequestConfig } from 'axios'; import util from 'util'; +import { Callback } from '../ops/CallbackOps'; import { State } from '../shared/State'; import { getRealmPath } from '../utils/ForgeRockUtils'; import { generateAmApi } from './BaseApi'; @@ -12,31 +14,36 @@ const getApiConfig = () => ({ apiVersion, }); -/** - * Fill callbacks from a map - * Just a start - * @param {object} response json response from a call to /authenticate - * @param {{ [k: string]: string | number | boolean | string[] }} map name/value map - * @returns filled response body so it can be used as input to another call to /authenticate - */ -export function fillCallbacks({ - response, - map, -}: { - response: object; - map: { [k: string]: string | number | boolean | string[] }; -}): object { - const body = JSON.parse(JSON.stringify(response)); - for (const callback of body.callbacks) { - callback.input[0].value = map[callback.input[0].name]; - } - return body; -} +export type AuthenticateStep = { + authId: string; + callbacks: Callback[]; + template?: string; + stage?: string; + header?: string; + description?: string; +}; + +export type AuthenticateSuccessResponse = { + tokenId: string; + successUrl: string; + realm: string; +}; + +export type AuthenticateErrorResponse = { + code: string; + reason: string; + message: string; +}; + +export type AuthenticateResponse = + | AuthenticateStep + | AuthenticateSuccessResponse + | AuthenticateErrorResponse; /** - * - * @param {any} body POST request body - * @param {any} config request config + * Performs an authentication step using the service's authenticate endpoint + * @param {AuthenticateStep | Record} body POST request body + * @param {AxiosRequestConfig} config request config * @param {string} realm realm * @param {string} service name of authentication service/journey * @returns Promise resolving to the authentication service response @@ -48,12 +55,12 @@ export async function step({ service = undefined, state, }: { - body?: object; - config?: object; + body?: AuthenticateStep | Record; + config?: AxiosRequestConfig; realm?: string; service?: string; state: State; -}): Promise { +}): Promise { const urlString = service || state.getAuthenticationService() ? util.format( diff --git a/src/lib/FrodoLib.test.ts b/src/lib/FrodoLib.test.ts index 181f60176..b39331846 100644 --- a/src/lib/FrodoLib.test.ts +++ b/src/lib/FrodoLib.test.ts @@ -1,5 +1,11 @@ import { frodo, frodo as instance0, state as state0 } from '../index'; +import Constants from '../shared/Constants'; import { autoSetupPolly } from '../utils/AutoSetupPolly'; +import fs from 'fs' +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); autoSetupPolly(); @@ -66,4 +72,39 @@ describe('FrodoLib', () => { JSON.parse('{"k":"jwk2"}') ); }); + + test(`frodo.createInstanceWithAmsterAccount(): FrodoLib is instantiable using factory helper`, async () => { + const privateKey1 = fs.readFileSync( + path.resolve( + __dirname, + '../test/mocks/CryptoUtils/pkcs8Rsa.pem' + ), + 'utf8' + ); + const privateKey2 = fs.readFileSync( + path.resolve( + __dirname, + '../test/mocks/CryptoUtils/pkcs1Rsa.pem' + ), + 'utf8' + ); + const customAmsterService = 'AmsterLogin'; + state0.setHost('https://instance0/am'); + const instance1 = frodo.createInstanceWithAmsterAccount( + 'https://instance1/am', + privateKey1 + ); + const instance2 = frodo.createInstanceWithAmsterAccount( + 'https://instance2/am', + privateKey2, + customAmsterService + ); + expect(instance0.state.getHost()).toEqual(host0); + expect(instance1.state.getHost()).toEqual(host1); + expect(instance1.state.getAmsterPrivateKey()).toEqual(privateKey1); + expect(instance1.state.getAuthenticationService()).toEqual(Constants.DEFAULT_AMSTER_SERVICE); + expect(instance2.state.getHost()).toEqual(host2); + expect(instance2.state.getAmsterPrivateKey()).toEqual(privateKey2); + expect(instance2.state.getAuthenticationService()).toEqual(customAmsterService); + }); }); diff --git a/src/lib/FrodoLib.ts b/src/lib/FrodoLib.ts index 097d48748..db825c34f 100644 --- a/src/lib/FrodoLib.ts +++ b/src/lib/FrodoLib.ts @@ -91,6 +91,7 @@ import VersionUtils, { Version } from '../ops/VersionUtils'; import ConstantsImpl, { Constants } from '../shared/Constants'; import StateImpl, { State, StateInterface } from '../shared/State'; import Base64Utils, { Base64 } from '../utils/Base64Utils'; +import CryptoUtils, { FrodoCrypto } from '../utils/CryptoUtils'; import ExportImportUtils, { ExportImport } from '../utils/ExportImportUtils'; import ForgeRockUtils, { FRUtils } from '../utils/ForgeRockUtils'; import JsonUtils, { Json } from '../utils/JsonUtils'; @@ -208,6 +209,7 @@ export type Frodo = { ExportImport & Base64 & { constants: Constants; + crypto: FrodoCrypto; jose: Jose; json: Json; version: Version; @@ -265,6 +267,29 @@ export type Frodo = { debug?: boolean, curlirize?: boolean ): Frodo; + + /** + * Factory helper to create a frodo instance ready for logging in with Amster credentials + * @param {string} host host base URL, e.g. 'https://am.example.com:8443/am' + * @param {string} amsterPrivateKey the pem encoded private key used to authenticate with Amster + * @param {string} authenticationService (optional) the authentication service used to authenticate with Amster (default: 'amsterService') + * @param {string} realm (optional) override default realm + * @param {string} deploymentType (optional) override deployment type ('cloud', 'forgeops', or 'classic') + * @param {boolean} allowInsecureConnection (optional) allow insecure connection + * @param {boolean} debug (optional) enable debug output + * @param {boolean} curlirize (optional) enable output of all library REST calls as curl commands + * @returns {Frodo} frodo instance + */ + createInstanceWithAmsterAccount( + host: string, + amsterPrivateKey: string, + authenticationService?: string, + realm?: string, + deploymentType?: string, + allowInsecureConnection?: boolean, + debug?: boolean, + curlirize?: boolean + ): Frodo; }; /** @@ -381,6 +406,7 @@ const FrodoLib = (config: StateInterface = {}): Frodo => { ...ExportImportUtils(state), ...Base64Utils(), constants: ConstantsImpl, + crypto: CryptoUtils(), jose: JoseOps(state), json: JsonUtils(), version: VersionUtils(state), @@ -389,6 +415,7 @@ const FrodoLib = (config: StateInterface = {}): Frodo => { createInstance, createInstanceWithAdminAccount, createInstanceWithServiceAccount, + createInstanceWithAmsterAccount, }; }; @@ -397,6 +424,30 @@ function createInstance(config: StateInterface): Frodo { return frodo; } +function createInstanceWithAmsterAccount( + host: string, + amsterPrivateKey: string, + authenticationService: string = ConstantsImpl.DEFAULT_AMSTER_SERVICE, + realm: string = undefined, + deploymentType: string = undefined, + allowInsecureConnection = false, + debug = false, + curlirize = false +): Frodo { + const config: StateInterface = { + host, + amsterPrivateKey, + authenticationService, + realm, + deploymentType, + allowInsecureConnection, + debug, + curlirize, + }; + const frodo = FrodoLib(config); + return frodo; +} + function createInstanceWithServiceAccount( host: string, serviceAccountId: string, @@ -520,6 +571,22 @@ function createInstanceWithAdminAccount( * // and perform operations * instance3.authn.journey.exportJourney('Login'); * ``` + * + * {@link frodo.createInstanceWithAmsterAccount} + * ```javascript + * // use factory method to create a new Frodo instance ready to login with Amster account + * const instance4 = frodo.createInstanceWithAmsterAccount( + * 'https://instance4/am', + * '-----BEGIN PRIVATE KEY-----\nMIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCVPUZaHCRHu9i3\n...', + * 'amsterService' + * ); + * + * // now the instance can login + * instance4.login.getTokens(); + * + * // and perform AM operations + * instance4.authn.journey.exportJourney('Login'); + * ``` */ const frodo = FrodoLib(); diff --git a/src/ops/AdminOps.ts b/src/ops/AdminOps.ts index 65cb80181..0b8cc425a 100644 --- a/src/ops/AdminOps.ts +++ b/src/ops/AdminOps.ts @@ -1660,7 +1660,7 @@ export async function trainAA({ .then((response) => { printMessage({ message: `${username},${ipAddress},${userAgent},${ - response.tokenId ? 'OK' : 'NOK' + 'tokenId' in response ? 'OK' : 'NOK' }`, state, }); diff --git a/src/ops/AuthenticateOps.test.ts b/src/ops/AuthenticateOps.test.ts index 1265227b8..22ec86dcb 100644 --- a/src/ops/AuthenticateOps.test.ts +++ b/src/ops/AuthenticateOps.test.ts @@ -9,6 +9,12 @@ * * FRODO_DEBUG=1 FRODO_HOST=https://openam-frodo-dev.forgeblocks.com/am FRODO_USERNAME=volker.scheuber@forgerock.com FRODO_PASSWORD='S3cr3!S@uc3' npm run test:record_noauth AuthenticateOps * + * You must also do the same when testing a classic deployment. Additionally, + * if recording any tests involving the Amster private key in the pkcs8.pem + * file, you must add the corresponding public key from pkcs8.pub into your + * authorized_keys file in /path/to/am/security/keys/amster/authorized_keys, + * otherwise the key will not be recognized by AM and you will get a 401 error. + * * 2. Update CJS snapshots * * After recording, the ESM snapshots will already be updated as that happens @@ -28,8 +34,14 @@ */ import { state } from '../index'; import * as AuthenticateOps from './AuthenticateOps'; -import { autoSetupPolly } from '../utils/AutoSetupPolly'; +import { autoSetupPolly, setDefaultState } from '../utils/AutoSetupPolly'; import { defaultMatchRequestsBy, filterRecording } from '../utils/PollyUtils'; +import Constants from '../shared/Constants'; +import fs from 'fs' +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); // need to modify the default matching rules to allow the mocking to work for an authentication flow. const matchConfig = defaultMatchRequestsBy(); @@ -47,27 +59,138 @@ describe('AuthenticateOps', () => { } }); - describe('getTokens()', () => { - test('0: Method is implemented', async () => { - expect(AuthenticateOps.getTokens).toBeDefined(); + // Phase 1 + if ( + !process.env.FRODO_POLLY_MODE || + (process.env.FRODO_POLLY_MODE === 'record_noauth' && + process.env.FRODO_RECORD_PHASE === '1') + ) { + describe('Cloud Tests', () => { + beforeEach(() => { + setDefaultState(undefined, true); + }); + + describe('getTokens()', () => { + test('0: Method is implemented', async () => { + expect(AuthenticateOps.getTokens).toBeDefined(); + }); + + test.skip('1: Authenticate successfully as user', async () => { + state.setDeploymentType(undefined); + state.setUsername(process.env.FRODO_USERNAME || 'mockUser'); + state.setPassword(process.env.FRODO_PASSWORD || 'mockPassword'); + const result = await AuthenticateOps.getTokens({ state }); + expect(result).toBeTruthy(); + expect(result.subject).toEqual("user " + state.getUsername()); + expect(result.userSessionToken.tokenId).toBeTruthy(); + expect(result.userSessionToken.expires).toBeTruthy(); + expect(result).toMatchSnapshot({ + subject: expect.any(String) + }); + expect(state.getDeploymentType()).toEqual(Constants.CLOUD_DEPLOYMENT_TYPE_KEY); + expect(state.getCookieName()).toBeTruthy(); + expect(state.getCookieValue()).toBeTruthy(); + expect(state.getBearerToken()).toBeTruthy(); + expect(state.getCookieName()).toMatchSnapshot(); + expect(state.getCookieValue()).toMatchSnapshot(); + expect(state.getBearerToken()).toMatchSnapshot(); + }); + + test.todo("2: Authenticate successfully as service account"); + }); }); + } + + // Phase 2 + if ( + !process.env.FRODO_POLLY_MODE || + (process.env.FRODO_POLLY_MODE === 'record_noauth' && + process.env.FRODO_RECORD_PHASE === '2') + ) { + describe('Classic Tests', () => { + beforeEach(() => { + setDefaultState(Constants.CLASSIC_DEPLOYMENT_TYPE_KEY, true); + }); + describe('getTokens()', () => { + test('0: Authenticate successfully as user', async () => { + state.setDeploymentType(undefined); + state.setUsername(process.env.FRODO_USERNAME || 'mockUser'); + state.setPassword(process.env.FRODO_PASSWORD || 'mockPassword'); + const result = await AuthenticateOps.getTokens({ state }); + expect(result).toBeTruthy(); + expect(result.subject).toEqual("user " + state.getUsername()); + expect(result.userSessionToken.tokenId).toBeTruthy(); + expect(result.userSessionToken.expires).toBeTruthy(); + expect(result).toMatchSnapshot({ + subject: expect.any(String) + }); + expect(state.getDeploymentType()).toEqual(Constants.CLASSIC_DEPLOYMENT_TYPE_KEY); + expect(state.getCookieName()).toBeTruthy(); + expect(state.getCookieValue()).toBeTruthy(); + expect(state.getCookieName()).toMatchSnapshot(); + expect(state.getCookieValue()).toMatchSnapshot(); + }); + + test('1: Authenticate successfully using Amster credentials', async () => { + const privateKey = process.env.FRODO_AMSTER_PRIVATE_KEY || fs.readFileSync( + path.resolve( + __dirname, + '../test/mocks/AuthenticateOps/pkcs8.pem' + ), + 'utf8' + ); + state.setDeploymentType(undefined); + state.setAmsterPrivateKey(privateKey);; + const result = await AuthenticateOps.getTokens({ state }); + expect(result).toBeTruthy(); + expect(result.subject).toEqual("user " + state.getUsername()); + expect(result.userSessionToken.tokenId).toBeTruthy(); + expect(result.userSessionToken.expires).toBeTruthy(); + expect(result).toMatchSnapshot({ + subject: expect.any(String), + }); + expect(state.getDeploymentType()).toEqual(Constants.CLASSIC_DEPLOYMENT_TYPE_KEY); + expect(state.getAmsterPrivateKey()).toEqual(privateKey); + expect(state.getAuthenticationService()).toEqual(Constants.DEFAULT_AMSTER_SERVICE); + expect(state.getUsername()).toEqual(Constants.DEFAULT_CLASSIC_USERNAME); + expect(state.getCookieName()).toBeTruthy(); + expect(state.getCookieValue()).toBeTruthy(); + expect(state.getCookieName()).toMatchSnapshot(); + expect(state.getCookieValue()).toMatchSnapshot(); + }); - test.skip('1: Authenticate successfully as user', async () => { - state.setHost( - process.env.FRODO_HOST || 'https://openam-frodo-dev.forgeblocks.com/am' - ); - state.setRealm(process.env.FRODO_REALM || 'alpha'); - state.setUsername(process.env.FRODO_USERNAME || 'mockUser'); - state.setPassword(process.env.FRODO_PASSWORD || 'mockPassword'); - const result = await AuthenticateOps.getTokens({ state }); - expect(result).toBe(true); - expect(state.getDeploymentType()).toEqual('cloud'); - expect(state.getCookieName()).toBeTruthy(); - expect(state.getCookieValue()).toBeTruthy(); - expect(state.getBearerToken()).toBeTruthy(); - expect(state.getCookieName()).toMatchSnapshot(); - expect(state.getCookieValue()).toMatchSnapshot(); - expect(state.getBearerToken()).toMatchSnapshot(); + test('2: Authenticate successfully using alternative Amster subject and service', async () => { + const privateKey = process.env.FRODO_AMSTER_PRIVATE_KEY || fs.readFileSync( + path.resolve( + __dirname, + '../test/mocks/AuthenticateOps/pkcs8.pem' + ), + 'utf8' + ); + const authenticationService = process.env.FRODO_AUTHENTICATION_SERVICE || 'MockAmsterService'; + const username = process.env.FRODO_USERNAME || 'MockUser'; + state.setAuthenticationService(authenticationService); + state.setUsername(username); + state.setDeploymentType(undefined); + state.setAmsterPrivateKey(privateKey); + const result = await AuthenticateOps.getTokens({ state }); + expect(result).toBeTruthy(); + expect(result.subject).toEqual("user " + state.getUsername()); + expect(result.userSessionToken.tokenId).toBeTruthy(); + expect(result.userSessionToken.expires).toBeTruthy(); + expect(result).toMatchSnapshot({ + subject: expect.any(String) + }); + expect(state.getDeploymentType()).toEqual(Constants.CLASSIC_DEPLOYMENT_TYPE_KEY); + expect(state.getAmsterPrivateKey()).toEqual(privateKey); + expect(state.getAuthenticationService()).toEqual(authenticationService); + expect(state.getUsername()).toEqual(username); + expect(state.getCookieName()).toBeTruthy(); + expect(state.getCookieValue()).toBeTruthy(); + expect(state.getCookieName()).toMatchSnapshot(); + expect(state.getCookieValue()).toMatchSnapshot(); + }); + }); }); - }); + } }); diff --git a/src/ops/AuthenticateOps.ts b/src/ops/AuthenticateOps.ts index e6cbeb861..280b659cf 100644 --- a/src/ops/AuthenticateOps.ts +++ b/src/ops/AuthenticateOps.ts @@ -1,15 +1,26 @@ +import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; import { createHash, randomBytes } from 'crypto'; +import jose from 'node-jose'; +import sshpk from 'sshpk'; import url from 'url'; import { v4 } from 'uuid'; -import { step } from '../api/AuthenticateApi'; +import { + AuthenticateStep, + AuthenticateSuccessResponse, + step, +} from '../api/AuthenticateApi'; import { getServerInfo, getServerVersionInfo } from '../api/ServerInfoApi'; import Constants from '../shared/Constants'; import { State } from '../shared/State'; import { encodeBase64Url } from '../utils/Base64Utils'; import { debugMessage, verboseMessage } from '../utils/Console'; import { isValidUrl, parseUrl } from '../utils/ExportImportUtils'; -import { CallbackHandler } from './CallbackOps'; +import { + CallbackHandler, + fillCallbacks, + getCallbackValue, +} from './CallbackOps'; import { readServiceAccountScopes } from './cloud/EnvServiceAccountScopesOps'; import { getServiceAccount, @@ -43,7 +54,7 @@ import { export type Authenticate = { /** * Get tokens and store them in State - * @param {boolean} forceLoginAsUser true to force login as user even if a service account is available (default: false) + * @param {boolean} forceLoginAsUser true to force login as user even if a service account or Amster account is available (default: false) * @param {boolean} autoRefresh true to automatically refresh tokens before they expire (default: true) * @param {string[]} types Array of supported deployment types. The function will throw an error if an unsupported type is detected (default: ['classic', 'cloud', 'forgeops']) * @param {CallbackHandler} callbackHandler function allowing the library to collect responses from the user through callbacks @@ -147,6 +158,17 @@ const serviceAccountDefaultScopes = SERVICE_ACCOUNT_DEFAULT_SCOPES.join(' '); const fidcClientId = 'idmAdminClient'; const forgeopsClientId = 'idm-admin-ui'; + +export type UserSessionMetaType = AuthenticateSuccessResponse & { + expires: number; + from_cache?: boolean; +}; +type StepHandler = (step: AuthenticateStep) => Promise; +type MFAResult = { + factor: string; + supported: boolean; +}; + let adminClientId = fidcClientId; /** @@ -165,118 +187,94 @@ async function determineCookieName(state: State): Promise { /** * Helper function to determine if this is a setup mfa prompt in the ID Cloud tenant admin login journey - * @param {Object} payload response from the previous authentication journey step + * @param {AuthenticateStep} payload response from the previous authentication journey step * @param {State} state library state - * @returns {Object} an object indicating if 2fa is required and the original payload + * @returns {MFAResult} an object indicating if 2fa is required */ function checkAndHandle2FA({ payload, otpCallbackHandler, state, }: { - payload; + payload: AuthenticateStep; otpCallbackHandler: CallbackHandler; state: State; -}) { +}): MFAResult { debugMessage({ message: `AuthenticateOps.checkAndHandle2FA: start`, state }); - // let skippable = false; - if ('callbacks' in payload) { - for (let callback of payload.callbacks) { - // select localAuthentication if Admin Federation is enabled - if (callback.type === 'SelectIdPCallback') { - debugMessage({ - message: `AuthenticateOps.checkAndHandle2FA: Admin federation enabled. Allowed providers:`, - state, - }); - let localAuth = false; - for (const value of callback.output[0].value) { - debugMessage({ message: `${value.provider}`, state }); - if (value.provider === 'localAuthentication') { - localAuth = true; - } - } - if (localAuth) { - debugMessage({ message: `local auth allowed`, state }); - callback.input[0].value = 'localAuthentication'; - } else { - debugMessage({ message: `local auth NOT allowed`, state }); + for (let callback of payload.callbacks) { + // select localAuthentication if Admin Federation is enabled + if (callback.type === 'SelectIdPCallback') { + debugMessage({ + message: `AuthenticateOps.checkAndHandle2FA: Admin federation enabled. Allowed providers:`, + state, + }); + let localAuth = false; + for (const value of callback.output[0].value) { + debugMessage({ message: `${value.provider}`, state }); + if (value.provider === 'localAuthentication') { + localAuth = true; } } - if (callback.type === 'HiddenValueCallback') { - if (callback.input[0].value.includes('skip')) { - // skippable = true; - callback.input[0].value = 'Skip'; - // debugMessage( - // `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=true]` - // ); - // return { - // nextStep: true, - // need2fa: true, - // factor: 'None', - // supported: true, - // payload, - // }; - } - if (callback.input[0].value.includes('webAuthnOutcome')) { - // webauthn!!! - debugMessage({ - message: `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, unsupported factor: webauthn]`, - state, - }); - return { - nextStep: false, - need2fa: true, - factor: 'WebAuthN', - supported: false, - payload, - }; - } + if (localAuth) { + debugMessage({ message: `local auth allowed`, state }); + callback.input[0].value = 'localAuthentication'; + } else { + debugMessage({ message: `local auth NOT allowed`, state }); } - if (callback.type === 'NameCallback') { - if (callback.output[0].value.includes('code')) { - // skippable = false; - debugMessage({ - message: `AuthenticateOps.checkAndHandle2FA: need2fa=true, skippable=false`, - state, - }); - if (!otpCallbackHandler) - throw new FrodoError( - `2fa required but no otpCallback function provided.` - ); - callback = otpCallbackHandler(callback); - debugMessage({ - message: `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=false, factor=Code]`, - state, - }); - return { - nextStep: true, - need2fa: true, - factor: 'Code', - supported: true, - payload, - }; - } else { - // answer callback - callback.input[0].value = state.getUsername(); - } + } + if (callback.type === 'HiddenValueCallback') { + if (callback.input[0].value.includes('skip')) { + // skippable = true; + callback.input[0].value = 'Skip'; + // debugMessage( + // `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=true]` + // ); + // return { + // factor: 'None', + // supported: true, + // }; } - if (callback.type === 'PasswordCallback') { + if (callback.input[0].value.includes('webAuthnOutcome')) { + // webauthn!!! + debugMessage({ + message: `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, unsupported factor: webauthn]`, + state, + }); + return { + factor: 'WebAuthN', + supported: false, + }; + } + } + if (callback.type === 'NameCallback') { + if (callback.output[0].value.includes('code')) { + // skippable = false; + debugMessage({ + message: `AuthenticateOps.checkAndHandle2FA: need2fa=true, skippable=false`, + state, + }); + if (!otpCallbackHandler) + throw new FrodoError( + `2fa required but no otpCallback function provided.` + ); + callback = otpCallbackHandler(callback); + debugMessage({ + message: `AuthenticateOps.checkAndHandle2FA: end [need2fa=true, skippable=false, factor=Code]`, + state, + }); + return { + factor: 'Code', + supported: true, + }; + } else { // answer callback - callback.input[0].value = state.getPassword(); + callback.input[0].value = state.getUsername(); } } - debugMessage({ - message: `AuthenticateOps.checkAndHandle2FA: end [need2fa=false]`, - state, - }); - // debugMessage(payload); - return { - nextStep: true, - need2fa: false, - factor: 'None', - supported: true, - payload, - }; + if (callback.type === 'PasswordCallback') { + // answer callback + callback.input[0].value = state.getPassword(); + } } debugMessage({ message: `AuthenticateOps.checkAndHandle2FA: end [need2fa=false]`, @@ -284,11 +282,8 @@ function checkAndHandle2FA({ }); // debugMessage(payload); return { - nextStep: false, - need2fa: false, factor: 'None', supported: true, - payload, }; } @@ -441,91 +436,97 @@ function getSemanticVersion(versionInfo) { throw new Error('Cannot extract semantic version from version info object.'); } -export type UserSessionMetaType = { - tokenId: string; - successUrl: string; - realm: string; - expires: number; - from_cache?: boolean; -}; - /** * Helper function to authenticate and obtain and store session cookie + * @param {StepHandler} stepHandler function to handle any intermediate authentication step * @param {State} state library state * @returns {string} Session token or null */ async function getFreshUserSessionToken({ - otpCallbackHandler, + stepHandler, state, }: { - otpCallbackHandler: CallbackHandler; + stepHandler: StepHandler; state: State; }): Promise { debugMessage({ message: `AuthenticateOps.getFreshUserSessionToken: start`, state, }); - const config = { - headers: { + // Username and password headers are only sent in the first request, assuming they both exist in the state + const config: AxiosRequestConfig = {}; + if (state.getUsername() && state.getPassword()) { + config.headers = { 'X-OpenAM-Username': state.getUsername(), 'X-OpenAM-Password': state.getPassword(), - }, - }; - let response = await step({ body: {}, config, state }); - - let skip2FA = null; - let steps = 0; - const maxSteps = 3; - do { - skip2FA = checkAndHandle2FA({ - payload: response, - otpCallbackHandler: otpCallbackHandler, - state, - }); - - // throw exception if 2fa required but factor not supported by frodo (e.g. WebAuthN) - if (!skip2FA.supported) { - throw new Error(`Unsupported 2FA factor: ${skip2FA.factor}`); - } - - if (skip2FA.nextStep) { - steps++; - response = await step({ body: skip2FA.payload, state }); - } - - if ('tokenId' in response) { - response['from_cache'] = false; - // get session expiration - const sessionInfo = await getSessionInfo({ - tokenId: response['tokenId'], - state, - }); - response['expires'] = Date.parse(sessionInfo.maxIdleExpirationTime); - debugMessage({ - message: `AuthenticateOps.getFreshUserSessionToken: end [tokenId=${response['tokenId']}]`, - state, - }); - debugMessage({ - message: response, + }; + } + try { + let currentStep = null; + // eslint-disable-next-line no-constant-condition + while (true) { + const response = await step({ + body: currentStep || {}, + config: currentStep ? {} : config, state, }); - return response as UserSessionMetaType; + // Handle success response + if ('tokenId' in response) { + // get session expiration + const { maxIdleExpirationTime } = await getSessionInfo({ + tokenId: response.tokenId, + state, + }); + const expires = Date.parse(maxIdleExpirationTime); + debugMessage({ + message: `AuthenticateOps.getFreshUserSessionToken: end [tokenId=${response.tokenId}]`, + state, + }); + debugMessage({ + message: response, + state, + }); + return { + ...response, + from_cache: false, + expires, + }; + } + // Handle error response (error responses should be thrown and caught already, but just in case they aren't) + if ('code' in response) { + throw new AxiosError( + response.message, + response.code, + undefined, + undefined, + { data: response } as AxiosResponse + ); + } + // Handle step response + if (!stepHandler) { + throw new FrodoError( + `No step handler function provided for user authentication.` + ); + } + currentStep = await stepHandler(response); } - } while (skip2FA.nextStep && steps < maxSteps); - debugMessage({ - message: `AuthenticateOps.getFreshUserSessionToken: end [no session]`, - state, - }); - return null; + } catch (e) { + debugMessage({ + message: `AuthenticateOps.getFreshUserSessionToken: end [no session]`, + state, + }); + throw new FrodoError('Error authenticating user', e); + } } /** * Helper function to obtain user session token + * @param {StepHandler} stepHandler function to handle any intermediate authentication step * @param {State} state library state * @returns {Promise} session token or null */ async function getUserSessionToken( - otpCallback: CallbackHandler, + stepHandler: StepHandler, state: State ): Promise { debugMessage({ @@ -550,7 +551,7 @@ async function getUserSessionToken( } if (!token) { token = await getFreshUserSessionToken({ - otpCallbackHandler: otpCallback, + stepHandler, state, }); token.from_cache = false; @@ -984,7 +985,7 @@ async function getLoggedInSubject(state: State): Promise { /** * Helper method to set, reset, or cancel timer to auto refresh tokens - * @param {boolean} forceLoginAsUser true to force login as user even if a service account is available (default: false) + * @param {boolean} forceLoginAsUser true to force login as user even if a service account or Amster account is available (default: false) * @param {boolean} autoRefresh true to automatically refresh tokens before they expire (default: true) * @param {State} state library state */ @@ -1042,6 +1043,44 @@ function scheduleAutoRefresh( } } +/** + * Helper to authenticate a user + * @param usingConnectionProfile True if using connection profiles + * @param {string[]} types Array of supported deployment types. The function will throw an error if an unsupported type is detected (default: ['classic', 'cloud', 'forgeops']) + * @param stepHandler function to handle any authentication steps + * @param state library state + */ +async function authenticateUser( + usingConnectionProfile: boolean, + types: string[], + stepHandler: StepHandler, + state: State +) { + const token = await getUserSessionToken(stepHandler, state); + if (token) state.setUserSessionTokenMeta(token); + if (usingConnectionProfile && !token.from_cache) { + saveConnectionProfile({ host: state.getHost(), state }); + } + await determineDeploymentTypeAndDefaultRealmAndVersion(state); + + // fail if deployment type not applicable + if (state.getDeploymentType() && !types.includes(state.getDeploymentType())) { + throw new FrodoError( + `Unsupported deployment type '${state.getDeploymentType()}'` + ); + } + + if ( + state.getCookieValue() && + // !state.getBearerToken() && + (state.getDeploymentType() === Constants.CLOUD_DEPLOYMENT_TYPE_KEY || + state.getDeploymentType() === Constants.FORGEOPS_DEPLOYMENT_TYPE_KEY) + ) { + const accessToken = await getUserBearerToken(state); + if (accessToken) state.setBearerTokenMeta(accessToken); + } +} + export type Tokens = { bearerToken?: AccessTokenMetaType; userSessionToken?: UserSessionMetaType; @@ -1052,8 +1091,10 @@ export type Tokens = { /** * Get tokens - * @param {boolean} forceLoginAsUser true to force login as user even if a service account is available (default: false) + * @param {boolean} forceLoginAsUser true to force login as user even if a service account or Amster account is available (default: false) * @param {boolean} autoRefresh true to automatically refresh tokens before they expire (default: true) + * @param {string[]} types Array of supported deployment types. The function will throw an error if an unsupported type is detected (default: ['classic', 'cloud', 'forgeops']) + * @param {CallbackHandler} callbackHandler function allowing the library to collect responses from the user through callbacks * @param {State} state library state * @returns {Promise} object containing the tokens */ @@ -1084,7 +1125,8 @@ export async function getTokens({ state.getUsername() == null && state.getPassword() == null && !state.getServiceAccountId() && - !state.getServiceAccountJwk() + !state.getServiceAccountJwk() && + !state.getAmsterPrivateKey() ) { usingConnectionProfile = await loadConnectionProfile({ state }); @@ -1154,38 +1196,92 @@ export async function getTokens({ throw new FrodoError(`Service account login error`, saErr); } } - // use user account to login + // use Amster credentials to login? + else if ( + !forceLoginAsUser && + (state.getDeploymentType() === Constants.CLASSIC_DEPLOYMENT_TYPE_KEY || + state.getDeploymentType() === undefined) && + state.getAmsterPrivateKey() + ) { + if (!state.getAuthenticationService()) { + state.setAuthenticationService(Constants.DEFAULT_AMSTER_SERVICE); + } + if (!state.getUsername()) { + state.setUsername(Constants.DEFAULT_CLASSIC_USERNAME); + } + debugMessage({ + message: `AuthenticateOps.getTokens: Authenticating with Amster credentials using the ${state.getAuthenticationService()} authentication service`, + state, + }); + await authenticateUser( + usingConnectionProfile, + types, + async (currentStep: AuthenticateStep) => { + if (currentStep.callbacks.length !== 1) { + throw new FrodoError( + `Expected a single HiddenValueCallback for Amster authentication, but got ${currentStep.callbacks.length} callbacks` + ); + } + const callback = currentStep.callbacks[0]; + if (callback.type !== 'HiddenValueCallback') { + throw new FrodoError( + `Expected a single HiddenValueCallback for Amster authentication, but got a ${callback.type}` + ); + } + const key = await jose.JWK.asKey(state.getAmsterPrivateKey(), 'pem'); + const payload = { + sub: state.getUsername(), + nonce: getCallbackValue('value', callback.output), + }; + const header = { + typ: 'jwt', + kid: sshpk + .parsePrivateKey(state.getAmsterPrivateKey()) + .toPublic() + .toString('ssh') + .split(' ')[1], + }; + const jwt = await createSignedJwtToken(payload, key, header); + return fillCallbacks({ + step: currentStep, + map: { + IDToken1: jwt.toString(), + }, + }); + }, + state + ); + } + // use user account to login? else if (state.getUsername() && state.getPassword()) { debugMessage({ message: `AuthenticateOps.getTokens: Authenticating with user account ${state.getUsername()}`, state, }); - const token = await getUserSessionToken(callbackHandler, state); - if (token) state.setUserSessionTokenMeta(token); - if (usingConnectionProfile && !token.from_cache) { - saveConnectionProfile({ host: state.getHost(), state }); - } - await determineDeploymentTypeAndDefaultRealmAndVersion(state); - - // fail if deployment type not applicable - if ( - state.getDeploymentType() && - !types.includes(state.getDeploymentType()) - ) { - throw new FrodoError( - `Unsupported deployment type '${state.getDeploymentType()}'` - ); - } + const maxSteps = 3; + let steps = 0; + await authenticateUser( + usingConnectionProfile, + types, + async (currentStep: AuthenticateStep) => { + // if max steps is reached, throw an error + if (++steps > maxSteps) { + throw new FrodoError('Too many 2FA attempts'); + } + const skip2FA = checkAndHandle2FA({ + payload: currentStep, + otpCallbackHandler: callbackHandler, + state, + }); - if ( - state.getCookieValue() && - // !state.getBearerToken() && - (state.getDeploymentType() === Constants.CLOUD_DEPLOYMENT_TYPE_KEY || - state.getDeploymentType() === Constants.FORGEOPS_DEPLOYMENT_TYPE_KEY) - ) { - const accessToken = await getUserBearerToken(state); - if (accessToken) state.setBearerTokenMeta(accessToken); - } + // throw exception if 2fa required but factor not supported by frodo (e.g. WebAuthN) + if (!skip2FA.supported) { + throw new Error(`Unsupported 2FA factor: ${skip2FA.factor}`); + } + return currentStep; + }, + state + ); } // incomplete or no credentials else { diff --git a/src/ops/CallbackOps.ts b/src/ops/CallbackOps.ts index 7b7ff44f2..426ca9164 100644 --- a/src/ops/CallbackOps.ts +++ b/src/ops/CallbackOps.ts @@ -1,7 +1,12 @@ +import { AuthenticateStep } from '../api/AuthenticateApi'; +import { cloneDeep } from '../utils/JsonUtils'; + export type CallbackType = | 'NameCallback' | 'PasswordCallback' - | 'TextInputCallback'; + | 'TextInputCallback' + | 'HiddenValueCallback' + | 'SelectIdPCallback'; export type CallbackKeyValuePair = { name: string; value: any; @@ -12,3 +17,44 @@ export type Callback = { input: CallbackKeyValuePair[]; }; export type CallbackHandler = (callback: Callback) => Callback; + +/** + * Fill callbacks from a map + * @param {AuthenticateStep} step authenticate step json response from a call to /authenticate + * @param {{ [k: string]: string | number | boolean | string[] }} map name/value map + * @returns filled authenticate step body so it can be used as input to another call to /authenticate + */ +export function fillCallbacks({ + step, + map, +}: { + step: AuthenticateStep; + map: { [k: string]: string | number | boolean | string[] }; +}): AuthenticateStep { + const stepCopy = cloneDeep(step) as AuthenticateStep; + for (const callback of stepCopy.callbacks) { + for (const [n, v] of Object.entries(map)) { + setCallbackValue(n, v, callback.input); + } + } + return stepCopy; +} + +export function getCallbackValue( + name: string, + pairs: CallbackKeyValuePair[] +): any | undefined { + const pair = pairs.find((p) => p.name === name); + return pair && pair.value; +} + +export function setCallbackValue( + name: string, + value: any, + pairs: CallbackKeyValuePair[] +): boolean { + const pair = pairs.find((p) => p.name === name); + if (!pair) return false; + pair.value = value; + return true; +} diff --git a/src/ops/ConnectionProfileOps.ts b/src/ops/ConnectionProfileOps.ts index 7196a2149..ef38cd061 100644 --- a/src/ops/ConnectionProfileOps.ts +++ b/src/ops/ConnectionProfileOps.ts @@ -145,6 +145,7 @@ export interface SecureConnectionProfileInterface { encodedSvcacctJwk?: string | null; svcacctName?: string | null; svcacctScope?: string | null; + encodedAmsterPrivateKey?: string | null; } export interface ConnectionProfileInterface { @@ -164,6 +165,7 @@ export interface ConnectionProfileInterface { svcacctJwk?: JwkRsa; svcacctName?: string | null; svcacctScope?: string | null; + amsterPrivateKey?: string | null; } export interface ConnectionsFileInterface { @@ -324,6 +326,14 @@ export async function initConnectionProfiles({ state }: { state: State }) { await dataProtection.encrypt(connectionsData[conn]['svcacctJwk']); delete connectionsData[conn]['svcacctJwk']; } + if (connectionsData[conn]['amsterPrivateKey']) { + convert = true; + connectionsData[conn].encodedAmsterPrivateKey = + await dataProtection.encrypt( + connectionsData[conn]['amsterPrivateKey'] + ); + delete connectionsData[conn]['amsterPrivateKey']; + } } if (convert) { fs.writeFileSync( @@ -409,6 +419,9 @@ export async function getConnectionProfileByHost({ ? await dataProtection.decrypt(profiles[0].encodedSvcacctJwk) : null, svcacctScope: profiles[0].svcacctScope ? profiles[0].svcacctScope : null, + amsterPrivateKey: profiles[0].encodedAmsterPrivateKey + ? await dataProtection.decrypt(profiles[0].encodedAmsterPrivateKey) + : null, }; } @@ -456,6 +469,7 @@ export async function loadConnectionProfileByHost({ state.setServiceAccountId(conn.svcacctId); state.setServiceAccountJwk(conn.svcacctJwk); state.setServiceAccountScope(conn.svcacctScope); + state.setAmsterPrivateKey(conn.amsterPrivateKey); return true; } @@ -609,6 +623,13 @@ export async function saveConnectionProfile({ }); } + // Amster account + if (state.getAmsterPrivateKey()) { + profile.encodedAmsterPrivateKey = await dataProtection.encrypt( + state.getAmsterPrivateKey() + ); + } + // advanced settings if (state.getAuthenticationService()) { profile.authenticationService = state.getAuthenticationService(); diff --git a/src/ops/JoseOps.ts b/src/ops/JoseOps.ts index 525268456..cbd766984 100644 --- a/src/ops/JoseOps.ts +++ b/src/ops/JoseOps.ts @@ -87,14 +87,15 @@ export function createJwks(...keys: JwkInterface[]): JwksInterface { export async function createSignedJwtToken( payload: string | object, - jwkJson: JwkRsa + jwkJson: JwkRsa, + header: object = {} ) { const key = await jose.JWK.asKey(jwkJson); if (typeof payload === 'object') { payload = JSON.stringify(payload); } const jwt = await jose.JWS.createSign( - { alg: 'RS256', compact: true, fields: {} }, + { alg: 'RS256', compact: true, fields: header }, // https://github.com/cisco/node-jose/issues/253 { key, reference: false } ) diff --git a/src/shared/Constants.ts b/src/shared/Constants.ts index 441406fdf..7b1d7e2fb 100644 --- a/src/shared/Constants.ts +++ b/src/shared/Constants.ts @@ -28,6 +28,8 @@ const DEPLOYMENT_TYPE_REALM_MAP = { [CLOUD_DEPLOYMENT_TYPE_KEY]: 'alpha', [FORGEOPS_DEPLOYMENT_TYPE_KEY]: '/', }; +const DEFAULT_AMSTER_SERVICE = 'amsterService'; +const DEFAULT_CLASSIC_USERNAME = 'amadmin'; const FRODO_METADATA_ID = 'frodo'; const FRODO_CONNECTION_PROFILES_PATH_KEY = 'FRODO_CONNECTION_PROFILES_PATH'; const FRODO_MASTER_KEY_PATH_KEY = 'FRODO_MASTER_KEY_PATH'; @@ -124,6 +126,8 @@ export default { FORGEOPS_DEPLOYMENT_TYPE_KEY, DEPLOYMENT_TYPES, DEPLOYMENT_TYPE_REALM_MAP, + DEFAULT_AMSTER_SERVICE, + DEFAULT_CLASSIC_USERNAME, FRODO_METADATA_ID, FRODO_CONNECTION_PROFILES_PATH_KEY, FRODO_MASTER_KEY_PATH_KEY, diff --git a/src/shared/State.test.ts b/src/shared/State.test.ts index 141275bfd..ef42cbe9d 100644 --- a/src/shared/State.test.ts +++ b/src/shared/State.test.ts @@ -8,6 +8,12 @@ */ import { state } from '../index'; import { JwkRsa } from '../ops/JoseOps'; +import Constants from './Constants'; +import fs from 'fs' +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); describe('State', () => { const host = 'https://openam-frodo-dev.forgeblocks.com/am'; @@ -141,10 +147,47 @@ describe('State', () => { // setCookieValue, // getCookieValue, - // setAuthenticationHeaderOverrides, - // getAuthenticationHeaderOverrides, - // setAuthenticationService, - // getAuthenticationService, + describe('getAuthenticationHeaderOverrides()/setAuthenticationHeaderOverrides()', () => { + const override: Record = { + host: hostEnv, + ["User-Agent"]: 'frodoTestAgent', + Connection: 'keep-alive' + }; + + test('0: Method getAuthenticationHeaderOverrides is implemented', () => { + expect(state.getAuthenticationHeaderOverrides).toBeDefined(); + }); + + test('1: Method setAuthenticationHeaderOverrides is implemented', () => { + expect(state.setAuthenticationHeaderOverrides).toBeDefined(); + }); + + test("2: Authentication service value should be empty if it hasn't been set before or defined if set explicitly", () => { + expect(state.getAuthenticationHeaderOverrides()).toMatchObject({}); + state.setAuthenticationHeaderOverrides(override); + expect(state.getAuthenticationHeaderOverrides()).toMatchObject(override); + }); + }); + describe('getAuthenticationService()/setAuthenticationService()', () => { + const loginService = 'Login'; + + test('0: Method getAuthenticationService is implemented', () => { + expect(state.getAuthenticationService).toBeDefined(); + }); + + test('1: Method setAuthenticationService is implemented', () => { + expect(state.setAuthenticationService).toBeDefined(); + }); + + test("2: Authentication service value should be undefined if it hasn't been set before or defined if FRODO_AUTHENTICATION_SERVICE env variable has been set or if set explicitly", () => { + delete process.env.FRODO_AUTHENTICATION_SERVICE; + expect(state.getAuthenticationService()).toBeUndefined(); + process.env.FRODO_AUTHENTICATION_SERVICE = Constants.DEFAULT_AMSTER_SERVICE; + expect(state.getAuthenticationService()).toEqual(Constants.DEFAULT_AMSTER_SERVICE); + state.setAuthenticationService(loginService); + expect(state.getAuthenticationService()).toEqual(loginService); + }); + }); describe('getServiceAccountId()/setServiceAccountId()', () => { const saId = '0de8d0d8-e423-41e8-9034-73883af90917'; @@ -212,6 +255,59 @@ describe('State', () => { }); }); + describe('getAmsterPrivateKey()/setAmsterPrivateKey()', () => { + const privateKey1 = fs.readFileSync( + path.resolve( + __dirname, + '../test/mocks/CryptoUtils/pkcs8Rsa.pem' + ), + 'utf8' + ); + const privateKey2 = fs.readFileSync( + path.resolve( + __dirname, + '../test/mocks/CryptoUtils/pkcs1Rsa.pem' + ), + 'utf8' + ); + const privateKey3 = fs.readFileSync( + path.resolve( + __dirname, + '../test/mocks/CryptoUtils/pkcs8Ed25519Enc.pem' + ), + 'utf8' + ); + test('0: Method setAmsterPrivateKey is implemented', () => { + expect(state.setAmsterPrivateKey).toBeDefined(); + }); + + test('1: Method getAmsterPrivateKey is implemented', () => { + expect(state.getAmsterPrivateKey).toBeDefined(); + }); + + test("2: Amster private key value should be undefined if it hasn't been set before or defined if FRODO_AMSTER_PRIVATE_KEY env variable has been set or if set explicitly", () => { + delete process.env.FRODO_AMSTER_PRIVATE_KEY; + expect(state.getAmsterPrivateKey()).toBeUndefined(); + process.env.FRODO_AMSTER_PRIVATE_KEY = privateKey1; + // Tests that privateKey1, in PKCS#8 format, is still in PKCS#8 format after being parsed + expect(state.getAmsterPrivateKey()).toEqual(privateKey1); + state.setAmsterPrivateKey(privateKey2); + // Tests that privateKey2, in PKCS#1 format, is still in PKCS#1 format since we set it directly + expect(state.getAmsterPrivateKey()).toEqual(privateKey2); + }); + + test("3: Amster private key value should be undefined and throw error if FRODO_AMSTER_PRIVATE_KEY is encrypted and FRODO_AMSTER_PASSPHRASE is not provided, or defined if FRODO_AMSTER_PASSPHRASE is provided.", () => { + delete process.env.FRODO_AMSTER_PRIVATE_KEY; + delete process.env.FRODO_AMSTER_PASSPHRASE; + state.setAmsterPrivateKey(undefined); + process.env.FRODO_AMSTER_PRIVATE_KEY = privateKey3; + expect(state.getAmsterPrivateKey).toThrow("The PEM format key (unnamed) is encrypted (password-protected), and no passphrase was provided in `options`"); + process.env.FRODO_AMSTER_PASSPHRASE = 'test'; + // Should be in PKCS#8 format now instead of OpenSSH + expect(state.getAmsterPrivateKey()).not.toEqual(privateKey3); + }); + }); + // setUseBearerTokenForAmApis, // getUseBearerTokenForAmApis, // setBearerToken, diff --git a/src/shared/State.ts b/src/shared/State.ts index e420f0642..163adb651 100644 --- a/src/shared/State.ts +++ b/src/shared/State.ts @@ -10,6 +10,7 @@ import { ProgressIndicatorStatusType, ProgressIndicatorType, } from '../utils/Console'; +import { convertPrivateKeyToPem } from '../utils/CryptoUtils'; import { cloneDeep } from '../utils/JsonUtils'; import { getPackageVersion } from './Version'; @@ -74,6 +75,8 @@ export type State = { getServiceAccountJwk(): JwkRsa; setServiceAccountScope(scope: string): void; getServiceAccountScope(): string; + setAmsterPrivateKey(key: string): void; + getAmsterPrivateKey(): string; setUseBearerTokenForAmApis(useBearerTokenForAmApis: boolean): void; getUseBearerTokenForAmApis(): boolean; setBearerTokenMeta(token: AccessTokenMetaType): void; @@ -313,6 +316,19 @@ export default (initialState: StateInterface): State => { return state.serviceAccountScope; }, + setAmsterPrivateKey(key: string) { + state.amsterPrivateKey = key; + }, + getAmsterPrivateKey(): string { + if (!state.amsterPrivateKey && process.env.FRODO_AMSTER_PRIVATE_KEY) { + state.amsterPrivateKey = convertPrivateKeyToPem({ + key: process.env.FRODO_AMSTER_PRIVATE_KEY, + passphrase: process.env.FRODO_AMSTER_PASSPHRASE || undefined, + }); + } + return state.amsterPrivateKey || undefined; + }, + setUseBearerTokenForAmApis(useBearerTokenForAmApis: boolean) { state.useBearerTokenForAmApis = useBearerTokenForAmApis; }, @@ -538,6 +554,8 @@ export interface StateInterface { serviceAccountId?: string; serviceAccountJwk?: JwkRsa; serviceAccountScope?: string; + // Amster settings + amsterPrivateKey?: string; // bearer token settings useBearerTokenForAmApis?: boolean; bearerToken?: AccessTokenMetaType; diff --git a/src/test/mock-recordings/AuthenticateOps_3073788200/Classic-Tests_743483830/getTokens_3422903202/0-Authenticate-successfully-as-user_4258934026/recording.har b/src/test/mock-recordings/AuthenticateOps_3073788200/Classic-Tests_743483830/getTokens_3422903202/0-Authenticate-successfully-as-user_4258934026/recording.har new file mode 100644 index 000000000..a436a8884 --- /dev/null +++ b/src/test/mock-recordings/AuthenticateOps_3073788200/Classic-Tests_743483830/getTokens_3422903202/0-Authenticate-successfully-as-user_4258934026/recording.har @@ -0,0 +1,816 @@ +{ + "log": { + "_recordingName": "AuthenticateOps/Classic Tests/getTokens()/0: Authenticate successfully as user", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.6" + }, + "entries": [ + { + "_id": "a92fbf6a95676ead13c4d7e1621eb0a1", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=1.1" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 386, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/serverinfo/*" + }, + "response": { + "bodySize": 589, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 589, + "text": "{\"_id\":\"*\",\"_rev\":\"2075994301\",\"domains\":[],\"protectedUserAttributes\":[\"telephoneNumber\",\"mail\"],\"cookieName\":\"iPlanetDirectoryPro\",\"secureCookie\":false,\"forgotPassword\":\"false\",\"forgotUsername\":\"false\",\"kbaEnabled\":\"false\",\"selfRegistration\":\"false\",\"lang\":\"en-US\",\"successfulUserRegistrationDestination\":\"default\",\"socialImplementations\":[],\"referralsEnabled\":\"false\",\"zeroPageLogin\":{\"enabled\":false,\"refererWhitelist\":[],\"allowedWithoutReferer\":true},\"realm\":\"/\",\"xuiUserSessionValidationEnabled\":true,\"fileBasedConfiguration\":false,\"userIdAttributes\":[],\"nodeDesignerXuiEnabled\":true}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "content-api-version", + "value": "resource=1.1" + }, + { + "name": "content-security-policy", + "value": "default-src 'none';frame-ancestors 'none';sandbox" + }, + { + "name": "cross-origin-opener-policy", + "value": "same-origin" + }, + { + "name": "cross-origin-resource-policy", + "value": "same-origin" + }, + { + "name": "etag", + "value": "\"2075994301\"" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "589" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:44 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 486, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:44.922Z", + "time": 73, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 73 + } + }, + { + "_id": "c2e553166020b8d50e82f777d9c117cc", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 2, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=2.0, protocol=1.0" + }, + { + "name": "x-openam-username", + "value": "amadmin" + }, + { + "name": "x-openam-password", + "value": "TriVir#1" + }, + { + "name": "content-length", + "value": "2" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 489, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{}" + }, + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/realms/root/authenticate" + }, + "response": { + "bodySize": 163, + "content": { + "mimeType": "application/json", + "size": 163, + "text": "{\"tokenId\":\"pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*\",\"successUrl\":\"/am/console\",\"realm\":\"/\"}" + }, + "cookies": [ + { + "httpOnly": true, + "name": "iPlanetDirectoryPro", + "path": "/", + "value": "pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "httpOnly": true, + "name": "amlbcookie", + "path": "/", + "value": "01" + } + ], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "private" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "iPlanetDirectoryPro=pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*; Path=/; HttpOnly" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "amlbcookie=01; Path=/; HttpOnly" + }, + { + "name": "content-api-version", + "value": "resource=2.1" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": "163" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 498, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.005Z", + "time": 21, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 21 + } + }, + { + "_id": "b729d22f437e3d001bc90ede6a93667d", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 124, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=4.0" + }, + { + "name": "content-length", + "value": "124" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 440, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\"tokenId\":\"pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*\"}" + }, + "queryString": [ + { + "name": "_action", + "value": "getSessionInfo" + } + ], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/realms/root/sessions/?_action=getSessionInfo" + }, + "response": { + "bodySize": 289, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 289, + "text": "{\"username\":\"amadmin\",\"universalId\":\"id=amadmin,ou=user,ou=am-config\",\"realm\":\"/\",\"latestAccessTime\":\"2025-07-31T15:05:45Z\",\"maxIdleExpirationTime\":\"2025-07-31T15:35:45Z\",\"maxSessionExpirationTime\":\"2025-07-31T17:05:44Z\",\"properties\":{\"AMCtxId\":\"86d527f2-3667-4c8e-bfd8-3bf1b9517d24-280\"}}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "private" + }, + { + "name": "content-api-version", + "value": "resource=4.0" + }, + { + "name": "content-security-policy", + "value": "default-src 'none';frame-ancestors 'none';sandbox" + }, + { + "name": "cross-origin-opener-policy", + "value": "same-origin" + }, + { + "name": "cross-origin-resource-policy", + "value": "same-origin" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "289" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 465, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.035Z", + "time": 6, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 6 + } + }, + { + "_id": "074c9dfd2413e8a532954fa6dbfed72b", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 363, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/x-www-form-urlencoded" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "protocol=2.1,resource=1.0" + }, + { + "name": "cookie", + "value": "iPlanetDirectoryPro=pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "iplanetdirectorypro", + "value": "pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "content-length", + "value": "363" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 710, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/x-www-form-urlencoded", + "params": [], + "text": "redirect_uri=http://openam-frodo-dev.classic.com:8080/platform/appAuthHelperRedirect.html&scope=openid&response_type=code&client_id=idmAdminClient&csrf=pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*&decision=allow&code_challenge=p5G8TYO-LCFfV6IYAlFlqS08DmxeewsAhr1eYIdiJcw&code_challenge_method=S256" + }, + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/oauth2/authorize" + }, + "response": { + "bodySize": 1849, + "content": { + "mimeType": "text/html;charset=UTF-8", + "size": 1849, + "text": "\n\n\n\n \n \n \n \n OAuth2 Error Page\n\n\n\n
Loading...
\n
\n \n \n\n\n" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "text/html;charset=UTF-8" + }, + { + "name": "content-length", + "value": "1849" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 224, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 400, + "statusText": "Bad Request" + }, + "startedDateTime": "2025-07-31T15:05:45.047Z", + "time": 10, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 10 + } + }, + { + "_id": "074c9dfd2413e8a532954fa6dbfed72b", + "_order": 1, + "cache": {}, + "request": { + "bodySize": 361, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/x-www-form-urlencoded" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "protocol=2.1,resource=1.0" + }, + { + "name": "cookie", + "value": "iPlanetDirectoryPro=pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "iplanetdirectorypro", + "value": "pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "content-length", + "value": "361" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 710, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/x-www-form-urlencoded", + "params": [], + "text": "redirect_uri=http://openam-frodo-dev.classic.com:8080/platform/appAuthHelperRedirect.html&scope=openid&response_type=code&client_id=idm-admin-ui&csrf=pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*&decision=allow&code_challenge=p5G8TYO-LCFfV6IYAlFlqS08DmxeewsAhr1eYIdiJcw&code_challenge_method=S256" + }, + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/oauth2/authorize" + }, + "response": { + "bodySize": 1849, + "content": { + "mimeType": "text/html;charset=UTF-8", + "size": 1849, + "text": "\n\n\n\n \n \n \n \n OAuth2 Error Page\n\n\n\n
Loading...
\n
\n \n \n\n\n" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "text/html;charset=UTF-8" + }, + { + "name": "content-length", + "value": "1849" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 224, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 400, + "statusText": "Bad Request" + }, + "startedDateTime": "2025-07-31T15:05:45.067Z", + "time": 8, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 8 + } + }, + { + "_id": "aa49f3ff76d93ac5b0dbc7d6f8a32b44", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=1.0" + }, + { + "name": "cookie", + "value": "iPlanetDirectoryPro=pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 532, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/serverinfo/version" + }, + "response": { + "bodySize": 257, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 257, + "text": "{\"_id\":\"version\",\"_rev\":\"-466575464\",\"version\":\"8.0.1\",\"fullVersion\":\"ForgeRock Access Management 8.0.1 Build b59bc0908346197b0c33afcb9e733d0400feeea1 (2025-April-15 11:37)\",\"revision\":\"b59bc0908346197b0c33afcb9e733d0400feeea1\",\"date\":\"2025-April-15 11:37\"}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "content-api-version", + "value": "resource=1.0" + }, + { + "name": "content-security-policy", + "value": "default-src 'none';frame-ancestors 'none';sandbox" + }, + { + "name": "cross-origin-opener-policy", + "value": "same-origin" + }, + { + "name": "cross-origin-resource-policy", + "value": "same-origin" + }, + { + "name": "etag", + "value": "\"-466575464\"" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "257" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 486, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.080Z", + "time": 8, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 8 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/src/test/mock-recordings/AuthenticateOps_3073788200/Classic-Tests_743483830/getTokens_3422903202/1-Authenticate-successfully-using-Amster-credentials_3101911984/recording.har b/src/test/mock-recordings/AuthenticateOps_3073788200/Classic-Tests_743483830/getTokens_3422903202/1-Authenticate-successfully-using-Amster-credentials_3101911984/recording.har new file mode 100644 index 000000000..c252e7afb --- /dev/null +++ b/src/test/mock-recordings/AuthenticateOps_3073788200/Classic-Tests_743483830/getTokens_3422903202/1-Authenticate-successfully-using-Amster-credentials_3101911984/recording.har @@ -0,0 +1,970 @@ +{ + "log": { + "_recordingName": "AuthenticateOps/Classic Tests/getTokens()/1: Authenticate successfully using Amster credentials", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.6" + }, + "entries": [ + { + "_id": "a92fbf6a95676ead13c4d7e1621eb0a1", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=1.1" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 386, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/serverinfo/*" + }, + "response": { + "bodySize": 589, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 589, + "text": "{\"_id\":\"*\",\"_rev\":\"2075994301\",\"domains\":[],\"protectedUserAttributes\":[\"telephoneNumber\",\"mail\"],\"cookieName\":\"iPlanetDirectoryPro\",\"secureCookie\":false,\"forgotPassword\":\"false\",\"forgotUsername\":\"false\",\"kbaEnabled\":\"false\",\"selfRegistration\":\"false\",\"lang\":\"en-US\",\"successfulUserRegistrationDestination\":\"default\",\"socialImplementations\":[],\"referralsEnabled\":\"false\",\"zeroPageLogin\":{\"enabled\":false,\"refererWhitelist\":[],\"allowedWithoutReferer\":true},\"realm\":\"/\",\"xuiUserSessionValidationEnabled\":true,\"fileBasedConfiguration\":false,\"userIdAttributes\":[],\"nodeDesignerXuiEnabled\":true}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "content-api-version", + "value": "resource=1.1" + }, + { + "name": "content-security-policy", + "value": "default-src 'none';frame-ancestors 'none';sandbox" + }, + { + "name": "cross-origin-opener-policy", + "value": "same-origin" + }, + { + "name": "cross-origin-resource-policy", + "value": "same-origin" + }, + { + "name": "etag", + "value": "\"2075994301\"" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "589" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 486, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.101Z", + "time": 7, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 7 + } + }, + { + "_id": "70a1e477f811afc968b0c7f14c7a840d", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 2, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=2.0, protocol=1.0" + }, + { + "name": "x-openam-username", + "value": "amadmin" + }, + { + "name": "x-openam-password", + "value": "TriVir#1" + }, + { + "name": "content-length", + "value": "2" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 540, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{}" + }, + "queryString": [ + { + "name": "authIndexType", + "value": "service" + }, + { + "name": "authIndexValue", + "value": "amsterService" + } + ], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/realms/root/authenticate?authIndexType=service&authIndexValue=amsterService" + }, + "response": { + "bodySize": 2470, + "content": { + "mimeType": "application/json", + "size": 2470, + "text": "{\"authId\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoSW5kZXhWYWx1ZSI6ImFtc3RlclNlcnZpY2UiLCJvdGsiOiI0Z2RqZ2NpczE5MXZwazRsOWZvM2RwN2F0ZiIsImF1dGhJbmRleFR5cGUiOiJzZXJ2aWNlIiwicmVhbG0iOiIvIiwic2Vzc2lvbklkIjoiKkFBSlRTUUFDTURFQUJIUjVjR1VBQ0VwWFZGOUJWVlJJQUFKVE1RQUEqZXlKMGVYQWlPaUpLVjFRaUxDSmpkSGtpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5aWGxLTUdWWVFXbFBhVXBMVmpGUmFVeERTbXhpYlUxcFQybEtRazFVU1RSUk1FcEVURlZvVkUxcVZUSkphWGRwV1ZkNGJrbHFiMmxhUjJ4NVNXNHdMaTVSTFhKSWQyRnBVVEJrVEZsNVZuZzNSbFp4V2kxQkxrNUJReTFxV1ZaWVRYaG9aRWRDYVc1aVpYWkpRVkpZVDBsQmExWnZUVWhhVVRGYVNEWnpWWGxDTm01eFlXMW9XRk01TmpCUExVWXdUak00UzA1bFdGbGZiVmN5VFdsaE4yWlpUVGg2TlhSYVFUWjZWRkJ0YkU4eFpqQTFjRlpXVkdsVVNGTk5aRFJMVEU1dWVuaFJUSFJhWmtoTGFXNHlPSFJZUTFacVNHMHpSbTFRUldWaVZVWkJSMmN0VURoYVRFZHRabUZ5WlZKaGQwOWFPRGxaV21sb2VrTkRSV1pJTjBGbFpqbHVSbmRUUVRScVgydFRTUzFFVERsT2FISXlPVFJmVERkTmRrTldiRmhPUm05d1EycDNiMkV6ZHpkRlNrRXlSaTFWV1MxSlFXRTNORFpSVVY5blUyTnpRbmhOVlhabFMwSmhRMnBYU1d0WWFGcEZVR2xFY2tVNFJpMXJUbWhmY2xGTU1XWlphREE1VFRaT1FuUkRNMk5UYkVocGJYVnJaRmcwZEZaQ09XcHNObEpSV1MxUFUzSnpWa2w0ZEZwblIydHZjWFoxYkhGRWVuaHRiRU5aTjNGQlJXVkVWa1ptY25CMlEyeGlaSEZ2VUU5WlQybGthVmxVTWkxUU16ZFNSME0wZVZkdlpIZFlOMnBDT1RWMVlsbEdRMnhIZWxKTmRYaHZiMGgxTXkxemFEUmhUbEl3YlhSNk1qQklWRzlDUjNKelNWTmljWEJpYjBGUmFteHpjbXhKYlVod01FaHZhVGx6VFU1cWVYRmFhbDlQTmpKT1R6bERPRXRvYm5GUlgzWnhOa0l3YlZobFdrc3dWa0U0Um1obVZtdFRObUYzY21aMVVXeGlSM3BvYjJOc01VeE1OMHhLWWxZMll6QldUbEpLZVVOaExURlZiMjlmV210SlpHd3RRMU0zVFdkdU9UTlRPV3cxVWpKMlIwazNRamROZDE4elQxZzFPVlZUYmw5TFJXZFNja3RGWlVwSFpuRm9WekV4VFRkRWRXSjRZVzFrWXkxdVpVOTFlbHBWTnpOa1QxaHBhMVpDUlU1UWJEZHRia1k1WnpoVWJHNXpTbmhGY21RemIxWllXWHBEZGpNMFpXTkhWMVJqZW5KSVIxUk9SbUZwVTJwRWFHOVZjMnR2VFhGSFpsZE5SMGxYZHpablVFZHhNRGRETm1ZMVZsZERWRmxMVlZsSWNXOXdjVWMwWmtGU01FSldkV3BHUVRCYVRHbENMVjg1WVVkTmRIVXdRbnBmYVdWWU1XUm1Va2c0TTBwNVJIZHNTRWhvVUZkeVduVlFjbXRaV21GTllrVlhWMk5VTUVWV2ExTXRSMUV4UVdrME1FWndaMlJhY1VjNE4yTlRRbGMyVEdkc1pqUjBjVFF6UWt4NlJXUm5NbGQzTFdsTVRGWkpXWFZHTUUxRFRHNUVNa3RKY1UxcFZrNHlNbUoyWDFWSFpqaENhMVJTZEhwaWIyZDBjelJWTUUxMFIzSlBhRnA2ZVRseWJuaEtXSE0zU2pGbWVVOWliMU5vUzBoeVNqZ3RSbTg1TTFkalgzaFJTa2RyVG5JM2RVZHZNa1pOTG13d2JrNVZSR05DVEMxMFJVaHFNbWd0U1ZwbVYxRS5hMm9qMlFVeUZKbGI5NVBKeTl6eXJ3d2Z2bndRTDdjTjZRMTVjU0paUjdRIiwiZXhwIjoxNzUzOTc0NjQ1LCJpYXQiOjE3NTM5NzQzNDV9.gTo53H6DJ4tCTi3pi1OTAzZsd79SCKsbuBZgYWyrqHE\",\"callbacks\":[{\"type\":\"HiddenValueCallback\",\"output\":[{\"name\":\"value\",\"value\":\"67e50231-c646-4a74-9b5d-2022a3e6f8d6\"},{\"name\":\"id\",\"value\":\"jwt\"}],\"input\":[{\"name\":\"IDToken1\",\"value\":\"jwt\"}]}]}" + }, + "cookies": [ + { + "httpOnly": true, + "name": "amlbcookie", + "path": "/", + "value": "01" + } + ], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "private" + }, + { + "name": "content-api-version", + "value": "resource=2.1" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "amlbcookie=01; Path=/; HttpOnly" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": "2470" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 337, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.114Z", + "time": 13, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 13 + } + }, + { + "_id": "70a1e477f811afc968b0c7f14c7a840d", + "_order": 1, + "cache": {}, + "request": { + "bodySize": 4241, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=2.0, protocol=1.0" + }, + { + "name": "content-length", + "value": "4241" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 486, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\"authId\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoSW5kZXhWYWx1ZSI6ImFtc3RlclNlcnZpY2UiLCJvdGsiOiI0Z2RqZ2NpczE5MXZwazRsOWZvM2RwN2F0ZiIsImF1dGhJbmRleFR5cGUiOiJzZXJ2aWNlIiwicmVhbG0iOiIvIiwic2Vzc2lvbklkIjoiKkFBSlRTUUFDTURFQUJIUjVjR1VBQ0VwWFZGOUJWVlJJQUFKVE1RQUEqZXlKMGVYQWlPaUpLVjFRaUxDSmpkSGtpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5aWGxLTUdWWVFXbFBhVXBMVmpGUmFVeERTbXhpYlUxcFQybEtRazFVU1RSUk1FcEVURlZvVkUxcVZUSkphWGRwV1ZkNGJrbHFiMmxhUjJ4NVNXNHdMaTVSTFhKSWQyRnBVVEJrVEZsNVZuZzNSbFp4V2kxQkxrNUJReTFxV1ZaWVRYaG9aRWRDYVc1aVpYWkpRVkpZVDBsQmExWnZUVWhhVVRGYVNEWnpWWGxDTm01eFlXMW9XRk01TmpCUExVWXdUak00UzA1bFdGbGZiVmN5VFdsaE4yWlpUVGg2TlhSYVFUWjZWRkJ0YkU4eFpqQTFjRlpXVkdsVVNGTk5aRFJMVEU1dWVuaFJUSFJhWmtoTGFXNHlPSFJZUTFacVNHMHpSbTFRUldWaVZVWkJSMmN0VURoYVRFZHRabUZ5WlZKaGQwOWFPRGxaV21sb2VrTkRSV1pJTjBGbFpqbHVSbmRUUVRScVgydFRTUzFFVERsT2FISXlPVFJmVERkTmRrTldiRmhPUm05d1EycDNiMkV6ZHpkRlNrRXlSaTFWV1MxSlFXRTNORFpSVVY5blUyTnpRbmhOVlhabFMwSmhRMnBYU1d0WWFGcEZVR2xFY2tVNFJpMXJUbWhmY2xGTU1XWlphREE1VFRaT1FuUkRNMk5UYkVocGJYVnJaRmcwZEZaQ09XcHNObEpSV1MxUFUzSnpWa2w0ZEZwblIydHZjWFoxYkhGRWVuaHRiRU5aTjNGQlJXVkVWa1ptY25CMlEyeGlaSEZ2VUU5WlQybGthVmxVTWkxUU16ZFNSME0wZVZkdlpIZFlOMnBDT1RWMVlsbEdRMnhIZWxKTmRYaHZiMGgxTXkxemFEUmhUbEl3YlhSNk1qQklWRzlDUjNKelNWTmljWEJpYjBGUmFteHpjbXhKYlVod01FaHZhVGx6VFU1cWVYRmFhbDlQTmpKT1R6bERPRXRvYm5GUlgzWnhOa0l3YlZobFdrc3dWa0U0Um1obVZtdFRObUYzY21aMVVXeGlSM3BvYjJOc01VeE1OMHhLWWxZMll6QldUbEpLZVVOaExURlZiMjlmV210SlpHd3RRMU0zVFdkdU9UTlRPV3cxVWpKMlIwazNRamROZDE4elQxZzFPVlZUYmw5TFJXZFNja3RGWlVwSFpuRm9WekV4VFRkRWRXSjRZVzFrWXkxdVpVOTFlbHBWTnpOa1QxaHBhMVpDUlU1UWJEZHRia1k1WnpoVWJHNXpTbmhGY21RemIxWllXWHBEZGpNMFpXTkhWMVJqZW5KSVIxUk9SbUZwVTJwRWFHOVZjMnR2VFhGSFpsZE5SMGxYZHpablVFZHhNRGRETm1ZMVZsZERWRmxMVlZsSWNXOXdjVWMwWmtGU01FSldkV3BHUVRCYVRHbENMVjg1WVVkTmRIVXdRbnBmYVdWWU1XUm1Va2c0TTBwNVJIZHNTRWhvVUZkeVduVlFjbXRaV21GTllrVlhWMk5VTUVWV2ExTXRSMUV4UVdrME1FWndaMlJhY1VjNE4yTlRRbGMyVEdkc1pqUjBjVFF6UWt4NlJXUm5NbGQzTFdsTVRGWkpXWFZHTUUxRFRHNUVNa3RKY1UxcFZrNHlNbUoyWDFWSFpqaENhMVJTZEhwaWIyZDBjelJWTUUxMFIzSlBhRnA2ZVRseWJuaEtXSE0zU2pGbWVVOWliMU5vUzBoeVNqZ3RSbTg1TTFkalgzaFJTa2RyVG5JM2RVZHZNa1pOTG13d2JrNVZSR05DVEMxMFJVaHFNbWd0U1ZwbVYxRS5hMm9qMlFVeUZKbGI5NVBKeTl6eXJ3d2Z2bndRTDdjTjZRMTVjU0paUjdRIiwiZXhwIjoxNzUzOTc0NjQ1LCJpYXQiOjE3NTM5NzQzNDV9.gTo53H6DJ4tCTi3pi1OTAzZsd79SCKsbuBZgYWyrqHE\",\"callbacks\":[{\"type\":\"HiddenValueCallback\",\"output\":[{\"name\":\"value\",\"value\":\"67e50231-c646-4a74-9b5d-2022a3e6f8d6\"},{\"name\":\"id\",\"value\":\"jwt\"}],\"input\":[{\"name\":\"IDToken1\",\"value\":\"eyJ0eXAiOiJqd3QiLCJraWQiOiJBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFDQVFEZ0pXRGp6ZWUyOXpqNHdTQ2dtUFIxdzIwR0x0Z0h0N3REQ0I3c29CaFBUT1JhOEptb0YrYjFBelFJZDAweHZYeWtOWDRkcUNwbU9uVVp4dzRLWENUN0VUVWNSdmw3VU0wd3ZxVmhBTkM2azZrYTB3czBsUkw4RHE2NHZMVjJhQnJjT0F2RU5tcG11emplakcxU3pJVFFHQTJpNW9Ra3hBVkd5c2F6bHBrQTkwQjJpNjNsRFpyZHJlbWUwd1J4S2pKdmhpRGQ0UEtVb01NdEpTQjEvVFdjcUJRdlVMTU5BdWU1ZkhmbXpITnd1NXBrU1NJZElYRGR6TEdPVi8rODNvaU9PaGtGQWNManl4T3pOTGdhUXJvNW5IWGtwMElpZml1OGs2QWJacHVEWDJFMUI0YXNjbFdscldIbWQ4b3E4RVlyOEg3YVpHclNsNFhxRWtWSGZpa1lmMkwvUnREcy96bkQrR1Z1TXl4UEFmVVo3QWVVcGF2bGtkdlRDTGFBa291MkZXVkViN25oaEdsTjNDQncyaGVjRFhhUTFaZi81MWxCMkl3QnBueVVyWDMxQm1WVmZ0RzlzQld6bGRvSnlLVk5VY2tBYlYwWHA0cjFvN2ExQjRTeFFSZVRGSWh4RXVPOWRDMDJkVXhWMEZSUXZtMUNEZ01ISnNlTCs4ZGI2UG1ScmQxdWQ3NUo1TmZEWUFieU5jU09yT0g5WEhqSzFydVkyV2FBd1Exc1I5d25XWERoRk1LRVFWSWZvK3dNSDYxNS8vZGRZcndxdTBGK2RLeEtUODNuYmZ5ZTVKMUhabFBmZFppM0tsVEV2OVd1ZmtVQnhTQVZKK2JTY1lvWFRYNzY2MjFDbE5NeUJWeDVYQ1M5SlhXRFRGQ2Z3MUI3VTkvQlhOTVdVRkwxWXc9PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJhbWFkbWluIiwibm9uY2UiOiI2N2U1MDIzMS1jNjQ2LTRhNzQtOWI1ZC0yMDIyYTNlNmY4ZDYifQ.HJc-oMe1NtGd9uMIQWUCWXvtTtPHtVnWcIjuKpVF9xO1PcfCgTQKNtubzt8Ez2kmReeIGW83iM95BsDnon9O_aTyI0DfSLAkaL5f4RRMMBpAVhWdNBWuKxYdymDL7lwQ9BmAgplvG4IsWgk27hzjA4TctSQXhz41DThYIwtVReX6Chkcr7SJ9_ACA0lnkZr6GbOGnoJXVpEQNNM7mGtIrtWPB5ukXmjnPf1F6xYWqPBWoh7B7gqewZtA-9Yd-foDs9nzztGfQxjVkVXqGJY3fttqVRE_oZBsLoHkDPfhBXWjEXvII-oDKBUPmKW7UkFPx1r7HqDWIWVMtGNkNx2UZfNuHG8xp29Zcg1AfrTaWK9JOdNcIEE1hy9SzocMoipnVAvljyx_30W_AFQn9pmhxuLo1E457kSMD0NI-f99lLB9jzwL5fKQ0kH5hagLyFdyfHmTrzBgi9Iy4G0rJ5galglqAOOvNP3S9BkBOpaIJXe-fmrURegfUuNI7not_Ypknvzw__px8lD4MmRN4Qd7_1P1q5ruhQAvWRqSnJher5dRszC2Jt19Okn31CbzGa5ZVUaVVLnaE2Hgouo9Gwl6NHZNGrY5RhKw0shX9wC2b_t9x2F24Ax7A9saV-ZMaEaLeEGKpWhHbduMTcpqASvvkVAJYKJNiDwtdW4YhVuCqJM\"}]}]}" + }, + "queryString": [ + { + "name": "authIndexType", + "value": "service" + }, + { + "name": "authIndexValue", + "value": "amsterService" + } + ], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/realms/root/authenticate?authIndexType=service&authIndexValue=amsterService" + }, + "response": { + "bodySize": 163, + "content": { + "mimeType": "application/json", + "size": 163, + "text": "{\"tokenId\":\"ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*\",\"successUrl\":\"/am/console\",\"realm\":\"/\"}" + }, + "cookies": [ + { + "httpOnly": true, + "name": "iPlanetDirectoryPro", + "path": "/", + "value": "ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "httpOnly": true, + "name": "amlbcookie", + "path": "/", + "value": "01" + } + ], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "private" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "iPlanetDirectoryPro=ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*; Path=/; HttpOnly" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "amlbcookie=01; Path=/; HttpOnly" + }, + { + "name": "content-api-version", + "value": "resource=2.1" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": "163" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 498, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.150Z", + "time": 22, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 22 + } + }, + { + "_id": "b729d22f437e3d001bc90ede6a93667d", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 124, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=4.0" + }, + { + "name": "content-length", + "value": "124" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 440, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\"tokenId\":\"ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*\"}" + }, + "queryString": [ + { + "name": "_action", + "value": "getSessionInfo" + } + ], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/realms/root/sessions/?_action=getSessionInfo" + }, + "response": { + "bodySize": 289, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 289, + "text": "{\"username\":\"amadmin\",\"universalId\":\"id=amadmin,ou=user,ou=am-config\",\"realm\":\"/\",\"latestAccessTime\":\"2025-07-31T15:05:45Z\",\"maxIdleExpirationTime\":\"2025-07-31T15:35:45Z\",\"maxSessionExpirationTime\":\"2025-07-31T17:05:44Z\",\"properties\":{\"AMCtxId\":\"86d527f2-3667-4c8e-bfd8-3bf1b9517d24-302\"}}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "private" + }, + { + "name": "content-api-version", + "value": "resource=4.0" + }, + { + "name": "content-security-policy", + "value": "default-src 'none';frame-ancestors 'none';sandbox" + }, + { + "name": "cross-origin-opener-policy", + "value": "same-origin" + }, + { + "name": "cross-origin-resource-policy", + "value": "same-origin" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "289" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 465, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.177Z", + "time": 7, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 7 + } + }, + { + "_id": "074c9dfd2413e8a532954fa6dbfed72b", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 363, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/x-www-form-urlencoded" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "protocol=2.1,resource=1.0" + }, + { + "name": "cookie", + "value": "iPlanetDirectoryPro=ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "iplanetdirectorypro", + "value": "ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "content-length", + "value": "363" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 710, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/x-www-form-urlencoded", + "params": [], + "text": "redirect_uri=http://openam-frodo-dev.classic.com:8080/platform/appAuthHelperRedirect.html&scope=openid&response_type=code&client_id=idmAdminClient&csrf=ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*&decision=allow&code_challenge=aBsQo0xdyLdi960lImz0bqPE9xeYRPzwG6xhCyRoUsw&code_challenge_method=S256" + }, + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/oauth2/authorize" + }, + "response": { + "bodySize": 1849, + "content": { + "mimeType": "text/html;charset=UTF-8", + "size": 1849, + "text": "\n\n\n\n \n \n \n \n OAuth2 Error Page\n\n\n\n
Loading...
\n
\n \n \n\n\n" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "text/html;charset=UTF-8" + }, + { + "name": "content-length", + "value": "1849" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 224, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 400, + "statusText": "Bad Request" + }, + "startedDateTime": "2025-07-31T15:05:45.190Z", + "time": 8, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 8 + } + }, + { + "_id": "074c9dfd2413e8a532954fa6dbfed72b", + "_order": 1, + "cache": {}, + "request": { + "bodySize": 361, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/x-www-form-urlencoded" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "protocol=2.1,resource=1.0" + }, + { + "name": "cookie", + "value": "iPlanetDirectoryPro=ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "iplanetdirectorypro", + "value": "ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "content-length", + "value": "361" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 710, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/x-www-form-urlencoded", + "params": [], + "text": "redirect_uri=http://openam-frodo-dev.classic.com:8080/platform/appAuthHelperRedirect.html&scope=openid&response_type=code&client_id=idm-admin-ui&csrf=ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*&decision=allow&code_challenge=aBsQo0xdyLdi960lImz0bqPE9xeYRPzwG6xhCyRoUsw&code_challenge_method=S256" + }, + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/oauth2/authorize" + }, + "response": { + "bodySize": 1849, + "content": { + "mimeType": "text/html;charset=UTF-8", + "size": 1849, + "text": "\n\n\n\n \n \n \n \n OAuth2 Error Page\n\n\n\n
Loading...
\n
\n \n \n\n\n" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "text/html;charset=UTF-8" + }, + { + "name": "content-length", + "value": "1849" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 224, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 400, + "statusText": "Bad Request" + }, + "startedDateTime": "2025-07-31T15:05:45.203Z", + "time": 6, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 6 + } + }, + { + "_id": "aa49f3ff76d93ac5b0dbc7d6f8a32b44", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=1.0" + }, + { + "name": "cookie", + "value": "iPlanetDirectoryPro=ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 532, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/serverinfo/version" + }, + "response": { + "bodySize": 257, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 257, + "text": "{\"_id\":\"version\",\"_rev\":\"-466575464\",\"version\":\"8.0.1\",\"fullVersion\":\"ForgeRock Access Management 8.0.1 Build b59bc0908346197b0c33afcb9e733d0400feeea1 (2025-April-15 11:37)\",\"revision\":\"b59bc0908346197b0c33afcb9e733d0400feeea1\",\"date\":\"2025-April-15 11:37\"}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "content-api-version", + "value": "resource=1.0" + }, + { + "name": "content-security-policy", + "value": "default-src 'none';frame-ancestors 'none';sandbox" + }, + { + "name": "cross-origin-opener-policy", + "value": "same-origin" + }, + { + "name": "cross-origin-resource-policy", + "value": "same-origin" + }, + { + "name": "etag", + "value": "\"-466575464\"" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "257" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 486, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.214Z", + "time": 6, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 6 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/src/test/mock-recordings/AuthenticateOps_3073788200/Classic-Tests_743483830/getTokens_3422903202/2-Authenticate-successfully-using-alternative-Amster-subject-and-service_3371820952/recording.har b/src/test/mock-recordings/AuthenticateOps_3073788200/Classic-Tests_743483830/getTokens_3422903202/2-Authenticate-successfully-using-alternative-Amster-subject-and-service_3371820952/recording.har new file mode 100644 index 000000000..c502c2fa4 --- /dev/null +++ b/src/test/mock-recordings/AuthenticateOps_3073788200/Classic-Tests_743483830/getTokens_3422903202/2-Authenticate-successfully-using-alternative-Amster-subject-and-service_3371820952/recording.har @@ -0,0 +1,970 @@ +{ + "log": { + "_recordingName": "AuthenticateOps/Classic Tests/getTokens()/2: Authenticate successfully using alternative Amster subject and service", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.6" + }, + "entries": [ + { + "_id": "a92fbf6a95676ead13c4d7e1621eb0a1", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=1.1" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 386, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/serverinfo/*" + }, + "response": { + "bodySize": 589, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 589, + "text": "{\"_id\":\"*\",\"_rev\":\"2075994301\",\"domains\":[],\"protectedUserAttributes\":[\"telephoneNumber\",\"mail\"],\"cookieName\":\"iPlanetDirectoryPro\",\"secureCookie\":false,\"forgotPassword\":\"false\",\"forgotUsername\":\"false\",\"kbaEnabled\":\"false\",\"selfRegistration\":\"false\",\"lang\":\"en-US\",\"successfulUserRegistrationDestination\":\"default\",\"socialImplementations\":[],\"referralsEnabled\":\"false\",\"zeroPageLogin\":{\"enabled\":false,\"refererWhitelist\":[],\"allowedWithoutReferer\":true},\"realm\":\"/\",\"xuiUserSessionValidationEnabled\":true,\"fileBasedConfiguration\":false,\"userIdAttributes\":[],\"nodeDesignerXuiEnabled\":true}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "content-api-version", + "value": "resource=1.1" + }, + { + "name": "content-security-policy", + "value": "default-src 'none';frame-ancestors 'none';sandbox" + }, + { + "name": "cross-origin-opener-policy", + "value": "same-origin" + }, + { + "name": "cross-origin-resource-policy", + "value": "same-origin" + }, + { + "name": "etag", + "value": "\"2075994301\"" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "589" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 486, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.227Z", + "time": 4, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 4 + } + }, + { + "_id": "48c945f7b47c59f5c7e5748aab7c9dc4", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 2, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=2.0, protocol=1.0" + }, + { + "name": "x-openam-username", + "value": "amadmin" + }, + { + "name": "x-openam-password", + "value": "TriVir#1" + }, + { + "name": "content-length", + "value": "2" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 544, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{}" + }, + "queryString": [ + { + "name": "authIndexType", + "value": "service" + }, + { + "name": "authIndexValue", + "value": "MockAmsterService" + } + ], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/realms/root/authenticate?authIndexType=service&authIndexValue=MockAmsterService" + }, + "response": { + "bodySize": 2476, + "content": { + "mimeType": "application/json", + "size": 2476, + "text": "{\"authId\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoSW5kZXhWYWx1ZSI6Ik1vY2tBbXN0ZXJTZXJ2aWNlIiwib3RrIjoicWxmM2RyZWdkZ283MGhhbzFrcnZwbWF1ZDIiLCJhdXRoSW5kZXhUeXBlIjoic2VydmljZSIsInJlYWxtIjoiLyIsInNlc3Npb25JZCI6IipBQUpUU1FBQ01ERUFCSFI1Y0dVQUNFcFhWRjlCVlZSSUFBSlRNUUFBKmV5SjBlWEFpT2lKS1YxUWlMQ0pqZEhraU9pSktWMVFpTENKaGJHY2lPaUpJVXpJMU5pSjkuWlhsS01HVllRV2xQYVVwTFZqRlJhVXhEU214aWJVMXBUMmxLUWsxVVNUUlJNRXBFVEZWb1ZFMXFWVEpKYVhkcFdWZDRia2xxYjJsYVIyeDVTVzR3TGk1SlJHdDJTa1pDY0UxVU4xWm9Ua0ZSVDBGRU1GVlJMa2hXU3pOc2ExbGlVMjlZVTNsNWJrTTJOa0UzYVY4d2JUVTBkbFJJTTJkME1tRXpjM3BFWTNReFowVnZSblJEVW1RekxUUXlla3RUUTJvNVdEZHBVVXBGYVdGMVEyeGliRUZ1VjFCbVVWSnlla015VEdsU1puaHdZM2xGUzFNMVNGaDZVMmxGZVhCTWMwOWtUMXBHV21NemNYWldSVGRFV0dWUE1XZGtZbGhaTTBKUWRFSkRjbWRZVURGNlQwVm1ja3RLTTBRMGNYRkdTV3d0WDB4SlpUTmpRWEp2YjNwWk1HTjZkMlJ3Y1dobUxVNVBUR1pWVFUwelozWTJPSFJsVmpCUVMzWnBSRjh5ZDFoUlNqZFVaRVJZTFVadU5TMU1jemhYU2xGU0xYTmFjVGx3ZG1Nd1lXZHpNWEJwY1hKTGFUaFNSMGcwTW1WT1UycHNVRFowTkUxbE56Wm9iVXB5VTJoSU5IaDZaM2xyU21wWk9YSlFXa1pHYzBaZlpuYzRlRkZPU0dKb09WQlBlVkJMYmpWa1ZXSmtiVzVqUWxWWmRrVlNValV0VFdWVGNHTnZkVmRWVUUxUE1rZGxVVVo2Y2pNeFFUQnNhRGhuYW10U1RqZzJjMlptWTAxdU1uazFZelpoYVhad1pUZzBlbE5wZFdjM2RFMXNiR2RHUVdaVUxXZ3RWblZmY2tSUVZETm1NVVpaY1ZaRWNYTjRkV0ZNYUVSM1VrMUxOMU5XZERWMlNXMUJTV3N0YmpsbWQzSkpjSGhXZUdaNWFXeGhWbmsxYWpoVVpscGpNbWxqZVZoZlpXSkpNV2wzZVZwV2QzUjFXRkZyVlROTGVqRmphREJrTFRJeGVFVklTbTVoYTFwNFRIVmhkakJvZWpOWU1uaDJaRWhpVXpkclZWWlFSM0pPTkcxeWMzQklWVWhTVFVoNWQzYzJlRlYzVVZaVmVVeGhiMFpqY0Y5aFRFcG9hWFZ1Y2t0elYxaEdWVlkwU1VWeWVWWnpXVGRwU0Uwd2VYVnNlak41ZFhKNGNVOWpNR3A1YkRkRlZ6UTJPR2xrWVV0eFpWSndhRzFGZDFSRldESmZTM0I1ZW14RVN6WlBhMkU0Y214VlRUSTNlRXR3WjNSdmFVbFBORU0zTW1OcVltVjROMDQwVjBoaVN6TkVPR3AwVWxkbWExbFljbEpLVkVwWE9VeEJia3B2V1ZCVmRucHpObFk0YkZWU09YazRYM2x1VUZCUlZGVk9RbU5NY1hkM1pEZFJNV05sZFZoZk5raHRlSFpsYUUxTWRUZG5RazlpWlhWWWJuTktlSFptV0ZrMVJXSXRaRmt0VW1Sbk5FVlZSVmd4YzJkM1VrcGtZa1pmWlcxeGJYSXhMVTl6VTFndFJHOTNkMHR1TWtFd09FSnRSbTA1ZDIweFVVMHdXbGhtVVhJMVVYSTRXSGxKUzNwSFEyMVBkRFJFVVVGdmFGWjBZbFpaY1RBMVZGbzNORGxzVDBwV01GTlJiMFJ1V21sNGNVbHhkSGM1YTFReVJUUkljRkpWVUVsblpVdE1hRXBpYUdwck4ydG9NbVJ5UW1GUWFWaG5aMWQ2ZDBKQ0xucFZhSFZ6VWs1VmMzQkhabmxUUkdkaFJYVlNORUUuT1VFWWFfWEswcnpPenFHeHg4aXZlOGpkR1E3TXp5VUxhUDl1VTM1VUNsayIsImV4cCI6MTc1Mzk3NDY0NSwiaWF0IjoxNzUzOTc0MzQ1fQ.s9WJaN830tUItTEK_F0h-vS2eMgZQJPiY2m7SOjS6no\",\"callbacks\":[{\"type\":\"HiddenValueCallback\",\"output\":[{\"name\":\"value\",\"value\":\"3a96bc28-91d9-4a91-b81c-90c8a0289caa\"},{\"name\":\"id\",\"value\":\"jwt\"}],\"input\":[{\"name\":\"IDToken1\",\"value\":\"jwt\"}]}]}" + }, + "cookies": [ + { + "httpOnly": true, + "name": "amlbcookie", + "path": "/", + "value": "01" + } + ], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "private" + }, + { + "name": "content-api-version", + "value": "resource=2.1" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "amlbcookie=01; Path=/; HttpOnly" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": "2476" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 337, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.236Z", + "time": 7, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 7 + } + }, + { + "_id": "48c945f7b47c59f5c7e5748aab7c9dc4", + "_order": 1, + "cache": {}, + "request": { + "bodySize": 4247, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=2.0, protocol=1.0" + }, + { + "name": "content-length", + "value": "4247" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 490, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\"authId\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoSW5kZXhWYWx1ZSI6Ik1vY2tBbXN0ZXJTZXJ2aWNlIiwib3RrIjoicWxmM2RyZWdkZ283MGhhbzFrcnZwbWF1ZDIiLCJhdXRoSW5kZXhUeXBlIjoic2VydmljZSIsInJlYWxtIjoiLyIsInNlc3Npb25JZCI6IipBQUpUU1FBQ01ERUFCSFI1Y0dVQUNFcFhWRjlCVlZSSUFBSlRNUUFBKmV5SjBlWEFpT2lKS1YxUWlMQ0pqZEhraU9pSktWMVFpTENKaGJHY2lPaUpJVXpJMU5pSjkuWlhsS01HVllRV2xQYVVwTFZqRlJhVXhEU214aWJVMXBUMmxLUWsxVVNUUlJNRXBFVEZWb1ZFMXFWVEpKYVhkcFdWZDRia2xxYjJsYVIyeDVTVzR3TGk1SlJHdDJTa1pDY0UxVU4xWm9Ua0ZSVDBGRU1GVlJMa2hXU3pOc2ExbGlVMjlZVTNsNWJrTTJOa0UzYVY4d2JUVTBkbFJJTTJkME1tRXpjM3BFWTNReFowVnZSblJEVW1RekxUUXlla3RUUTJvNVdEZHBVVXBGYVdGMVEyeGliRUZ1VjFCbVVWSnlla015VEdsU1puaHdZM2xGUzFNMVNGaDZVMmxGZVhCTWMwOWtUMXBHV21NemNYWldSVGRFV0dWUE1XZGtZbGhaTTBKUWRFSkRjbWRZVURGNlQwVm1ja3RLTTBRMGNYRkdTV3d0WDB4SlpUTmpRWEp2YjNwWk1HTjZkMlJ3Y1dobUxVNVBUR1pWVFUwelozWTJPSFJsVmpCUVMzWnBSRjh5ZDFoUlNqZFVaRVJZTFVadU5TMU1jemhYU2xGU0xYTmFjVGx3ZG1Nd1lXZHpNWEJwY1hKTGFUaFNSMGcwTW1WT1UycHNVRFowTkUxbE56Wm9iVXB5VTJoSU5IaDZaM2xyU21wWk9YSlFXa1pHYzBaZlpuYzRlRkZPU0dKb09WQlBlVkJMYmpWa1ZXSmtiVzVqUWxWWmRrVlNValV0VFdWVGNHTnZkVmRWVUUxUE1rZGxVVVo2Y2pNeFFUQnNhRGhuYW10U1RqZzJjMlptWTAxdU1uazFZelpoYVhad1pUZzBlbE5wZFdjM2RFMXNiR2RHUVdaVUxXZ3RWblZmY2tSUVZETm1NVVpaY1ZaRWNYTjRkV0ZNYUVSM1VrMUxOMU5XZERWMlNXMUJTV3N0YmpsbWQzSkpjSGhXZUdaNWFXeGhWbmsxYWpoVVpscGpNbWxqZVZoZlpXSkpNV2wzZVZwV2QzUjFXRkZyVlROTGVqRmphREJrTFRJeGVFVklTbTVoYTFwNFRIVmhkakJvZWpOWU1uaDJaRWhpVXpkclZWWlFSM0pPTkcxeWMzQklWVWhTVFVoNWQzYzJlRlYzVVZaVmVVeGhiMFpqY0Y5aFRFcG9hWFZ1Y2t0elYxaEdWVlkwU1VWeWVWWnpXVGRwU0Uwd2VYVnNlak41ZFhKNGNVOWpNR3A1YkRkRlZ6UTJPR2xrWVV0eFpWSndhRzFGZDFSRldESmZTM0I1ZW14RVN6WlBhMkU0Y214VlRUSTNlRXR3WjNSdmFVbFBORU0zTW1OcVltVjROMDQwVjBoaVN6TkVPR3AwVWxkbWExbFljbEpLVkVwWE9VeEJia3B2V1ZCVmRucHpObFk0YkZWU09YazRYM2x1VUZCUlZGVk9RbU5NY1hkM1pEZFJNV05sZFZoZk5raHRlSFpsYUUxTWRUZG5RazlpWlhWWWJuTktlSFptV0ZrMVJXSXRaRmt0VW1Sbk5FVlZSVmd4YzJkM1VrcGtZa1pmWlcxeGJYSXhMVTl6VTFndFJHOTNkMHR1TWtFd09FSnRSbTA1ZDIweFVVMHdXbGhtVVhJMVVYSTRXSGxKUzNwSFEyMVBkRFJFVVVGdmFGWjBZbFpaY1RBMVZGbzNORGxzVDBwV01GTlJiMFJ1V21sNGNVbHhkSGM1YTFReVJUUkljRkpWVUVsblpVdE1hRXBpYUdwck4ydG9NbVJ5UW1GUWFWaG5aMWQ2ZDBKQ0xucFZhSFZ6VWs1VmMzQkhabmxUUkdkaFJYVlNORUUuT1VFWWFfWEswcnpPenFHeHg4aXZlOGpkR1E3TXp5VUxhUDl1VTM1VUNsayIsImV4cCI6MTc1Mzk3NDY0NSwiaWF0IjoxNzUzOTc0MzQ1fQ.s9WJaN830tUItTEK_F0h-vS2eMgZQJPiY2m7SOjS6no\",\"callbacks\":[{\"type\":\"HiddenValueCallback\",\"output\":[{\"name\":\"value\",\"value\":\"3a96bc28-91d9-4a91-b81c-90c8a0289caa\"},{\"name\":\"id\",\"value\":\"jwt\"}],\"input\":[{\"name\":\"IDToken1\",\"value\":\"eyJ0eXAiOiJqd3QiLCJraWQiOiJBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFDQVFEZ0pXRGp6ZWUyOXpqNHdTQ2dtUFIxdzIwR0x0Z0h0N3REQ0I3c29CaFBUT1JhOEptb0YrYjFBelFJZDAweHZYeWtOWDRkcUNwbU9uVVp4dzRLWENUN0VUVWNSdmw3VU0wd3ZxVmhBTkM2azZrYTB3czBsUkw4RHE2NHZMVjJhQnJjT0F2RU5tcG11emplakcxU3pJVFFHQTJpNW9Ra3hBVkd5c2F6bHBrQTkwQjJpNjNsRFpyZHJlbWUwd1J4S2pKdmhpRGQ0UEtVb01NdEpTQjEvVFdjcUJRdlVMTU5BdWU1ZkhmbXpITnd1NXBrU1NJZElYRGR6TEdPVi8rODNvaU9PaGtGQWNManl4T3pOTGdhUXJvNW5IWGtwMElpZml1OGs2QWJacHVEWDJFMUI0YXNjbFdscldIbWQ4b3E4RVlyOEg3YVpHclNsNFhxRWtWSGZpa1lmMkwvUnREcy96bkQrR1Z1TXl4UEFmVVo3QWVVcGF2bGtkdlRDTGFBa291MkZXVkViN25oaEdsTjNDQncyaGVjRFhhUTFaZi81MWxCMkl3QnBueVVyWDMxQm1WVmZ0RzlzQld6bGRvSnlLVk5VY2tBYlYwWHA0cjFvN2ExQjRTeFFSZVRGSWh4RXVPOWRDMDJkVXhWMEZSUXZtMUNEZ01ISnNlTCs4ZGI2UG1ScmQxdWQ3NUo1TmZEWUFieU5jU09yT0g5WEhqSzFydVkyV2FBd1Exc1I5d25XWERoRk1LRVFWSWZvK3dNSDYxNS8vZGRZcndxdTBGK2RLeEtUODNuYmZ5ZTVKMUhabFBmZFppM0tsVEV2OVd1ZmtVQnhTQVZKK2JTY1lvWFRYNzY2MjFDbE5NeUJWeDVYQ1M5SlhXRFRGQ2Z3MUI3VTkvQlhOTVdVRkwxWXc9PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJhbWFkbWluIiwibm9uY2UiOiIzYTk2YmMyOC05MWQ5LTRhOTEtYjgxYy05MGM4YTAyODljYWEifQ.loJ50LPaSZ6BbNZuWisGaOylgb9aw0NgP3ig_M-gqPzu444EgU2XDCRWxwp4Gj-agaUp2MxH-C7dFwX_UDNQ_Lzc8mQABbQr86n6koalQARdXSGtp74zQHRwVf0p1KtTecb8CCQqsc3kPnVlxfAxzzyrYRR97-VahrKalnEpHUVskMGO8gs9Gr_j-uUaxOoyepajlvQVnXpPpzww2I3TlbUqnva7kmC1Zzj9gNFOBIT2L4JFfR8PXQSuUEN78vrDOTcb0NUQJvw-5_T1hJOphHvNmUhs2SY90xgBHFA8qqprgyZ5qEhDztMywKD3UXU6vp9qIqjWyIPHQn1PL0tAJUHu7m-DR6oYFerpCW2eB2Wu1ONYYi2IH8DCMsQ2s45qmmSA0pvkP0dtSk04Jz7aVPGn1BpURv3SGZ5PVep7GHUIaVY_yq6GAG8idNtXvLKv0eLOHwgWW9mJWqB1yrXJ71-n0MbNykb2Qg1dU_Boov-0euBDJKSdhZ7LTl9LrNnsX_apcNeZhUi8jU2FKoQfY_veJvFst6IvUPFdSARN2qPVRSDgZo0UTYL85O8by8VAyuB0YjvOIOrk_2cA9Id2u_278pt5KfRvnKUO3Dw33VBCtKPvr86Wo2yOpVk-U8ZHN14tX68bP7oJjaKvF0a2x-c6DynLF2y9uwP50JP_2X0\"}]}]}" + }, + "queryString": [ + { + "name": "authIndexType", + "value": "service" + }, + { + "name": "authIndexValue", + "value": "MockAmsterService" + } + ], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/realms/root/authenticate?authIndexType=service&authIndexValue=MockAmsterService" + }, + "response": { + "bodySize": 163, + "content": { + "mimeType": "application/json", + "size": 163, + "text": "{\"tokenId\":\"JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*\",\"successUrl\":\"/am/console\",\"realm\":\"/\"}" + }, + "cookies": [ + { + "httpOnly": true, + "name": "iPlanetDirectoryPro", + "path": "/", + "value": "JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "httpOnly": true, + "name": "amlbcookie", + "path": "/", + "value": "01" + } + ], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "private" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "iPlanetDirectoryPro=JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*; Path=/; HttpOnly" + }, + { + "_fromType": "array", + "name": "set-cookie", + "value": "amlbcookie=01; Path=/; HttpOnly" + }, + { + "name": "content-api-version", + "value": "resource=2.1" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "content-length", + "value": "163" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 498, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.254Z", + "time": 29, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 29 + } + }, + { + "_id": "b729d22f437e3d001bc90ede6a93667d", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 124, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=4.0" + }, + { + "name": "content-length", + "value": "124" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 440, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/json", + "params": [], + "text": "{\"tokenId\":\"JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*\"}" + }, + "queryString": [ + { + "name": "_action", + "value": "getSessionInfo" + } + ], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/realms/root/sessions/?_action=getSessionInfo" + }, + "response": { + "bodySize": 289, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 289, + "text": "{\"username\":\"amadmin\",\"universalId\":\"id=amadmin,ou=user,ou=am-config\",\"realm\":\"/\",\"latestAccessTime\":\"2025-07-31T15:05:45Z\",\"maxIdleExpirationTime\":\"2025-07-31T15:35:45Z\",\"maxSessionExpirationTime\":\"2025-07-31T17:05:44Z\",\"properties\":{\"AMCtxId\":\"86d527f2-3667-4c8e-bfd8-3bf1b9517d24-325\"}}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "private" + }, + { + "name": "content-api-version", + "value": "resource=4.0" + }, + { + "name": "content-security-policy", + "value": "default-src 'none';frame-ancestors 'none';sandbox" + }, + { + "name": "cross-origin-opener-policy", + "value": "same-origin" + }, + { + "name": "cross-origin-resource-policy", + "value": "same-origin" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "289" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 465, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.288Z", + "time": 5, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 5 + } + }, + { + "_id": "074c9dfd2413e8a532954fa6dbfed72b", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 363, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/x-www-form-urlencoded" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "protocol=2.1,resource=1.0" + }, + { + "name": "cookie", + "value": "iPlanetDirectoryPro=JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "iplanetdirectorypro", + "value": "JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "content-length", + "value": "363" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 710, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/x-www-form-urlencoded", + "params": [], + "text": "redirect_uri=http://openam-frodo-dev.classic.com:8080/platform/appAuthHelperRedirect.html&scope=openid&response_type=code&client_id=idmAdminClient&csrf=JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*&decision=allow&code_challenge=PcgcH_nIDAlN1PQ1eZs4cK-v8lVY9MOy8o5ka8KsypI&code_challenge_method=S256" + }, + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/oauth2/authorize" + }, + "response": { + "bodySize": 1849, + "content": { + "mimeType": "text/html;charset=UTF-8", + "size": 1849, + "text": "\n\n\n\n \n \n \n \n OAuth2 Error Page\n\n\n\n
Loading...
\n
\n \n \n\n\n" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "text/html;charset=UTF-8" + }, + { + "name": "content-length", + "value": "1849" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 224, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 400, + "statusText": "Bad Request" + }, + "startedDateTime": "2025-07-31T15:05:45.301Z", + "time": 8, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 8 + } + }, + { + "_id": "074c9dfd2413e8a532954fa6dbfed72b", + "_order": 1, + "cache": {}, + "request": { + "bodySize": 361, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/x-www-form-urlencoded" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "protocol=2.1,resource=1.0" + }, + { + "name": "cookie", + "value": "iPlanetDirectoryPro=JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "iplanetdirectorypro", + "value": "JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "content-length", + "value": "361" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 710, + "httpVersion": "HTTP/1.1", + "method": "POST", + "postData": { + "mimeType": "application/x-www-form-urlencoded", + "params": [], + "text": "redirect_uri=http://openam-frodo-dev.classic.com:8080/platform/appAuthHelperRedirect.html&scope=openid&response_type=code&client_id=idm-admin-ui&csrf=JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*&decision=allow&code_challenge=PcgcH_nIDAlN1PQ1eZs4cK-v8lVY9MOy8o5ka8KsypI&code_challenge_method=S256" + }, + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/oauth2/authorize" + }, + "response": { + "bodySize": 1849, + "content": { + "mimeType": "text/html;charset=UTF-8", + "size": 1849, + "text": "\n\n\n\n \n \n \n \n OAuth2 Error Page\n\n\n\n
Loading...
\n
\n \n \n\n\n" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "text/html;charset=UTF-8" + }, + { + "name": "content-length", + "value": "1849" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 224, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 400, + "statusText": "Bad Request" + }, + "startedDateTime": "2025-07-31T15:05:45.314Z", + "time": 12, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 12 + } + }, + { + "_id": "aa49f3ff76d93ac5b0dbc7d6f8a32b44", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "name": "accept", + "value": "application/json, text/plain, */*" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "user-agent", + "value": "@rockcarver/frodo-lib/3.3.1" + }, + { + "name": "x-forgerock-transactionid", + "value": "frodo-d427afab-2fdf-4393-905b-757e9f74f7d2" + }, + { + "name": "accept-api-version", + "value": "resource=1.0" + }, + { + "name": "cookie", + "value": "iPlanetDirectoryPro=JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*" + }, + { + "name": "accept-encoding", + "value": "gzip, compress, deflate, br" + }, + { + "name": "host", + "value": "openam-frodo-dev.classic.com:8080" + } + ], + "headersSize": 532, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [], + "url": "http://openam-frodo-dev.classic.com:8080/am/json/serverinfo/version" + }, + "response": { + "bodySize": 257, + "content": { + "mimeType": "application/json;charset=UTF-8", + "size": 257, + "text": "{\"_id\":\"version\",\"_rev\":\"-466575464\",\"version\":\"8.0.1\",\"fullVersion\":\"ForgeRock Access Management 8.0.1 Build b59bc0908346197b0c33afcb9e733d0400feeea1 (2025-April-15 11:37)\",\"revision\":\"b59bc0908346197b0c33afcb9e733d0400feeea1\",\"date\":\"2025-April-15 11:37\"}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "cache-control", + "value": "no-store" + }, + { + "name": "content-api-version", + "value": "resource=1.0" + }, + { + "name": "content-security-policy", + "value": "default-src 'none';frame-ancestors 'none';sandbox" + }, + { + "name": "cross-origin-opener-policy", + "value": "same-origin" + }, + { + "name": "cross-origin-resource-policy", + "value": "same-origin" + }, + { + "name": "etag", + "value": "\"-466575464\"" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "content-type", + "value": "application/json;charset=UTF-8" + }, + { + "name": "content-length", + "value": "257" + }, + { + "name": "date", + "value": "Thu, 31 Jul 2025 15:05:45 GMT" + }, + { + "name": "keep-alive", + "value": "timeout=20" + }, + { + "name": "connection", + "value": "keep-alive" + } + ], + "headersSize": 486, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2025-07-31T15:05:45.330Z", + "time": 8, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 8 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/src/test/mocks/AuthenticateOps/pkcs8.pem b/src/test/mocks/AuthenticateOps/pkcs8.pem new file mode 100644 index 000000000..8102a615f --- /dev/null +++ b/src/test/mocks/AuthenticateOps/pkcs8.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDgJWDjzee29zj4 +wSCgmPR1w20GLtgHt7tDCB7soBhPTORa8JmoF+b1AzQId00xvXykNX4dqCpmOnUZ +xw4KXCT7ETUcRvl7UM0wvqVhANC6k6ka0ws0lRL8Dq64vLV2aBrcOAvENmpmuzje +jG1SzITQGA2i5oQkxAVGysazlpkA90B2i63lDZrdreme0wRxKjJvhiDd4PKUoMMt +JSB1/TWcqBQvULMNAue5fHfmzHNwu5pkSSIdIXDdzLGOV/+83oiOOhkFAcLjyxOz +NLgaQro5nHXkp0Iifiu8k6AbZpuDX2E1B4asclWlrWHmd8oq8EYr8H7aZGrSl4Xq +EkVHfikYf2L/RtDs/znD+GVuMyxPAfUZ7AeUpavlkdvTCLaAkou2FWVEb7nhhGlN +3CBw2hecDXaQ1Zf/51lB2IwBpnyUrX31BmVVftG9sBWzldoJyKVNUckAbV0Xp4r1 +o7a1B4SxQReTFIhxEuO9dC02dUxV0FRQvm1CDgMHJseL+8db6PmRrd1ud75J5NfD +YAbyNcSOrOH9XHjK1ruY2WaAwQ1sR9wnWXDhFMKEQVIfo+wMH615//ddYrwqu0F+ +dKxKT83nbfye5J1HZlPfdZi3KlTEv9WufkUBxSAVJ+bScYoXTX76621ClNMyBVx5 +XCS9JXWDTFCfw1B7U9/BXNMWUFL1YwIDAQABAoICAG09Sk871qYxLq7tUL10Tg/m +woe05Mw+GyG8H3YrQC3Fx7UadYiavZDDkuRdYF3Rf6F4AJLrll5kNxXtEUazScKi +zEOPw5757kbHY0o3X4LO3Ceam2dxD+OQNE8MiVO/wPd6ySFDc+/FZx3vSoiD7GaO +knbF6bkRX5D02opD5yW1WtFrmcB8NPG9BJLHPO76JcJ8mCAKta8B+p1R7tQSJ8LF +l7DHn7vDorOfmb/9/HUAuWoOynVwCtXOIOrCsHyX5+2JGxB5n0XtfxhjpSYHR0M0 +obBiY7Tz0NUDDZDNHRWVsoZpuJTq1Br+uSvdTcZHe6uGdgxxzC3+cuF1LhZ9wgCo +nozQf38ohdXmAyJjCgi45uh3fnmnYg1mEfhuDFrlw0rFlWmfSKe4MeR2uqXxynpe +m0izp4eV0CmdrqaWts2wjVjJi+oBnJu7uPjH91RfprrqPIebThd+SAdimdaVxOk4 +42PQp5pluyTu9YMTH4ap1d3DHL7npTdgOZAThN962AnpQZvRYbmI0PcWpKPAxJ/4 +W+eaYWR1lq9VihuM+98+QEgHwPI11/iy6rJPStrBzS7Y4hPk5CPj16oM3sN/iEa4 +kxGZjATGGLctbw2CbVTQgLh4VwPzTR9lBNBBGU1LOroXnumpNRDzJYlOSQVGsFDi +KLrZOaV/KJgo81lJxoahAoIBAQD88P1xHB3Nj4fjo3foMHHXx0vGiHI+2BUBjFMH +qy4+uwc/voFF+YXehYbMCRklsMsHLoxCVDGBlPZXFhcsb3TNy15Cg6R+6HuxXlv0 +ASxHqmFQ/7gyiaf6YhAml/evUhWRb7ffnQIzmbO2QvlByQYVBbb/thHxI2S4oSfL +ElVI/Y2Y9rJQfwbIo9HsJPyyFU4/jNW2vYqggNQ6cSMZo3Fcrp66vmlINfp8FxI1 +4vrEowKWMcxG2XyJivwkQBDBeKIoyJ5s1wy8HfXVOrdRWm8PBRXWyrAyRu5gMy1Z +7jcuRro+VYLXXqn2wO2NzGXdPyyFgirN2GtO4uExPFsMa34NAoIBAQDi2z/AIJkj +96P1Aocntt9aduQvQFn6oHruH50+JP6BWuPKdgFaqCkdTP5cj8l7hgqxJgH0b+1T +JFHOh+55LKsdtaftGvRQTAYzQc5PrRm4jCpqls4EmUG7NLaYhYwB1O/D8eCf4Hnj +5L0iJbnxKOM6ta1TbB56Adm48E21G5gdsizqdSMrqLdvmnCrmvVwYaDS7qB+5g90 +4cMEIO1CLDsFmWXNZqFLZLL6Aaev6NzunTNNCdlAoEcWyTeLsgRJJjAzM/wBrxhg +K/7t3uO4D7mTRnEE+B7Flz8QgjKvuL4R4e+saVTqMHNYMpwYEI/WpIA+Ftcs5Z9K +KCFWClKEDtUvAoIBADPAlwpqh0qEwHf5/VGbf993XnCrgDyNd5bOgDqEcrzRBYWs +U8Tebd5+kXrzrZqBlrjMdFQp58sskE53LZyg3VQx9QJE7wYsSQqcVNIS3112Zdzt +xWFJuv3GzKko1LuhXtpX8lYlSMsH+rYi897a44yvk6a0OO2OoVAJYRvyj1KU1kDi +0M8ZfmjU4+GeG7nQFCd6iEuQcmmukj224kifoVUmQUHSLbZhnPOAwPAXldlfxOq4 +G6SvYQS/VCGSnhsHtJ4FliyMv4VJ1/+duN5DU4/OKY69/9j5nFJAhlntHIsyW27f +jhrEup/LD4/Ar2LaRyC15k4hpqsITq0FPVfIMo0CggEBAJh5xuqKvoOXQgM+9Lqz +xoHWYV6FATVcQePlKdoIIWuuPGbrU9OsLR49jsWF+2Vj3bHdmvjBdiEoyzdm09ls +NYMdgsXyFP4Xax9ufo29CgmpZCUKQWSD4dNDBeuT71uX1KO94j1zMloDd0AJXWYD +Fl9ylxH4HK/iqIUONxygdGiZRmk73CzPFKQViN8+ovMrG15K92LznLXxvBL9Uprk +gbMsvGkHLygugcFZ9WxdtolG7aE9GmG+mKlvCz+AojLMUR9JNHupRVf2mj0lT6d6 +IkAj23j8kCo8ky20ZP8Iy+/XDTG0Blf1wsEoGxRtpVAedvfEgFHz8FF2kZl0DKwo +EA8CggEAJXEdBGwefqv1QDDllk9Umox44R/M6ik7E+FElwr0r9Kk1+255NSOU/ER +yGOvRw+bTnDy7wCqFXqkma/uC4kZoFf+lVg0IxKpO35Nq7V274HP9iAum/vJa9kK +7cRES5eBYOcZTSQ82kTjsujcbrdLa1T9fV4yfAQZg2bAzpo0FTYobvRD/XVjTYLD +vltqhyPmBu6djXBVSckAOvHHa5zKBwoeCw3myYcKaANuz98PepdH/yZ7KcW04TrZ +hVnHHMNFXJWXUWH8ftj9b97M4WAWzqz0JhBUqLj2H3+dyPzOxGHX9pdkiYD4MDbA +nBZ7tjPhmJQ9O1dzsZhHvAkiD+U7Lw== +-----END PRIVATE KEY----- diff --git a/src/test/mocks/AuthenticateOps/pkcs8.pub b/src/test/mocks/AuthenticateOps/pkcs8.pub new file mode 100644 index 000000000..6411dd32e --- /dev/null +++ b/src/test/mocks/AuthenticateOps/pkcs8.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDgJWDjzee29zj4wSCgmPR1w20GLtgHt7tDCB7soBhPTORa8JmoF+b1AzQId00xvXykNX4dqCpmOnUZxw4KXCT7ETUcRvl7UM0wvqVhANC6k6ka0ws0lRL8Dq64vLV2aBrcOAvENmpmuzjejG1SzITQGA2i5oQkxAVGysazlpkA90B2i63lDZrdreme0wRxKjJvhiDd4PKUoMMtJSB1/TWcqBQvULMNAue5fHfmzHNwu5pkSSIdIXDdzLGOV/+83oiOOhkFAcLjyxOzNLgaQro5nHXkp0Iifiu8k6AbZpuDX2E1B4asclWlrWHmd8oq8EYr8H7aZGrSl4XqEkVHfikYf2L/RtDs/znD+GVuMyxPAfUZ7AeUpavlkdvTCLaAkou2FWVEb7nhhGlN3CBw2hecDXaQ1Zf/51lB2IwBpnyUrX31BmVVftG9sBWzldoJyKVNUckAbV0Xp4r1o7a1B4SxQReTFIhxEuO9dC02dUxV0FRQvm1CDgMHJseL+8db6PmRrd1ud75J5NfDYAbyNcSOrOH9XHjK1ruY2WaAwQ1sR9wnWXDhFMKEQVIfo+wMH615//ddYrwqu0F+dKxKT83nbfye5J1HZlPfdZi3KlTEv9WufkUBxSAVJ+bScYoXTX76621ClNMyBVx5XCS9JXWDTFCfw1B7U9/BXNMWUFL1Yw== Local Amster Test Key 1 \ No newline at end of file diff --git a/src/test/mocks/CryptoUtils/dnssecRsa.private b/src/test/mocks/CryptoUtils/dnssecRsa.private new file mode 100644 index 000000000..4654f845c --- /dev/null +++ b/src/test/mocks/CryptoUtils/dnssecRsa.private @@ -0,0 +1,13 @@ +Private-key-format: v1.3 +Algorithm: 10 (RSASHA512) +Modulus: 9w9AzSIL3a312ovgUTMfw7qCzEZ2UkYonXrg71b3DXwCncGFXLQ5EB3myWebyHzf9OkkgkJZx9c1OPy375fmoXeuiQ5FElENAVddPI+vvkMBQbXwGRvnVXE/Akz7g1vT+LdqEr1H/wmYPUr39twdDQlQtwWO/D1tpovBnf7YI38= +PublicExponent: AQAB +PrivateExponent: p+u2H7cEWafjcqefz5DwBhi+zhkWUtVcxay/Ne3l/URMgu0Ft/zGzC2Gr128hIhbsBrGtcJjQ3ECu7kJS8/y8HaE0zku19+3eh6bIekgtLzbsgQXkgqjVlXxkkQmG+1C9J3rknMyDsoV9ncSBd1VIHKvrvRBPysIr9VlbTWc0Q== +Prime1: /iMv/L2IKaicQy2c5zYuULhaDLcHXbvmpL19Uvi3kh5T6w66U81+HVoU/RprAxSVXuR5gnv3zq6TPewZcVOG6w== +Prime2: +N7JQ4piWJdfb0hDrfogVAeINsbrSxqxC1tnPhnIu+xA7mDz0ke7y6nspJ8JADv5dbt+xtffB1hM4NabM7aYvQ== +Exponent1: 93i4X/sIPtjL4bbnFVdzXnNGbR9F8wbon/9NrKA50fXqbfjXhIeUiWyRB67UChnfIpqFoISrWP4yr5fbZrmZpQ== +Exponent2: L1eaK8oAaV/qXK3/ifICAloodFsk2XWrZkCuBCc9g1ovA+nXmBHjTJv3k11ZhfziuJ3BQSxBlhEbFs5cWdsLdQ== +Coefficient: zYWYdN2jwzNp5F2LJMqqda31BzUhHMDkv1NE/33IvI2BL/tmx2MNREfauHQ5P0/a9bjQR0db1DvyWvsZ1IrxkQ== +Created: 20250730215453 +Publish: 20250730215453 +Activate: 20250730215453 diff --git a/src/test/mocks/CryptoUtils/jwkEcdsa.jwk b/src/test/mocks/CryptoUtils/jwkEcdsa.jwk new file mode 100644 index 000000000..96378ce15 --- /dev/null +++ b/src/test/mocks/CryptoUtils/jwkEcdsa.jwk @@ -0,0 +1,10 @@ +{ + "kty": "EC", + "d": "9BFuyjbx1c8yaaCNFejA3x7835-7nvdGjoLqnnlz3js", + "use": "sig", + "crv": "P-256", + "kid": "vU8zJFV3uX6bU6sKzdBb3Efv9g4ZngrtxdGFoTizIqk", + "x": "ex_4Zmro4qW-upKvWeC6ALKLtnrOIW-wty4dgR36SqE", + "y": "lgByvPwoNuQbmSanfq79NwQS2NSW-lEp38Ck33VAl7w", + "alg": "ES512" +} \ No newline at end of file diff --git a/src/test/mocks/CryptoUtils/jwkRsa.jwk b/src/test/mocks/CryptoUtils/jwkRsa.jwk new file mode 100644 index 000000000..fea0127de --- /dev/null +++ b/src/test/mocks/CryptoUtils/jwkRsa.jwk @@ -0,0 +1,14 @@ +{ + "p": "38A4kFvFEcv-sPqzE6xeG4q5_SLoekVE3cKf_JPFeeufKigoS7VX_0ltqABKp6bohP7uPfWT8oAT1V1VWCcs0Q", + "kty": "RSA", + "q": "p9mCXS5mmKkJrCZGfy0h3yZOJkiDIKi_hwu95VcX_vZ8SM1LKJgwDjPWyvGxPZPC-fXm86Gaj6aN6Q3AJc-G5Q", + "d": "hCC_zyLXg8xvxLmWE3aYK1Mumx2PiawkV2MZRk3kgsLL-jK-Czi2G-D4Wf78B4qZgCB7fairMNZVAeRtc8riB4s4WRNaXKjZkfsjW1UjEmKyO_P598fhURhJCsxpvRZRaDe_D1OT5No_Aj9pAZBqxhl9xVZ-MORbXBt8rgUIfUE", + "e": "AQAB", + "use": "sig", + "kid": "1I0-T7JGu5fjmu-M6PaNbd1IvcN49qK8uDGPy4bTaZo", + "qi": "l2m2wzcOpgI4zaD4yCB880Kg4s4iUz_k3vWgvjhWrqPmor5zPDdWp1LEwRFyoWdGPJg2vqALzE_57Nc3hNLa5A", + "dp": "AW42Ll4X1XRLbZA8wtxwQZbSd5bYi6eTuCM9aJz9PzlNbDSThWBkbqJmjBNsZsP_apJCray7RGqjNdMB761YcQ", + "alg": "RS512", + "dq": "o3ufFdMT6qz51sqQ712uGALfKzqFPVIe-hHdq3rgzFyXQPFzut5AMBUdqB0wXKGGzp9LixBj0naO0eeqtykiYQ", + "n": "krSAxyxXaeHsoqYZSkSRlIx61oHbYesYUzOMwmwEC4inVNVWKECB_teim6yzbhM8RI2HL2JOGeY8l0S8kAv2CoNT9JoZavjD65cDiVFD2nN71LJGslkDpXpbpS_n7lkovooV7YfF9EPb8wwEaHXZMd7ZoAjS3MpQPFhUY85_fPU" +} \ No newline at end of file diff --git a/src/test/mocks/CryptoUtils/opensshEcdsa b/src/test/mocks/CryptoUtils/opensshEcdsa new file mode 100644 index 000000000..5fb6b4a30 --- /dev/null +++ b/src/test/mocks/CryptoUtils/opensshEcdsa @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQRc7bpTE9HV2gT16w/tEpLsvAzkG3jn +qhlHYY9248/4QfEhB87RI7P2TfS0uw9nnDl5kodOvWqJ5q7irWXV1JMBAAAAuGgbtlxoG7 +ZcAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFztulMT0dXaBPXr +D+0Skuy8DOQbeOeqGUdhj3bjz/hB8SEHztEjs/ZN9LS7D2ecOXmSh069aonmruKtZdXUkw +EAAAAgLBIrlWvr1ioOIXQpJaWrvFTDtyPX82rRKy9co6BZNrgAAAAadHJpdmlyQHRyaXZp +ci1mcm9kby1kZXYtdm0BAgMEBQY= +-----END OPENSSH PRIVATE KEY----- diff --git a/src/test/mocks/CryptoUtils/opensshEd25519 b/src/test/mocks/CryptoUtils/opensshEd25519 new file mode 100644 index 000000000..8f6df2036 --- /dev/null +++ b/src/test/mocks/CryptoUtils/opensshEd25519 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACADZP4cJncsnqyzljwFkZe/OUT7jfyqp9YDitbIbx7J3QAAAKCybwj6sm8I ++gAAAAtzc2gtZWQyNTUxOQAAACADZP4cJncsnqyzljwFkZe/OUT7jfyqp9YDitbIbx7J3Q +AAAEB69Infw9oSwafiZ8lhInd0197obUwA0+Brtey/4ZvodQNk/hwmdyyerLOWPAWRl785 +RPuN/Kqn1gOK1shvHsndAAAAGnRyaXZpckB0cml2aXItZnJvZG8tZGV2LXZtAQID +-----END OPENSSH PRIVATE KEY----- diff --git a/src/test/mocks/CryptoUtils/opensshEd25519Enc b/src/test/mocks/CryptoUtils/opensshEd25519Enc new file mode 100644 index 000000000..074e99dd7 --- /dev/null +++ b/src/test/mocks/CryptoUtils/opensshEd25519Enc @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDF4+x4Po +q/v2DP0+p08K2EAAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIEsPs0tJFR/JYuT/ +MwVZVSeZEPqDyxN9xsG6H/BoZnhLAAAAoKfrIuQMw+DxLL2Obpd0a3TjwcB5vcQ3u1krq5 +OBp92/9sx+3C6dodKLCsVWjQcNoY+Xsr2q3/LAXqlhlYcYGb1L0rzvTjnRTTchUTd76hYI +xQqwasfwnmmRdAQiCRlFd8N33TWr+20SAfK+QZKBltet+6ED+BixI6kOR3xQCPN0jXMdez +ZFJW8yuHy+fgGjZjtG2d+/whWcamEfmRZosxo= +-----END OPENSSH PRIVATE KEY----- diff --git a/src/test/mocks/CryptoUtils/opensshRsa b/src/test/mocks/CryptoUtils/opensshRsa new file mode 100644 index 000000000..6037a831a --- /dev/null +++ b/src/test/mocks/CryptoUtils/opensshRsa @@ -0,0 +1,16 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAIEAyrB0SnD2N6xvlXYcxRgzTErJZfBE6RIJ7qE0lEF3+aqzP5Ls5J6j +jIUTwI8MHMX0eSxNMjdm1pycqbWS8mq4HwOwcAslObACU7BVm9yaKZVGbzFWr2Yps13wYg +HC0HJzQ0biolsOk0lpABV3Sn1icY80MTQExV0R2NaJLsgSkXUAAAIQPCQQBDwkEAQAAAAH +c3NoLXJzYQAAAIEAyrB0SnD2N6xvlXYcxRgzTErJZfBE6RIJ7qE0lEF3+aqzP5Ls5J6jjI +UTwI8MHMX0eSxNMjdm1pycqbWS8mq4HwOwcAslObACU7BVm9yaKZVGbzFWr2Yps13wYgHC +0HJzQ0biolsOk0lpABV3Sn1icY80MTQExV0R2NaJLsgSkXUAAAADAQABAAAAgEF6x9bUr3 +ZE6rdqNNknfEsFThL0kA6LmVHSGl5a2Bt8HfRHzuf8p/FZfIwUqmERQm1y9m1moH5skd/x +nMtb1qb7E2r3MQY3YyLbazxdFobT2xaMuPJhoVyMk1qK+etuPHuid+5hRbZEf4H0Y570uS +QDYaT46Ey4bxoqBs4GIuOBAAAAQFS3kACPqAjqvQ41ten8ijO2p8TWVt1ifNQjAG7FRToT +rzeYRF4/MEM8o81sZNQWUjojPeTFS+q9quIBuQxNF58AAABBAOt6fRn+VOFDglwaKUgOFV +d/KORX06Zqb6JiYD93dahhU8YtMsfZi+CwvUCSnT/A8sWneuhYQiJRIv9olswRVAkAAABB +ANxacMIXl9pJ15XvnNkYSt2nCK9gqjD5eHnsbXD8ZIJL2ulgo/DLNdA/2VjdSOCrsQh0Fv +KqXWYK0tidbNSsJQ0AAAAadHJpdmlyQHRyaXZpci1mcm9kby1kZXYtdm0B +-----END OPENSSH PRIVATE KEY----- diff --git a/src/test/mocks/CryptoUtils/pkcs1Rsa.pem b/src/test/mocks/CryptoUtils/pkcs1Rsa.pem new file mode 100644 index 000000000..1996ba9ae --- /dev/null +++ b/src/test/mocks/CryptoUtils/pkcs1Rsa.pem @@ -0,0 +1,9 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIBPAIBAAJBAIVXc55mE8gta8uGxx5Igm8qC/xyCQygknjAMmGmvWRoYbzfFK6U +uPNLtqXocQZiYlWbQ/NFIu3o4nYYrEkD0/kCAwEAAQJAY8uMu7sYCRR9fci0U+Te +wgYNDHXocevnU7o8ursJOhCUrnreeLzjga+BCAOX7eRTJTbVq8IzNG4NbA4+zfb9 +xQIhALm2KfPmdqMCyFwr464/PlSUVO7vu92JDFKJdrHvtJ+7AiEAt88X5H73slpS +xMpcXIOiKrFp2Y3Y+YiowanWlsJFHdsCIQCGXjE4kZVhGJBTcEYKOyQkGjbhsn9h +KNnLlj5VPDSaJQIhAIo/TO0jQhH3NYGl4koDB5mszrw+oA8u5h6jKy9A9tM5AiEA +n8AIUliGLIQQsBPJmDqF3yoDfv6WCMf5Ae8ii4WZHXY= +-----END RSA PRIVATE KEY----- diff --git a/src/test/mocks/CryptoUtils/pkcs8Dsa.pem b/src/test/mocks/CryptoUtils/pkcs8Dsa.pem new file mode 100644 index 000000000..773924105 --- /dev/null +++ b/src/test/mocks/CryptoUtils/pkcs8Dsa.pem @@ -0,0 +1,10 @@ +-----BEGIN PRIVATE KEY----- +MIIBWwIBADCCATMGByqGSM44BAEwggEmAoGBAJFoQkSiL3fBz2gn8ZslqNxDXs9Y +MjVqz0rWhGV1RaI4h13G7fCSF08PDfhO85PykJDQt4fXu0ixzMtvpxe30F88B4eN +xvdCexe7pTQ6y4k0dk7qPkf7y4kB2quHXh40C96zEXbQk8HGB25y+Ejk7QrViD7a +hI1BfUGvVN7ebZxrAh0A2r/w+CvfXziwX9jVEXEgSeAKhT/RZOm+whGs1QKBgC3a +ofB/kY+sKdFcM5GR6HlfQ3s1v0/kgW2eMc8yPpDJD5bTOoqnxVUrUWmlSgUcFP/e +lqOOlJyxjph69w52Ev03DR94GyDaG8kc5mvZI2c4FhwP0/fbxiZ74hH52PQ4gmVF +fblwgD3g/Osvnzgda3bU5ppriblosjeofJQosAKxBB8CHQCmfX6t6QEwqaEzjo10 +OiURiSrPsSH2dizYK1Wa +-----END PRIVATE KEY----- diff --git a/src/test/mocks/CryptoUtils/pkcs8Ecdsa.pem b/src/test/mocks/CryptoUtils/pkcs8Ecdsa.pem new file mode 100644 index 000000000..706e355b1 --- /dev/null +++ b/src/test/mocks/CryptoUtils/pkcs8Ecdsa.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+RC9AdyPFXArluWk +6KMDJZnRg6aXNXx4mTk3uwwKSqihRANCAAQPKZXQxE0Vk5S8Fzn7Vsj0f48Dipjs +2UXax/jF7DXDwHm6jaFAiSJqF9SL2XR2+0DYgb9NkuH44XQhNxkPdlkP +-----END PRIVATE KEY----- diff --git a/src/test/mocks/CryptoUtils/pkcs8Ed25519.pem b/src/test/mocks/CryptoUtils/pkcs8Ed25519.pem new file mode 100644 index 000000000..5f9607ff5 --- /dev/null +++ b/src/test/mocks/CryptoUtils/pkcs8Ed25519.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIBFQXo+EmadRdNDiBgu4hzoO+4+aByeuE5fm5sGuMS/7 +-----END PRIVATE KEY----- diff --git a/src/test/mocks/CryptoUtils/pkcs8Ed25519Enc.pem b/src/test/mocks/CryptoUtils/pkcs8Ed25519Enc.pem new file mode 100644 index 000000000..d72519f29 --- /dev/null +++ b/src/test/mocks/CryptoUtils/pkcs8Ed25519Enc.pem @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAi2MIxmFK5REAICCAAw +DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEFwEZGeKTY0ynhEP4/iSEMsEQEXj +O06EmbuIiRD5j6TlB+W3AAVgMQcyyBCeBEwhB+Me3C0t/DMIOufGNiDAv6gvtuMY ++OobbvMt9UZMt1VtNiU= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/mocks/CryptoUtils/pkcs8Rsa.pem b/src/test/mocks/CryptoUtils/pkcs8Rsa.pem new file mode 100644 index 000000000..4d2ba4a8d --- /dev/null +++ b/src/test/mocks/CryptoUtils/pkcs8Rsa.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM3SXvqSKtVUYEqK +Z00JDKIyGKqNe+mCbi5OCHkljnvvmj9jl9NLyhqEK2SHuSVtP/USCfS+6LF8hSpN +muK+P4KlEXSanGv0ubZFE3ELCvyrp49V+/BpPUaifOx33sySzyQ4Ng9JlZDy5qVS +gcY0fCo0Oxii17Tr94sDhiIy7EynAgMBAAECgYBPeLA1viSN1JONlJ1IcHsRUZiW +miTDSBp1BKpe5LJ8hkqfuTvRWU1DKc97dLB3lBSC8yEnAGPmzDa629Y3QNBSVtC2 +35NmJV0B3M8IX3PIMBYlI4dPjGKuNaHv+Es43fY404F1VY4e5o8emPzydLzTziI5 +NWznzvllwqC3cSAMoQJBAPgAwkrcej4XzBCrL0601U+amq3jbDSRxZUIyiXgtOUR +6+XPh54qMTB2nxef6yV5svuxbcIYEA5lmBciX8fQcnECQQDUdWkDlE3Y9Nu//zG8 +iYyy24tGiXzLGePjavPFhZzPm8lv06Yj/oug1IK+ZwFvGqi4vq9c9XOjs4IfkIt6 +E4yXAkEAmlNulEMq1qTcWasM3z9sVgLxKjART40+lIVJtdRk/P7v+qvvyp6ayXun +eH4NR965YCKDGHr80tipkgvT6nVuIQJAeOMdj6XQ7cPVTde8yxRQ9GwrQUV1tnLS +0lHGxvsRkKeJ5JE/FXf+ptKEWiZuEHPQ0N3fFjurQtgfECrE0f9RnQJAdo6rrN8u +SXEJ3VwldZK6RzERvAkPpD2HK5HAJ7r5+vnLVht1Zt0lL+vlTqiIM3MvalMlZB1v +tPdBth0KXQr6Jg== +-----END PRIVATE KEY----- diff --git a/src/test/snapshots/ops/AuthenticateOps.test.js.snap b/src/test/snapshots/ops/AuthenticateOps.test.js.snap index ae32edc7a..cca5f9728 100644 --- a/src/test/snapshots/ops/AuthenticateOps.test.js.snap +++ b/src/test/snapshots/ops/AuthenticateOps.test.js.snap @@ -1,7 +1,61 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AuthenticateOps getTokens() 1: Authenticate successfully as user 1`] = `"4ad7d57003aee4f"`; +exports[`AuthenticateOps Classic Tests getTokens() 0: Authenticate successfully as user 1`] = ` +{ + "bearerToken": undefined, + "host": "http://openam-frodo-dev.classic.com:8080/am", + "realm": "/", + "subject": Any, + "userSessionToken": { + "expires": 1753976145000, + "from_cache": false, + "realm": "/", + "successUrl": "/am/console", + "tokenId": "pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*", + }, +} +`; -exports[`AuthenticateOps getTokens() 1: Authenticate successfully as user 2`] = `"UU5DAqd_a_XWkhlktTfYW_neiNg.*AAJTSQACMDIAAlNLABxoNTRwQ2E5aEo2TjZCbzQ1ckkyTDNNVzdLcHM9AAR0eXBlAANDVFMAAlMxAAIwMQ..*"`; +exports[`AuthenticateOps Classic Tests getTokens() 0: Authenticate successfully as user 2`] = `"iPlanetDirectoryPro"`; -exports[`AuthenticateOps getTokens() 1: Authenticate successfully as user 3`] = `"eyJ0eXAiOiJKV1QiLCJraWQiOiJKSSt2QkJYWDd5ellQT3N4ZFdvWE16S2x1Q0E9IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJjNWYzY2YzNS00Y2MxLTQyZjktODBiMy01OWUxY2E4NDI1MTAiLCJjdHMiOiJPQVVUSDJfU1RBVEVMRVNTX0dSQU5UIiwiYXV0aF9sZXZlbCI6MCwiYXVkaXRUcmFja2luZ0lkIjoiNGUwMGY0ZDYtYmEzZi00NGNiLWFjNDAtZjRjZTU4MjcyZDEyLTIzMDk4MDgiLCJzdWJuYW1lIjoiYzVmM2NmMzUtNGNjMS00MmY5LTgwYjMtNTllMWNhODQyNTEwIiwiaXNzIjoiaHR0cHM6Ly9vcGVuYW0tdm9sa2VyLWRldi5mb3JnZWJsb2Nrcy5jb206NDQzL2FtL29hdXRoMiIsInRva2VuTmFtZSI6ImFjY2Vzc190b2tlbiIsInRva2VuX3R5cGUiOiJCZWFyZXIiLCJhdXRoR3JhbnRJZCI6IlFnNDBySmxhSTZGdjNDbnVuU1BpSUx0bC1VayIsImF1ZCI6ImlkbUFkbWluQ2xpZW50IiwibmJmIjoxNjcxNTk1NzAyLCJncmFudF90eXBlIjoiYXV0aG9yaXphdGlvbl9jb2RlIiwic2NvcGUiOlsib3BlbmlkIiwiZnI6aWRtOioiXSwiYXV0aF90aW1lIjoxNjcxNTk1NzAyLCJyZWFsbSI6Ii8iLCJleHAiOjE2NzE1OTkzMDIsImlhdCI6MTY3MTU5NTcwMiwiZXhwaXJlc19pbiI6MzYwMCwianRpIjoiZ2E1Um4wQ0txd3V1bThtbVc4cGRkZ0UyOFB3In0.G-FqbQXxxqwAGgbMz8UxVxLkFQZ_0xaZHcPQMEhY5Lj0zNdoIMOxWXXYFrAK6QsIfRInw5K5LDo0-lOZtq46JFDEq-v4mLqR2Llpb66c_2MYZL6WMkPbeE4rPmyAMdqEwvaXMo9Uju-dOgabuN1gn79gFzmKc2oGyuuy16LWeI9rJtIx2rMplxxZN8Xrxuc4-no3u09X0CkVI8F0SIQ64GN8bSP__XFqF3GAeU1qwHI3ItsQJV3HNZnrVDdvFuGz0e3GNuWRAehIwAPluhM80D9zGGHX4DmHDDBbEO_iwuX0uSZXyW3psinUFQaa3nvuq--9xPV0KhGhlR0wXVQAtA"`; +exports[`AuthenticateOps Classic Tests getTokens() 0: Authenticate successfully as user 3`] = `"pQvEBlVtlKosCXrta1USewzkja4.*AAJTSQACMDEAAlNLABwrS0Z0bzBaUkRHbkxiVEtYaTJ6b1NISWZVWXc9AAR0eXBlAANDVFMAAlMxAAA.*"`; + +exports[`AuthenticateOps Classic Tests getTokens() 1: Authenticate successfully using Amster credentials 1`] = ` +{ + "bearerToken": undefined, + "host": "http://openam-frodo-dev.classic.com:8080/am", + "realm": "/", + "subject": Any, + "userSessionToken": { + "expires": 1753976145000, + "from_cache": false, + "realm": "/", + "successUrl": "/am/console", + "tokenId": "ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*", + }, +} +`; + +exports[`AuthenticateOps Classic Tests getTokens() 1: Authenticate successfully using Amster credentials 2`] = `"iPlanetDirectoryPro"`; + +exports[`AuthenticateOps Classic Tests getTokens() 1: Authenticate successfully using Amster credentials 3`] = `"ZGWH6Y3tQsC3rhZHbyjFrn8USBw.*AAJTSQACMDEAAlNLABxHYW5GT1FRREtuTmhLeDRsLzBFUXdyRmU2NHc9AAR0eXBlAANDVFMAAlMxAAA.*"`; + +exports[`AuthenticateOps Classic Tests getTokens() 2: Authenticate successfully using alternative Amster subject and service 1`] = ` +{ + "bearerToken": undefined, + "host": "http://openam-frodo-dev.classic.com:8080/am", + "realm": "/", + "subject": Any, + "userSessionToken": { + "expires": 1753976145000, + "from_cache": false, + "realm": "/", + "successUrl": "/am/console", + "tokenId": "JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*", + }, +} +`; + +exports[`AuthenticateOps Classic Tests getTokens() 2: Authenticate successfully using alternative Amster subject and service 2`] = `"iPlanetDirectoryPro"`; + +exports[`AuthenticateOps Classic Tests getTokens() 2: Authenticate successfully using alternative Amster subject and service 3`] = `"JGVIkcoOtEcCAKTr1g9eUuzmwyk.*AAJTSQACMDEAAlNLABx5Z0NLOFRpbTYzTGRSNHdWM0Z3NElsSUZtODA9AAR0eXBlAANDVFMAAlMxAAA.*"`; diff --git a/src/test/snapshots/utils/CryptoUtils.test.js.snap b/src/test/snapshots/utils/CryptoUtils.test.js.snap new file mode 100644 index 000000000..dda6cf303 --- /dev/null +++ b/src/test/snapshots/utils/CryptoUtils.test.js.snap @@ -0,0 +1,166 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CryptoUtils convertPrivateKeyToPem() 2: PEM PKCS#1 RSA 1`] = ` +"-----BEGIN PRIVATE KEY----- +MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEAhVdznmYTyC1ry4bH +HkiCbyoL/HIJDKCSeMAyYaa9ZGhhvN8UrpS480u2pehxBmJiVZtD80Ui7ejidhis +SQPT+QIDAQABAkBjy4y7uxgJFH19yLRT5N7CBg0Mdehx6+dTujy6uwk6EJSuet54 +vOOBr4EIA5ft5FMlNtWrwjM0bg1sDj7N9v3FAiEAubYp8+Z2owLIXCvjrj8+VJRU +7u+73YkMUol2se+0n7sCIQC3zxfkfveyWlLEylxcg6IqsWnZjdj5iKjBqdaWwkUd +2wIhAIZeMTiRlWEYkFNwRgo7JCQaNuGyf2Eo2cuWPlU8NJolAiEAij9M7SNCEfc1 +gaXiSgMHmazOvD6gDy7mHqMrL0D20zkCIQCfwAhSWIYshBCwE8mYOoXfKgN+/pYI +x/kB7yKLhZkddg== +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 3: PEM PKCS#8 RSA 1`] = ` +"-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM3SXvqSKtVUYEqK +Z00JDKIyGKqNe+mCbi5OCHkljnvvmj9jl9NLyhqEK2SHuSVtP/USCfS+6LF8hSpN +muK+P4KlEXSanGv0ubZFE3ELCvyrp49V+/BpPUaifOx33sySzyQ4Ng9JlZDy5qVS +gcY0fCo0Oxii17Tr94sDhiIy7EynAgMBAAECgYBPeLA1viSN1JONlJ1IcHsRUZiW +miTDSBp1BKpe5LJ8hkqfuTvRWU1DKc97dLB3lBSC8yEnAGPmzDa629Y3QNBSVtC2 +35NmJV0B3M8IX3PIMBYlI4dPjGKuNaHv+Es43fY404F1VY4e5o8emPzydLzTziI5 +NWznzvllwqC3cSAMoQJBAPgAwkrcej4XzBCrL0601U+amq3jbDSRxZUIyiXgtOUR +6+XPh54qMTB2nxef6yV5svuxbcIYEA5lmBciX8fQcnECQQDUdWkDlE3Y9Nu//zG8 +iYyy24tGiXzLGePjavPFhZzPm8lv06Yj/oug1IK+ZwFvGqi4vq9c9XOjs4IfkIt6 +E4yXAkEAmlNulEMq1qTcWasM3z9sVgLxKjART40+lIVJtdRk/P7v+qvvyp6ayXun +eH4NR965YCKDGHr80tipkgvT6nVuIQJAeOMdj6XQ7cPVTde8yxRQ9GwrQUV1tnLS +0lHGxvsRkKeJ5JE/FXf+ptKEWiZuEHPQ0N3fFjurQtgfECrE0f9RnQJAdo6rrN8u +SXEJ3VwldZK6RzERvAkPpD2HK5HAJ7r5+vnLVht1Zt0lL+vlTqiIM3MvalMlZB1v +tPdBth0KXQr6Jg== +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 4: PEM PKCS#8 DSA 1`] = ` +"-----BEGIN PRIVATE KEY----- +MIIBWwIBADCCATMGByqGSM44BAEwggEmAoGBAJFoQkSiL3fBz2gn8ZslqNxDXs9Y +MjVqz0rWhGV1RaI4h13G7fCSF08PDfhO85PykJDQt4fXu0ixzMtvpxe30F88B4eN +xvdCexe7pTQ6y4k0dk7qPkf7y4kB2quHXh40C96zEXbQk8HGB25y+Ejk7QrViD7a +hI1BfUGvVN7ebZxrAh0A2r/w+CvfXziwX9jVEXEgSeAKhT/RZOm+whGs1QKBgC3a +ofB/kY+sKdFcM5GR6HlfQ3s1v0/kgW2eMc8yPpDJD5bTOoqnxVUrUWmlSgUcFP/e +lqOOlJyxjph69w52Ev03DR94GyDaG8kc5mvZI2c4FhwP0/fbxiZ74hH52PQ4gmVF +fblwgD3g/Osvnzgda3bU5ppriblosjeofJQosAKxBB8CHQCmfX6t6QEwqaEzjo10 +OiURiSrPsSH2dizYK1Wa +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 5: PEM PKCS#8 ECDSA 1`] = ` +"-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+RC9AdyPFXArluWk +6KMDJZnRg6aXNXx4mTk3uwwKSqihRANCAAQPKZXQxE0Vk5S8Fzn7Vsj0f48Dipjs +2UXax/jF7DXDwHm6jaFAiSJqF9SL2XR2+0DYgb9NkuH44XQhNxkPdlkP +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 6: PEM PKCS#8 ED25519 1`] = ` +"-----BEGIN PRIVATE KEY----- +MFECAQEwBQYDK2VwBCIEIBFQXo+EmadRdNDiBgu4hzoO+4+aByeuE5fm5sGuMS/7 +gSEAqZWTR+5H4FEh/HkZPNMh0lVmZky5TbpsLWQBaTFnN4A= +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 7: PEM PKCS#8 ED25519 Encrypted 1`] = ` +"-----BEGIN PRIVATE KEY----- +MFECAQEwBQYDK2VwBCIEIHtYTHB4ZGxOo8SGtEfoLqU2L094iBdaNnvdy5i00Hx0 +gSEAvwEr3F6eO4dtGsQcaX3GWhP7jeG4JuIJBzhAPo11D08= +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 8: OpenSSH RSA 1`] = ` +"-----BEGIN PRIVATE KEY----- +MIIB9wIBADANBgkqhkiG9w0BAQEFAASCAeEwggHdAgEAAoGBAMqwdEpw9jesb5V2 +HMUYM0xKyWXwROkSCe6hNJRBd/mqsz+S7OSeo4yFE8CPDBzF9HksTTI3ZtacnKm1 +kvJquB8DsHALJTmwAlOwVZvcmimVRm8xVq9mKbNd8GIBwtByc0NG4qJbDpNJaQAV +d0p9YnGPNDE0BMVdEdjWiS7IEpF1AgMBAAECgYBBesfW1K92ROq3ajTZJ3xLBU4S +9JAOi5lR0hpeWtgbfB30R87n/KfxWXyMFKphEUJtcvZtZqB+bJHf8ZzLW9am+xNq +9zEGN2Mi22s8XRaG09sWjLjyYaFcjJNaivnrbjx7onfuYUW2RH+B9GOe9LkkA2Gk ++OhMuG8aKgbOBiLjgQJBAOt6fRn+VOFDglwaKUgOFVd/KORX06Zqb6JiYD93dahh +U8YtMsfZi+CwvUCSnT/A8sWneuhYQiJRIv9olswRVAkCQQDcWnDCF5faSdeV75zZ +GErdpwivYKow+Xh57G1w/GSCS9rpYKPwyzXQP9lY3Ujgq7EIdBbyql1mCtLYnWzU +rCUNAgEAAgEAAkBUt5AAj6gI6r0ONbXp/IoztqfE1lbdYnzUIwBuxUU6E683mERe +PzBDPKPNbGTUFlI6Iz3kxUvqvariAbkMTRef +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 9: OpenSSH ECDSA 1`] = ` +"-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgLBIrlWvr1ioOIXQp +JaWrvFTDtyPX82rRKy9co6BZNrihRANCAARc7bpTE9HV2gT16w/tEpLsvAzkG3jn +qhlHYY9248/4QfEhB87RI7P2TfS0uw9nnDl5kodOvWqJ5q7irWXV1JMB +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 10: OpenSSH ED25519 1`] = ` +"-----BEGIN PRIVATE KEY----- +MFECAQEwBQYDK2VwBCIEIHr0id/D2hLBp+JnyWEid3TX3uhtTADT4Gu17L/hm+h1 +gSEAA2T+HCZ3LJ6ss5Y8BZGXvzlE+438qqfWA4rWyG8eyd0= +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 11: OpenSSH ED25519 Encrypted 1`] = ` +"-----BEGIN PRIVATE KEY----- +MFECAQEwBQYDK2VwBCIEIPySYKzdixbBuF2SAoYbBTuRx7F8//o+xoMiUPLTX1t/ +gSEASw+zS0kVH8li5P8zBVlVJ5kQ+oPLE33Gwbof8GhmeEs= +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 12: DNSSEC RSASHA512 1`] = ` +"-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPcPQM0iC92t9dqL +4FEzH8O6gsxGdlJGKJ164O9W9w18Ap3BhVy0ORAd5slnm8h83/TpJIJCWcfXNTj8 +t++X5qF3rokORRJRDQFXXTyPr75DAUG18Bkb51VxPwJM+4Nb0/i3ahK9R/8JmD1K +9/bcHQ0JULcFjvw9baaLwZ3+2CN/AgMBAAECgYAAp+u2H7cEWafjcqefz5DwBhi+ +zhkWUtVcxay/Ne3l/URMgu0Ft/zGzC2Gr128hIhbsBrGtcJjQ3ECu7kJS8/y8HaE +0zku19+3eh6bIekgtLzbsgQXkgqjVlXxkkQmG+1C9J3rknMyDsoV9ncSBd1VIHKv +rvRBPysIr9VlbTWc0QJBAP4jL/y9iCmonEMtnOc2LlC4Wgy3B1275qS9fVL4t5Ie +U+sOulPNfh1aFP0aawMUlV7keYJ7986ukz3sGXFThusCQQD43slDimJYl19vSEOt ++iBUB4g2xutLGrELW2c+Gci77EDuYPPSR7vLqeyknwkAO/l1u37G198HWEzg1psz +tpi9AkEA93i4X/sIPtjL4bbnFVdzXnNGbR9F8wbon/9NrKA50fXqbfjXhIeUiWyR +B67UChnfIpqFoISrWP4yr5fbZrmZpQJAL1eaK8oAaV/qXK3/ifICAloodFsk2XWr +ZkCuBCc9g1ovA+nXmBHjTJv3k11ZhfziuJ3BQSxBlhEbFs5cWdsLdQJBAM2FmHTd +o8MzaeRdiyTKqnWt9Qc1IRzA5L9TRP99yLyNgS/7ZsdjDURH2rh0OT9P2vW40EdH +W9Q78lr7GdSK8ZE= +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 13: JWK RSA 1`] = ` +"-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAJK0gMcsV2nh7KKm +GUpEkZSMetaB22HrGFMzjMJsBAuIp1TVVihAgf7Xopuss24TPESNhy9iThnmPJdE +vJAL9gqDU/SaGWr4w+uXA4lRQ9pze9SyRrJZA6V6W6Uv5+5ZKL6KFe2HxfRD2/MM +BGh12THe2aAI0tzKUDxYVGPOf3z1AgMBAAECgYEAhCC/zyLXg8xvxLmWE3aYK1Mu +mx2PiawkV2MZRk3kgsLL+jK+Czi2G+D4Wf78B4qZgCB7fairMNZVAeRtc8riB4s4 +WRNaXKjZkfsjW1UjEmKyO/P598fhURhJCsxpvRZRaDe/D1OT5No/Aj9pAZBqxhl9 +xVZ+MORbXBt8rgUIfUECQQDfwDiQW8URy/6w+rMTrF4birn9Iuh6RUTdwp/8k8V5 +658qKChLtVf/SW2oAEqnpuiE/u499ZPygBPVXVVYJyzRAkEAp9mCXS5mmKkJrCZG +fy0h3yZOJkiDIKi/hwu95VcX/vZ8SM1LKJgwDjPWyvGxPZPC+fXm86Gaj6aN6Q3A +Jc+G5QJAAW42Ll4X1XRLbZA8wtxwQZbSd5bYi6eTuCM9aJz9PzlNbDSThWBkbqJm +jBNsZsP/apJCray7RGqjNdMB761YcQJBAKN7nxXTE+qs+dbKkO9drhgC3ys6hT1S +HvoR3at64Mxcl0Dxc7reQDAVHagdMFyhhs6fS4sQY9J2jtHnqrcpImECQQCXabbD +Nw6mAjjNoPjIIHzzQqDiziJTP+Te9aC+OFauo+aivnM8N1anUsTBEXKhZ0Y8mDa+ +oAvMT/ns1zeE0trk +-----END PRIVATE KEY----- +" +`; + +exports[`CryptoUtils convertPrivateKeyToPem() 14: JWK ECDSA 1`] = ` +"-----BEGIN PRIVATE KEY----- +MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg9BFuyjbx1c8yaaCN +FejA3x7835+7nvdGjoLqnnlz3jugCgYIKoZIzj0DAQehRANCAAR7H/hmaujipb66 +kq9Z4LoAsou2es4hb7C3Lh2BHfpKoZYAcrz8KDbkG5kmp36u/TcEEtjUlvpRKd/A +pN91QJe8 +-----END PRIVATE KEY----- +" +`; diff --git a/src/utils/AutoSetupPolly.ts b/src/utils/AutoSetupPolly.ts index 1d57f9a91..9d313b4b2 100644 --- a/src/utils/AutoSetupPolly.ts +++ b/src/utils/AutoSetupPolly.ts @@ -64,8 +64,20 @@ switch (process.env.FRODO_POLLY_MODE) { } export function setDefaultState( - deployment = Constants.CLOUD_DEPLOYMENT_TYPE_KEY + deployment = Constants.CLOUD_DEPLOYMENT_TYPE_KEY, + resetState = false ) { + // To reset the state if recording multiple tests where previous state can conflict + if (resetState) { + state.setCookieName(undefined); + state.setUserSessionTokenMeta(undefined); + state.setUsername(undefined); + state.setPassword(undefined); + state.setBearerTokenMeta(undefined); + state.setServiceAccountId(undefined); + state.setServiceAccountJwk(undefined); + state.setServiceAccountScope(undefined); + } const classicHostUrl = 'http://openam-frodo-dev.classic.com:8080/am'; const classicRealm = '/'; const cloudHostUrl = 'https://openam-frodo-dev.forgeblocks.com/am'; diff --git a/src/utils/CryptoUtils.test.ts b/src/utils/CryptoUtils.test.ts new file mode 100644 index 000000000..e6b16f2b2 --- /dev/null +++ b/src/utils/CryptoUtils.test.ts @@ -0,0 +1,96 @@ +/** + * Run tests + * + * npm run test:only CryptoUtils + * + * Note: FRODO_DEBUG=1 is optional and enables debug logging for some output + * in case things don't function as expected + */ +import { convertPrivateKeyToPem } from './CryptoUtils'; +import fs from 'fs' +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +describe('CryptoUtils', () => { + describe('convertPrivateKeyToPem()', () => { + test('0: Method is implemented', async () => { + expect(convertPrivateKeyToPem).toBeDefined(); + }); + + function testSuccess(filename: string, usePassphrase = false) { + const key = fs.readFileSync( + path.resolve( + __dirname, + `../test/mocks/CryptoUtils/${filename}` + ), + 'utf8' + ); + const pem = convertPrivateKeyToPem({ + key, + passphrase: usePassphrase ? 'test' : undefined + }); + expect(pem).toMatchSnapshot(); + } + + test('1: Test not providing a key', () => { + expect(() => convertPrivateKeyToPem({ key: '' })).toThrow('Private key not provided.'); + }); + + test('2: PEM PKCS#1 RSA', () => { + testSuccess('pkcs1Rsa.pem'); + }); + + test('3: PEM PKCS#8 RSA', () => { + testSuccess('pkcs8Rsa.pem'); + }); + + test('4: PEM PKCS#8 DSA', () => { + testSuccess('pkcs8Dsa.pem'); + }); + + test('5: PEM PKCS#8 ECDSA', () => { + testSuccess('pkcs8Ecdsa.pem'); + }); + + test('6: PEM PKCS#8 ED25519', () => { + testSuccess('pkcs8Ed25519.pem'); + }); + + test('7: PEM PKCS#8 ED25519 Encrypted', () => { + testSuccess('pkcs8Ed25519Enc.pem', true); + }); + + test('8: OpenSSH RSA', () => { + testSuccess('opensshRsa'); + }); + + test('9: OpenSSH ECDSA', () => { + testSuccess('opensshEcdsa'); + }); + + test('10: OpenSSH ED25519', () => { + testSuccess('opensshEd25519'); + }); + + test('11: OpenSSH ED25519 Encrypted', () => { + testSuccess('opensshEd25519Enc', true); + }); + + test('12: DNSSEC RSASHA512', () => { + testSuccess('dnssecRsa.private'); + }); + + test('13: JWK RSA', () => { + testSuccess('jwkRsa.jwk'); + }); + + test('14: JWK ECDSA', () => { + testSuccess('jwkEcdsa.jwk'); + }); + + // Can't find way to test this yet since it ends the program when it fails + test.todo('15: Test incorrect passphrase'); + }); +}); diff --git a/src/utils/CryptoUtils.ts b/src/utils/CryptoUtils.ts new file mode 100644 index 000000000..573ae238f --- /dev/null +++ b/src/utils/CryptoUtils.ts @@ -0,0 +1,76 @@ +import jwkToPem from 'jwk-to-pem'; +import sshpk from 'sshpk'; + +import { FrodoError } from '../ops/FrodoError'; + +export type FrodoCrypto = { + /** + * Parses a private key and returns it as an unencrypted PKCS#8 PEM encoded string + * Supported private key formats include: + * - PEM (both PKCS#1 and PKCS#8 variants) + * - OpenSSH + * - DNSSEC + * - JWK + * @param {string} key The private key + * @param {string | undefined} passphrase The passphrase for the private key if it is encrypted + * @param {string | undefined} name The name of the private key (i.e. the name of the file it came from, if applicable); used for error handling + * @returns {string} The unencrypted PKCS#8 PEM encoded private key + */ + convertPrivateKeyToPem( + key: string, + passphrase?: string, + name?: string + ): string; +}; + +export default (): FrodoCrypto => { + return { + convertPrivateKeyToPem( + key: string, + passphrase?: string, + name?: string + ): string { + return convertPrivateKeyToPem({ key, passphrase, name }); + }, + }; +}; + +/** + * Parses a private key and returns it as an unencrypted PKCS#8 PEM encoded string + * Supported private key formats include: + * - PEM (both PKCS#1 and PKCS#8 variants) + * - OpenSSH + * - DNSSEC + * - JWK + * @param {string} key The private key + * @param {string | undefined} passphrase The passphrase for the private key if it is encrypted + * @param {string | undefined} name The name of the private key (i.e. the name of the file it came from, if applicable); used for error handling + * @returns {string} The unencrypted PKCS#8 PEM encoded private key + */ +export function convertPrivateKeyToPem({ + key, + passphrase, + name, +}: { + key: string; + passphrase?: string; + name?: string; +}): string { + if (!key) { + throw new FrodoError(`Private key${name ? ` ${name}` : ''} not provided.`); + } + // Try converting JWK to PEM PKCS#8 format. + try { + const jwk = JSON.parse(key); + // Need true flag to get the full private key + return jwkToPem(jwk, { private: true }); + } catch (e) { + /* Ignore error since private key may still be a supported format */ + } + // Will automatically detect the format the private key is in and parse it if it is able to + const privateKey = sshpk.parsePrivateKey(key, 'auto', { + filename: name, + passphrase, + }); + return privateKey.toString('pkcs8'); +}