From dacdde99b5df2e98db3dd52c955d07ea93ff68ab Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 13 Jul 2026 13:27:28 -0600 Subject: [PATCH 1/3] feat(journey-client): add-create-journey-step-to-api Add createJourneyStep to the journey-client api --- .changeset/tender-signs-crash.md | 5 + .../api-report/journey-client.api.md | 1004 +++++++++-------- packages/journey-client/src/index.ts | 5 +- 3 files changed, 513 insertions(+), 501 deletions(-) create mode 100644 .changeset/tender-signs-crash.md diff --git a/.changeset/tender-signs-crash.md b/.changeset/tender-signs-crash.md new file mode 100644 index 0000000000..bc31769664 --- /dev/null +++ b/.changeset/tender-signs-crash.md @@ -0,0 +1,5 @@ +--- +'@forgerock/journey-client': patch +--- + +export create-journey-step so it's easy to deserialize a step after it's been serialized diff --git a/packages/journey-client/api-report/journey-client.api.md b/packages/journey-client/api-report/journey-client.api.md index 35a4a51dbe..b9c15d06b0 100644 --- a/packages/journey-client/api-report/journey-client.api.md +++ b/packages/journey-client/api-report/journey-client.api.md @@ -1,500 +1,504 @@ -## API Report File for "@forgerock/journey-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import { ActionTypes } from '@forgerock/sdk-request-middleware'; -import { AuthResponse } from '@forgerock/sdk-types'; -import { Callback } from '@forgerock/sdk-types'; -import { CallbackType } from '@forgerock/sdk-types'; -import { callbackType } from '@forgerock/sdk-types'; -import { createWellknownError } from '@forgerock/sdk-utilities'; -import { CustomLogger } from '@forgerock/sdk-logger'; -import { FailedPolicyRequirement } from '@forgerock/sdk-types'; -import { FailureDetail } from '@forgerock/sdk-types'; -import { GenericError } from '@forgerock/sdk-types'; -import { isValidWellknownUrl } from '@forgerock/sdk-utilities'; -import { JourneyClientConfig } from '@forgerock/sdk-types'; -import { JourneyServerConfig } from '@forgerock/sdk-types'; -import { LogLevel } from '@forgerock/sdk-logger'; -import { NameValue } from '@forgerock/sdk-types'; -import { PolicyKey } from '@forgerock/sdk-types'; -import { PolicyParams } from '@forgerock/sdk-types'; -import { PolicyRequirement } from '@forgerock/sdk-types'; -import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; -import { Step } from '@forgerock/sdk-types'; -import { StepDetail } from '@forgerock/sdk-types'; -import { StepType } from '@forgerock/sdk-types'; -import { WellknownResponse } from '@forgerock/sdk-types'; - -export { ActionTypes } - -// @public -export class AttributeInputCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getName(): string; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setValidateOnly(value: boolean): void; - setValue(value: T): void; -} - -export { AuthResponse } - -// @public -export class BaseCallback { - constructor(payload: Callback); - getInputValue(selector?: number | string): unknown; - getOutputByName(name: string, defaultValue: T): T; - getOutputValue(selector?: number | string): unknown; - getType(): CallbackType; - // (undocumented) - payload: Callback; - setInputValue(value: unknown, selector?: number | string | RegExp): void; -} - -export { Callback } - -// @public (undocumented) -export type CallbackFactory = (callback: Callback) => BaseCallback; - -export { CallbackType } - -export { callbackType } - -// @public -export class ChoiceCallback extends BaseCallback { - constructor(payload: Callback); - getChoices(): string[]; - getDefaultChoice(): number; - getPrompt(): string; - // (undocumented) - payload: Callback; - setChoiceIndex(index: number): void; - setChoiceValue(value: string): void; -} - -// @public -export class ConfirmationCallback extends BaseCallback { - constructor(payload: Callback); - getDefaultOption(): number; - getMessageType(): number; - getOptions(): string[]; - getOptionType(): number; - getPrompt(): string; - // (undocumented) - payload: Callback; - setOptionIndex(index: number): void; - setOptionValue(value: string): void; -} - -// @public (undocumented) -export function createCallback(callback: Callback): BaseCallback; - -export { createWellknownError } - -export { CustomLogger } - -// @public -export class DeviceProfileCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - isLocationRequired(): boolean; - isMetadataRequired(): boolean; - // (undocumented) - payload: Callback; - setProfile(profile: DeviceProfileData): void; -} - -// @public (undocumented) -export interface DeviceProfileData { - // (undocumented) - identifier: string; - // (undocumented) - location?: Geolocation_2 | Record; - // (undocumented) - metadata?: { - hardware: { - display: { - [key: string]: string | number | null; - }; - [key: string]: any; - }; - browser: { - [key: string]: string | number | null; - }; - platform: { - [key: string]: string | number | null; - }; - }; -} - -export { FailedPolicyRequirement } - -export { FailureDetail } - -export { GenericError } - -// @public (undocumented) -interface Geolocation_2 { - // (undocumented) - latitude: number; - // (undocumented) - longitude: number; -} -export { Geolocation_2 as Geolocation } - -// @public -export class HiddenValueCallback extends BaseCallback { - constructor(payload: Callback); - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export interface IdPValue { - // (undocumented) - provider: string; - // (undocumented) - uiConfig: { - [key: string]: string; - }; -} - -// @internal -export interface InternalJourneyClientConfig { - // (undocumented) - error?: GenericError; - // (undocumented) - serverConfig: ResolvedServerConfig; -} - -export { isValidWellknownUrl } - -// @public -export function journey(input: { - config: JourneyClientConfig; - requestMiddleware?: RequestMiddleware[]; - logger?: { - level: LogLevel; - custom?: CustomLogger; - }; -}): Promise; - -// @public -export interface JourneyClient { - // (undocumented) - next: (step: JourneyStep, options?: NextOptions) => Promise; - // (undocumented) - redirect: (step: JourneyStep) => Promise; - // (undocumented) - resume: (url: string, options?: ResumeOptions) => Promise; - // (undocumented) - start: (options?: StartParam) => Promise; - // (undocumented) - subscribe: (listener: () => void) => () => void; - // (undocumented) - terminate: (options?: { - query?: Record; - }) => Promise; -} - -export { JourneyClientConfig } - -// @public (undocumented) -export type JourneyLoginFailure = AuthResponse & { - type: StepType.LoginFailure; - payload: Step; - getCode: () => number; - getDetail: () => FailureDetail | undefined; - getMessage: () => string | undefined; - getProcessedMessage: (messageCreator?: MessageCreator) => ProcessedPropertyError[]; - getReason: () => string | undefined; -}; - -// @public (undocumented) -export type JourneyLoginSuccess = AuthResponse & { - type: StepType.LoginSuccess; - payload: Step; - getRealm: () => string | undefined; - getSessionToken: () => string | undefined; - getSuccessUrl: () => string | undefined; -}; - -// @public (undocumented) -export type JourneyResult = JourneyStep | JourneyLoginSuccess | JourneyLoginFailure | GenericError; - -export { JourneyServerConfig } - -// @public (undocumented) -export type JourneyStep = AuthResponse & { - type: StepType.Step; - payload: Step; - callbacks: BaseCallback[]; - getCallbackOfType: (type: CallbackType) => T; - getCallbacksOfType: (type: CallbackType) => T[]; - setCallbackValue: (type: CallbackType, value: unknown) => void; - getDescription: () => string | undefined; - getHeader: () => string | undefined; - getStage: () => string | undefined; -}; - -// @public -export class KbaCreateCallback extends BaseCallback { - constructor(payload: Callback); - getPredefinedQuestions(): string[]; - getPrompt(): string; - isAllowedUserDefinedQuestions(): boolean; - // (undocumented) - payload: Callback; - setAnswer(answer: string): void; - setQuestion(question: string): void; -} - -export { LogLevel } - -// @public (undocumented) -export interface MessageCreator { - // (undocumented) - [key: string]: (propertyName: string, params?: { - [key: string]: unknown; - }) => string; -} - -// @public -export class MetadataCallback extends BaseCallback { - constructor(payload: Callback); - getData(): T; - // (undocumented) - payload: Callback; -} - -// @public -export class NameCallback extends BaseCallback { - constructor(payload: Callback); - getPrompt(): string; - // (undocumented) - payload: Callback; - setName(name: string): void; -} - -export { NameValue } - -// @public (undocumented) -export interface NextOptions { - // (undocumented) - query?: Record; -} - -// @public -export class PasswordCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): string[]; - getPrompt(): string; - // (undocumented) - payload: Callback; - setPassword(password: string): void; -} - -// @public -export class PingOneProtectEvaluationCallback extends BaseCallback { - constructor(payload: Callback); - getPauseBehavioralData(): boolean; - // (undocumented) - payload: Callback; - setClientError(errorMessage: string): void; - setData(data: string): void; -} - -// @public -export class PingOneProtectInitializeCallback extends BaseCallback { - constructor(payload: Callback); - getConfig(): Record | { - envId: string; - consoleLogEnabled: boolean; - deviceAttributesToIgnore: string[]; - customHost: string; - lazyMetadata: boolean; - behavioralDataCollection: boolean; - deviceKeyRsyncIntervals: number; - enableTrust: boolean; - disableTags: boolean; - disableHub: boolean; - }; - // (undocumented) - payload: Callback; - // (undocumented) - setClientError(errorMessage: string): void; -} - -export { PolicyKey } - -export { PolicyParams } - -export { PolicyRequirement } - -// @public -export class PollingWaitCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - getWaitTime(): number; - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export interface ProcessedPropertyError { - // (undocumented) - detail: FailedPolicyRequirement; - // (undocumented) - messages: string[]; -} - -// @public -export class ReCaptchaCallback extends BaseCallback { - constructor(payload: Callback); - getSiteKey(): string; - // (undocumented) - payload: Callback; - setResult(result: string): void; -} - -// @public -export class ReCaptchaEnterpriseCallback extends BaseCallback { - constructor(payload: Callback); - getApiUrl(): string; - getElementClass(): string; - getSiteKey(): string; - // (undocumented) - payload: Callback; - setAction(action: string): void; - setClientError(error: string): void; - setPayload(payload: unknown): void; - setResult(result: string): void; -} - -// @public -export class RedirectCallback extends BaseCallback { - constructor(payload: Callback); - getRedirectUrl(): string; - // (undocumented) - payload: Callback; -} - -export { RequestMiddleware } - -// @public -export interface ResolvedServerConfig { - // (undocumented) - baseUrl: string; - // (undocumented) - paths: { - authenticate: string; - sessions: string; - }; -} - -// @public (undocumented) -export interface ResumeOptions { - // (undocumented) - journey?: string; - // (undocumented) - query?: Record; -} - -// @public -export class SelectIdPCallback extends BaseCallback { - constructor(payload: Callback); - getProviders(): IdPValue[]; - // (undocumented) - payload: Callback; - setProvider(value: string): void; -} - -// @public (undocumented) -export interface StartParam { - // (undocumented) - journey: string; - // (undocumented) - query?: Record; -} - -export { Step } - -export { StepDetail } - -export { StepType } - -// @public -export class SuspendedTextOutputCallback extends TextOutputCallback { - constructor(payload: Callback); - // (undocumented) - payload: Callback; -} - -// @public -export class TermsAndConditionsCallback extends BaseCallback { - constructor(payload: Callback); - getCreateDate(): Date | null; - getTerms(): string; - getVersion(): string; - // (undocumented) - payload: Callback; - setAccepted(accepted?: boolean): void; -} - -// @public -export class TextInputCallback extends BaseCallback { - constructor(payload: Callback); - getPrompt(): string; - // (undocumented) - payload: Callback; - setInput(input: string): void; -} - -// @public -export class TextOutputCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - getMessageType(): string; - // (undocumented) - payload: Callback; -} - -// @public -export class ValidatedCreatePasswordCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setPassword(password: string): void; - setValidateOnly(value: boolean): void; -} - -// @public -export class ValidatedCreateUsernameCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setName(name: string): void; - setValidateOnly(value: boolean): void; -} - -export { WellknownResponse } - -// (No @packageDocumentation comment for this package) - -``` +## API Report File for "@forgerock/journey-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ActionTypes } from '@forgerock/sdk-request-middleware'; +import { AuthResponse } from '@forgerock/sdk-types'; +import { Callback } from '@forgerock/sdk-types'; +import { CallbackType } from '@forgerock/sdk-types'; +import { callbackType } from '@forgerock/sdk-types'; +import { createWellknownError } from '@forgerock/sdk-utilities'; +import { CustomLogger } from '@forgerock/sdk-logger'; +import { FailedPolicyRequirement } from '@forgerock/sdk-types'; +import { FailureDetail } from '@forgerock/sdk-types'; +import { GenericError } from '@forgerock/sdk-types'; +import { isValidWellknownUrl } from '@forgerock/sdk-utilities'; +import { JourneyClientConfig } from '@forgerock/sdk-types'; +import { JourneyServerConfig } from '@forgerock/sdk-types'; +import { LogLevel } from '@forgerock/sdk-logger'; +import { NameValue } from '@forgerock/sdk-types'; +import { PolicyKey } from '@forgerock/sdk-types'; +import { PolicyParams } from '@forgerock/sdk-types'; +import { PolicyRequirement } from '@forgerock/sdk-types'; +import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import { Step } from '@forgerock/sdk-types'; +import { StepDetail } from '@forgerock/sdk-types'; +import { StepType } from '@forgerock/sdk-types'; +import { WellknownResponse } from '@forgerock/sdk-types'; + +export { ActionTypes }; + +// @public +export class AttributeInputCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getName(): string; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setValidateOnly(value: boolean): void; + setValue(value: T): void; +} + +export { AuthResponse }; + +// @public +export class BaseCallback { + constructor(payload: Callback); + getInputValue(selector?: number | string): unknown; + getOutputByName(name: string, defaultValue: T): T; + getOutputValue(selector?: number | string): unknown; + getType(): CallbackType; + // (undocumented) + payload: Callback; + setInputValue(value: unknown, selector?: number | string | RegExp): void; +} + +export { Callback }; + +// @public (undocumented) +export type CallbackFactory = (callback: Callback) => BaseCallback; + +export { CallbackType }; + +export { callbackType }; + +// @public +export class ChoiceCallback extends BaseCallback { + constructor(payload: Callback); + getChoices(): string[]; + getDefaultChoice(): number; + getPrompt(): string; + // (undocumented) + payload: Callback; + setChoiceIndex(index: number): void; + setChoiceValue(value: string): void; +} + +// @public +export class ConfirmationCallback extends BaseCallback { + constructor(payload: Callback); + getDefaultOption(): number; + getMessageType(): number; + getOptions(): string[]; + getOptionType(): number; + getPrompt(): string; + // (undocumented) + payload: Callback; + setOptionIndex(index: number): void; + setOptionValue(value: string): void; +} + +// @public (undocumented) +export function createCallback(callback: Callback): BaseCallback; + +// @public (undocumented) +export function createJourneyStep(payload: Step, callbackFactory?: CallbackFactory): JourneyStep; + +export { createWellknownError }; + +export { CustomLogger }; + +// @public +export class DeviceProfileCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + isLocationRequired(): boolean; + isMetadataRequired(): boolean; + // (undocumented) + payload: Callback; + setProfile(profile: DeviceProfileData): void; +} + +// @public (undocumented) +export interface DeviceProfileData { + // (undocumented) + identifier: string; + // (undocumented) + location?: Geolocation_2 | Record; + // (undocumented) + metadata?: { + hardware: { + display: { + [key: string]: string | number | null; + }; + [key: string]: any; + }; + browser: { + [key: string]: string | number | null; + }; + platform: { + [key: string]: string | number | null; + }; + }; +} + +export { FailedPolicyRequirement }; + +export { FailureDetail }; + +export { GenericError }; + +// @public (undocumented) +interface Geolocation_2 { + // (undocumented) + latitude: number; + // (undocumented) + longitude: number; +} +export { Geolocation_2 as Geolocation }; + +// @public +export class HiddenValueCallback extends BaseCallback { + constructor(payload: Callback); + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export interface IdPValue { + // (undocumented) + provider: string; + // (undocumented) + uiConfig: { + [key: string]: string; + }; +} + +// @internal +export interface InternalJourneyClientConfig { + // (undocumented) + error?: GenericError; + // (undocumented) + serverConfig: ResolvedServerConfig; +} + +export { isValidWellknownUrl }; + +// @public +export function journey(input: { + config: JourneyClientConfig; + requestMiddleware?: RequestMiddleware[]; + logger?: { + level: LogLevel; + custom?: CustomLogger; + }; +}): Promise; + +// @public +export interface JourneyClient { + // (undocumented) + next: (step: JourneyStep, options?: NextOptions) => Promise; + // (undocumented) + redirect: (step: JourneyStep) => Promise; + // (undocumented) + resume: (url: string, options?: ResumeOptions) => Promise; + // (undocumented) + start: (options?: StartParam) => Promise; + // (undocumented) + subscribe: (listener: () => void) => () => void; + // (undocumented) + terminate: (options?: { query?: Record }) => Promise; +} + +export { JourneyClientConfig }; + +// @public (undocumented) +export type JourneyLoginFailure = AuthResponse & { + type: StepType.LoginFailure; + payload: Step; + getCode: () => number; + getDetail: () => FailureDetail | undefined; + getMessage: () => string | undefined; + getProcessedMessage: (messageCreator?: MessageCreator) => ProcessedPropertyError[]; + getReason: () => string | undefined; +}; + +// @public (undocumented) +export type JourneyLoginSuccess = AuthResponse & { + type: StepType.LoginSuccess; + payload: Step; + getRealm: () => string | undefined; + getSessionToken: () => string | undefined; + getSuccessUrl: () => string | undefined; +}; + +// @public (undocumented) +export type JourneyResult = JourneyStep | JourneyLoginSuccess | JourneyLoginFailure | GenericError; + +export { JourneyServerConfig }; + +// @public (undocumented) +export type JourneyStep = AuthResponse & { + type: StepType.Step; + payload: Step; + callbacks: BaseCallback[]; + getCallbackOfType: (type: CallbackType) => T; + getCallbacksOfType: (type: CallbackType) => T[]; + setCallbackValue: (type: CallbackType, value: unknown) => void; + getDescription: () => string | undefined; + getHeader: () => string | undefined; + getStage: () => string | undefined; +}; + +// @public +export class KbaCreateCallback extends BaseCallback { + constructor(payload: Callback); + getPredefinedQuestions(): string[]; + getPrompt(): string; + isAllowedUserDefinedQuestions(): boolean; + // (undocumented) + payload: Callback; + setAnswer(answer: string): void; + setQuestion(question: string): void; +} + +export { LogLevel }; + +// @public (undocumented) +export interface MessageCreator { + // (undocumented) + [key: string]: ( + propertyName: string, + params?: { + [key: string]: unknown; + }, + ) => string; +} + +// @public +export class MetadataCallback extends BaseCallback { + constructor(payload: Callback); + getData(): T; + // (undocumented) + payload: Callback; +} + +// @public +export class NameCallback extends BaseCallback { + constructor(payload: Callback); + getPrompt(): string; + // (undocumented) + payload: Callback; + setName(name: string): void; +} + +export { NameValue }; + +// @public (undocumented) +export interface NextOptions { + // (undocumented) + query?: Record; +} + +// @public +export class PasswordCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): string[]; + getPrompt(): string; + // (undocumented) + payload: Callback; + setPassword(password: string): void; +} + +// @public +export class PingOneProtectEvaluationCallback extends BaseCallback { + constructor(payload: Callback); + getPauseBehavioralData(): boolean; + // (undocumented) + payload: Callback; + setClientError(errorMessage: string): void; + setData(data: string): void; +} + +// @public +export class PingOneProtectInitializeCallback extends BaseCallback { + constructor(payload: Callback); + getConfig(): + | Record + | { + envId: string; + consoleLogEnabled: boolean; + deviceAttributesToIgnore: string[]; + customHost: string; + lazyMetadata: boolean; + behavioralDataCollection: boolean; + deviceKeyRsyncIntervals: number; + enableTrust: boolean; + disableTags: boolean; + disableHub: boolean; + }; + // (undocumented) + payload: Callback; + // (undocumented) + setClientError(errorMessage: string): void; +} + +export { PolicyKey }; + +export { PolicyParams }; + +export { PolicyRequirement }; + +// @public +export class PollingWaitCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + getWaitTime(): number; + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export interface ProcessedPropertyError { + // (undocumented) + detail: FailedPolicyRequirement; + // (undocumented) + messages: string[]; +} + +// @public +export class ReCaptchaCallback extends BaseCallback { + constructor(payload: Callback); + getSiteKey(): string; + // (undocumented) + payload: Callback; + setResult(result: string): void; +} + +// @public +export class ReCaptchaEnterpriseCallback extends BaseCallback { + constructor(payload: Callback); + getApiUrl(): string; + getElementClass(): string; + getSiteKey(): string; + // (undocumented) + payload: Callback; + setAction(action: string): void; + setClientError(error: string): void; + setPayload(payload: unknown): void; + setResult(result: string): void; +} + +// @public +export class RedirectCallback extends BaseCallback { + constructor(payload: Callback); + getRedirectUrl(): string; + // (undocumented) + payload: Callback; +} + +export { RequestMiddleware }; + +// @public +export interface ResolvedServerConfig { + // (undocumented) + baseUrl: string; + // (undocumented) + paths: { + authenticate: string; + sessions: string; + }; +} + +// @public (undocumented) +export interface ResumeOptions { + // (undocumented) + journey?: string; + // (undocumented) + query?: Record; +} + +// @public +export class SelectIdPCallback extends BaseCallback { + constructor(payload: Callback); + getProviders(): IdPValue[]; + // (undocumented) + payload: Callback; + setProvider(value: string): void; +} + +// @public (undocumented) +export interface StartParam { + // (undocumented) + journey: string; + // (undocumented) + query?: Record; +} + +export { Step }; + +export { StepDetail }; + +export { StepType }; + +// @public +export class SuspendedTextOutputCallback extends TextOutputCallback { + constructor(payload: Callback); + // (undocumented) + payload: Callback; +} + +// @public +export class TermsAndConditionsCallback extends BaseCallback { + constructor(payload: Callback); + getCreateDate(): Date | null; + getTerms(): string; + getVersion(): string; + // (undocumented) + payload: Callback; + setAccepted(accepted?: boolean): void; +} + +// @public +export class TextInputCallback extends BaseCallback { + constructor(payload: Callback); + getPrompt(): string; + // (undocumented) + payload: Callback; + setInput(input: string): void; +} + +// @public +export class TextOutputCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + getMessageType(): string; + // (undocumented) + payload: Callback; +} + +// @public +export class ValidatedCreatePasswordCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setPassword(password: string): void; + setValidateOnly(value: boolean): void; +} + +// @public +export class ValidatedCreateUsernameCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setName(name: string): void; + setValidateOnly(value: boolean): void; +} + +export { WellknownResponse }; + +// (No @packageDocumentation comment for this package) +``` diff --git a/packages/journey-client/src/index.ts b/packages/journey-client/src/index.ts index ad6630ab7d..d26a3fa0b9 100644 --- a/packages/journey-client/src/index.ts +++ b/packages/journey-client/src/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2025 Ping Identity Corporation. All rights reserved. + * Copyright (c) 2020 - 2026 Ping Identity Corporation. All rights reserved. * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,5 +8,8 @@ export * from './lib/client.store.js'; export * from './types.js'; +// required to help deserialize a step after it's been serialized +export { createJourneyStep } from './lib/step.utils.js'; + // Re-export types from internal packages that consumers need export { callbackType } from '@forgerock/sdk-types'; From a59fed08433dcaee740670ba58af88151dd092b1 Mon Sep 17 00:00:00 2001 From: Justin Lowery Date: Mon, 13 Jul 2026 16:49:56 -0500 Subject: [PATCH 2/3] fix(journey-client): add back baseUrl & export createJourneyStep for edge cases --- .../services/delete-webauthn-device.ts | 5 +- .../api-report/journey-client._utils.api.md | 44 + .../api-report/journey-client.api.md | 3 + .../api-report/journey-client.types.api.md | 988 +++++++++--------- packages/journey-client/package.json | 1 + packages/journey-client/src/_utils.ts | 9 + packages/journey-client/src/index.ts | 3 - .../journey-client/src/lib/client.store.ts | 53 +- .../src/lib/config.slice.test.ts | 5 +- .../journey-client/src/lib/config.slice.ts | 24 +- .../src/lib/config.types.test-d.ts | 32 +- .../journey-client/src/lib/config.types.ts | 6 +- packages/sdk-types/src/lib/config.types.ts | 7 +- .../src/lib/config/config.test.ts | 9 +- 14 files changed, 656 insertions(+), 533 deletions(-) create mode 100644 packages/journey-client/api-report/journey-client._utils.api.md create mode 100644 packages/journey-client/src/_utils.ts diff --git a/e2e/journey-app/services/delete-webauthn-device.ts b/e2e/journey-app/services/delete-webauthn-device.ts index e8c5e0827b..a7d05d3a61 100644 --- a/e2e/journey-app/services/delete-webauthn-device.ts +++ b/e2e/journey-app/services/delete-webauthn-device.ts @@ -92,7 +92,10 @@ export async function deleteWebAuthnDevice(config: JourneyClientConfig): Promise return 'No credential id found. Register a WebAuthn device first.'; } - const wellknown = config.serverConfig.wellknown; + const wellknown = 'wellknown' in config.serverConfig ? config.serverConfig.wellknown : undefined; + if (!wellknown) { + throw new Error('serverConfig.wellknown is required to delete a WebAuthn device.'); + } const baseUrl = getBaseUrlFromWellknown(wellknown); const realmUrlPath = getRealmUrlPathFromWellknown(wellknown); const userId = await getUserIdFromSession(baseUrl, realmUrlPath); diff --git a/packages/journey-client/api-report/journey-client._utils.api.md b/packages/journey-client/api-report/journey-client._utils.api.md new file mode 100644 index 0000000000..d10450dfee --- /dev/null +++ b/packages/journey-client/api-report/journey-client._utils.api.md @@ -0,0 +1,44 @@ +## API Report File for "@forgerock/journey-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import type { AuthResponse } from '@forgerock/sdk-types'; +import type { Callback } from '@forgerock/sdk-types'; +import type { CallbackType } from '@forgerock/sdk-types'; +import { Step } from '@forgerock/sdk-types'; +import type { StepType } from '@forgerock/sdk-types'; + +// @public +export class BaseCallback { + constructor(payload: Callback); + getInputValue(selector?: number | string): unknown; + getOutputByName(name: string, defaultValue: T): T; + getOutputValue(selector?: number | string): unknown; + getType(): CallbackType; + // (undocumented) + payload: Callback; + setInputValue(value: unknown, selector?: number | string | RegExp): void; +} + +// @public (undocumented) +export type CallbackFactory = (callback: Callback) => BaseCallback; + +// @public (undocumented) +export function createJourneyStep(payload: Step, callbackFactory?: CallbackFactory): JourneyStep; + +// @public (undocumented) +export type JourneyStep = AuthResponse & { + type: StepType.Step; + payload: Step; + callbacks: BaseCallback[]; + getCallbackOfType: (type: CallbackType) => T; + getCallbacksOfType: (type: CallbackType) => T[]; + setCallbackValue: (type: CallbackType, value: unknown) => void; + getDescription: () => string | undefined; + getHeader: () => string | undefined; + getStage: () => string | undefined; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/packages/journey-client/api-report/journey-client.api.md b/packages/journey-client/api-report/journey-client.api.md index b9c15d06b0..a19e09c0c5 100644 --- a/packages/journey-client/api-report/journey-client.api.md +++ b/packages/journey-client/api-report/journey-client.api.md @@ -16,6 +16,7 @@ import { GenericError } from '@forgerock/sdk-types'; import { isValidWellknownUrl } from '@forgerock/sdk-utilities'; import { JourneyClientConfig } from '@forgerock/sdk-types'; import { JourneyServerConfig } from '@forgerock/sdk-types'; +import { LegacyServerConfig } from '@forgerock/sdk-types'; import { LogLevel } from '@forgerock/sdk-logger'; import { NameValue } from '@forgerock/sdk-types'; import { PolicyKey } from '@forgerock/sdk-types'; @@ -256,6 +257,8 @@ export class KbaCreateCallback extends BaseCallback { setQuestion(question: string): void; } +export { LegacyServerConfig }; + export { LogLevel }; // @public (undocumented) diff --git a/packages/journey-client/api-report/journey-client.types.api.md b/packages/journey-client/api-report/journey-client.types.api.md index d9219a9710..59ff118690 100644 --- a/packages/journey-client/api-report/journey-client.types.api.md +++ b/packages/journey-client/api-report/journey-client.types.api.md @@ -1,487 +1,501 @@ -## API Report File for "@forgerock/journey-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import { ActionTypes } from '@forgerock/sdk-request-middleware'; -import { AuthResponse } from '@forgerock/sdk-types'; -import { Callback } from '@forgerock/sdk-types'; -import { CallbackType } from '@forgerock/sdk-types'; -import { createWellknownError } from '@forgerock/sdk-utilities'; -import { CustomLogger } from '@forgerock/sdk-logger'; -import { FailedPolicyRequirement } from '@forgerock/sdk-types'; -import { FailureDetail } from '@forgerock/sdk-types'; -import { GenericError } from '@forgerock/sdk-types'; -import { isValidWellknownUrl } from '@forgerock/sdk-utilities'; -import { JourneyClientConfig } from '@forgerock/sdk-types'; -import { JourneyServerConfig } from '@forgerock/sdk-types'; -import { LogLevel } from '@forgerock/sdk-logger'; -import { NameValue } from '@forgerock/sdk-types'; -import { PolicyKey } from '@forgerock/sdk-types'; -import { PolicyParams } from '@forgerock/sdk-types'; -import { PolicyRequirement } from '@forgerock/sdk-types'; -import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; -import { Step } from '@forgerock/sdk-types'; -import { StepDetail } from '@forgerock/sdk-types'; -import { StepType } from '@forgerock/sdk-types'; -import { WellknownResponse } from '@forgerock/sdk-types'; - -export { ActionTypes } - -// @public -export class AttributeInputCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getName(): string; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setValidateOnly(value: boolean): void; - setValue(value: T): void; -} - -export { AuthResponse } - -// @public -export class BaseCallback { - constructor(payload: Callback); - getInputValue(selector?: number | string): unknown; - getOutputByName(name: string, defaultValue: T): T; - getOutputValue(selector?: number | string): unknown; - getType(): CallbackType; - // (undocumented) - payload: Callback; - setInputValue(value: unknown, selector?: number | string | RegExp): void; -} - -export { Callback } - -// @public (undocumented) -export type CallbackFactory = (callback: Callback) => BaseCallback; - -export { CallbackType } - -// @public -export class ChoiceCallback extends BaseCallback { - constructor(payload: Callback); - getChoices(): string[]; - getDefaultChoice(): number; - getPrompt(): string; - // (undocumented) - payload: Callback; - setChoiceIndex(index: number): void; - setChoiceValue(value: string): void; -} - -// @public -export class ConfirmationCallback extends BaseCallback { - constructor(payload: Callback); - getDefaultOption(): number; - getMessageType(): number; - getOptions(): string[]; - getOptionType(): number; - getPrompt(): string; - // (undocumented) - payload: Callback; - setOptionIndex(index: number): void; - setOptionValue(value: string): void; -} - -// @public (undocumented) -export function createCallback(callback: Callback): BaseCallback; - -export { createWellknownError } - -export { CustomLogger } - -// @public -export class DeviceProfileCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - isLocationRequired(): boolean; - isMetadataRequired(): boolean; - // (undocumented) - payload: Callback; - setProfile(profile: DeviceProfileData): void; -} - -// @public (undocumented) -export interface DeviceProfileData { - // (undocumented) - identifier: string; - // (undocumented) - location?: Geolocation_2 | Record; - // (undocumented) - metadata?: { - hardware: { - display: { - [key: string]: string | number | null; - }; - [key: string]: any; - }; - browser: { - [key: string]: string | number | null; - }; - platform: { - [key: string]: string | number | null; - }; - }; -} - -export { FailedPolicyRequirement } - -export { FailureDetail } - -export { GenericError } - -// @public (undocumented) -interface Geolocation_2 { - // (undocumented) - latitude: number; - // (undocumented) - longitude: number; -} -export { Geolocation_2 as Geolocation } - -// @public -export class HiddenValueCallback extends BaseCallback { - constructor(payload: Callback); - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export interface IdPValue { - // (undocumented) - provider: string; - // (undocumented) - uiConfig: { - [key: string]: string; - }; -} - -// @internal -export interface InternalJourneyClientConfig { - // (undocumented) - error?: GenericError; - // (undocumented) - serverConfig: ResolvedServerConfig; -} - -export { isValidWellknownUrl } - -// @public -export interface JourneyClient { - // (undocumented) - next: (step: JourneyStep, options?: NextOptions) => Promise; - // (undocumented) - redirect: (step: JourneyStep) => Promise; - // (undocumented) - resume: (url: string, options?: ResumeOptions) => Promise; - // (undocumented) - start: (options?: StartParam) => Promise; - // (undocumented) - subscribe: (listener: () => void) => () => void; - // (undocumented) - terminate: (options?: { - query?: Record; - }) => Promise; -} - -export { JourneyClientConfig } - -// @public (undocumented) -export type JourneyLoginFailure = AuthResponse & { - type: StepType.LoginFailure; - payload: Step; - getCode: () => number; - getDetail: () => FailureDetail | undefined; - getMessage: () => string | undefined; - getProcessedMessage: (messageCreator?: MessageCreator) => ProcessedPropertyError[]; - getReason: () => string | undefined; -}; - -// @public (undocumented) -export type JourneyLoginSuccess = AuthResponse & { - type: StepType.LoginSuccess; - payload: Step; - getRealm: () => string | undefined; - getSessionToken: () => string | undefined; - getSuccessUrl: () => string | undefined; -}; - -// @public (undocumented) -export type JourneyResult = JourneyStep | JourneyLoginSuccess | JourneyLoginFailure | GenericError; - -export { JourneyServerConfig } - -// @public (undocumented) -export type JourneyStep = AuthResponse & { - type: StepType.Step; - payload: Step; - callbacks: BaseCallback[]; - getCallbackOfType: (type: CallbackType) => T; - getCallbacksOfType: (type: CallbackType) => T[]; - setCallbackValue: (type: CallbackType, value: unknown) => void; - getDescription: () => string | undefined; - getHeader: () => string | undefined; - getStage: () => string | undefined; -}; - -// @public -export class KbaCreateCallback extends BaseCallback { - constructor(payload: Callback); - getPredefinedQuestions(): string[]; - getPrompt(): string; - isAllowedUserDefinedQuestions(): boolean; - // (undocumented) - payload: Callback; - setAnswer(answer: string): void; - setQuestion(question: string): void; -} - -export { LogLevel } - -// @public (undocumented) -export interface MessageCreator { - // (undocumented) - [key: string]: (propertyName: string, params?: { - [key: string]: unknown; - }) => string; -} - -// @public -export class MetadataCallback extends BaseCallback { - constructor(payload: Callback); - getData(): T; - // (undocumented) - payload: Callback; -} - -// @public -export class NameCallback extends BaseCallback { - constructor(payload: Callback); - getPrompt(): string; - // (undocumented) - payload: Callback; - setName(name: string): void; -} - -export { NameValue } - -// @public (undocumented) -export interface NextOptions { - // (undocumented) - query?: Record; -} - -// @public -export class PasswordCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): string[]; - getPrompt(): string; - // (undocumented) - payload: Callback; - setPassword(password: string): void; -} - -// @public -export class PingOneProtectEvaluationCallback extends BaseCallback { - constructor(payload: Callback); - getPauseBehavioralData(): boolean; - // (undocumented) - payload: Callback; - setClientError(errorMessage: string): void; - setData(data: string): void; -} - -// @public -export class PingOneProtectInitializeCallback extends BaseCallback { - constructor(payload: Callback); - getConfig(): Record | { - envId: string; - consoleLogEnabled: boolean; - deviceAttributesToIgnore: string[]; - customHost: string; - lazyMetadata: boolean; - behavioralDataCollection: boolean; - deviceKeyRsyncIntervals: number; - enableTrust: boolean; - disableTags: boolean; - disableHub: boolean; - }; - // (undocumented) - payload: Callback; - // (undocumented) - setClientError(errorMessage: string): void; -} - -export { PolicyKey } - -export { PolicyParams } - -export { PolicyRequirement } - -// @public -export class PollingWaitCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - getWaitTime(): number; - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export interface ProcessedPropertyError { - // (undocumented) - detail: FailedPolicyRequirement; - // (undocumented) - messages: string[]; -} - -// @public -export class ReCaptchaCallback extends BaseCallback { - constructor(payload: Callback); - getSiteKey(): string; - // (undocumented) - payload: Callback; - setResult(result: string): void; -} - -// @public -export class ReCaptchaEnterpriseCallback extends BaseCallback { - constructor(payload: Callback); - getApiUrl(): string; - getElementClass(): string; - getSiteKey(): string; - // (undocumented) - payload: Callback; - setAction(action: string): void; - setClientError(error: string): void; - setPayload(payload: unknown): void; - setResult(result: string): void; -} - -// @public -export class RedirectCallback extends BaseCallback { - constructor(payload: Callback); - getRedirectUrl(): string; - // (undocumented) - payload: Callback; -} - -export { RequestMiddleware } - -// @public -export interface ResolvedServerConfig { - // (undocumented) - baseUrl: string; - // (undocumented) - paths: { - authenticate: string; - sessions: string; - }; -} - -// @public (undocumented) -export interface ResumeOptions { - // (undocumented) - journey?: string; - // (undocumented) - query?: Record; -} - -// @public -export class SelectIdPCallback extends BaseCallback { - constructor(payload: Callback); - getProviders(): IdPValue[]; - // (undocumented) - payload: Callback; - setProvider(value: string): void; -} - -// @public (undocumented) -export interface StartParam { - // (undocumented) - journey: string; - // (undocumented) - query?: Record; -} - -export { Step } - -export { StepDetail } - -export { StepType } - -// @public -export class SuspendedTextOutputCallback extends TextOutputCallback { - constructor(payload: Callback); - // (undocumented) - payload: Callback; -} - -// @public -export class TermsAndConditionsCallback extends BaseCallback { - constructor(payload: Callback); - getCreateDate(): Date | null; - getTerms(): string; - getVersion(): string; - // (undocumented) - payload: Callback; - setAccepted(accepted?: boolean): void; -} - -// @public -export class TextInputCallback extends BaseCallback { - constructor(payload: Callback); - getPrompt(): string; - // (undocumented) - payload: Callback; - setInput(input: string): void; -} - -// @public -export class TextOutputCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - getMessageType(): string; - // (undocumented) - payload: Callback; -} - -// @public -export class ValidatedCreatePasswordCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setPassword(password: string): void; - setValidateOnly(value: boolean): void; -} - -// @public -export class ValidatedCreateUsernameCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setName(name: string): void; - setValidateOnly(value: boolean): void; -} - -export { WellknownResponse } - -// (No @packageDocumentation comment for this package) - -``` +## API Report File for "@forgerock/journey-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ActionTypes } from '@forgerock/sdk-request-middleware'; +import { AuthResponse } from '@forgerock/sdk-types'; +import { Callback } from '@forgerock/sdk-types'; +import { CallbackType } from '@forgerock/sdk-types'; +import { createWellknownError } from '@forgerock/sdk-utilities'; +import { CustomLogger } from '@forgerock/sdk-logger'; +import { FailedPolicyRequirement } from '@forgerock/sdk-types'; +import { FailureDetail } from '@forgerock/sdk-types'; +import { GenericError } from '@forgerock/sdk-types'; +import { isValidWellknownUrl } from '@forgerock/sdk-utilities'; +import { JourneyClientConfig } from '@forgerock/sdk-types'; +import { JourneyServerConfig } from '@forgerock/sdk-types'; +import { LegacyServerConfig } from '@forgerock/sdk-types'; +import { LogLevel } from '@forgerock/sdk-logger'; +import { NameValue } from '@forgerock/sdk-types'; +import { PolicyKey } from '@forgerock/sdk-types'; +import { PolicyParams } from '@forgerock/sdk-types'; +import { PolicyRequirement } from '@forgerock/sdk-types'; +import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import { Step } from '@forgerock/sdk-types'; +import { StepDetail } from '@forgerock/sdk-types'; +import { StepType } from '@forgerock/sdk-types'; +import { WellknownResponse } from '@forgerock/sdk-types'; + +export { ActionTypes }; + +// @public +export class AttributeInputCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getName(): string; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setValidateOnly(value: boolean): void; + setValue(value: T): void; +} + +export { AuthResponse }; + +// @public +export class BaseCallback { + constructor(payload: Callback); + getInputValue(selector?: number | string): unknown; + getOutputByName(name: string, defaultValue: T): T; + getOutputValue(selector?: number | string): unknown; + getType(): CallbackType; + // (undocumented) + payload: Callback; + setInputValue(value: unknown, selector?: number | string | RegExp): void; +} + +export { Callback }; + +// @public (undocumented) +export type CallbackFactory = (callback: Callback) => BaseCallback; + +export { CallbackType }; + +// @public +export class ChoiceCallback extends BaseCallback { + constructor(payload: Callback); + getChoices(): string[]; + getDefaultChoice(): number; + getPrompt(): string; + // (undocumented) + payload: Callback; + setChoiceIndex(index: number): void; + setChoiceValue(value: string): void; +} + +// @public +export class ConfirmationCallback extends BaseCallback { + constructor(payload: Callback); + getDefaultOption(): number; + getMessageType(): number; + getOptions(): string[]; + getOptionType(): number; + getPrompt(): string; + // (undocumented) + payload: Callback; + setOptionIndex(index: number): void; + setOptionValue(value: string): void; +} + +// @public (undocumented) +export function createCallback(callback: Callback): BaseCallback; + +export { createWellknownError }; + +export { CustomLogger }; + +// @public +export class DeviceProfileCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + isLocationRequired(): boolean; + isMetadataRequired(): boolean; + // (undocumented) + payload: Callback; + setProfile(profile: DeviceProfileData): void; +} + +// @public (undocumented) +export interface DeviceProfileData { + // (undocumented) + identifier: string; + // (undocumented) + location?: Geolocation_2 | Record; + // (undocumented) + metadata?: { + hardware: { + display: { + [key: string]: string | number | null; + }; + [key: string]: any; + }; + browser: { + [key: string]: string | number | null; + }; + platform: { + [key: string]: string | number | null; + }; + }; +} + +export { FailedPolicyRequirement }; + +export { FailureDetail }; + +export { GenericError }; + +// @public (undocumented) +interface Geolocation_2 { + // (undocumented) + latitude: number; + // (undocumented) + longitude: number; +} +export { Geolocation_2 as Geolocation }; + +// @public +export class HiddenValueCallback extends BaseCallback { + constructor(payload: Callback); + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export interface IdPValue { + // (undocumented) + provider: string; + // (undocumented) + uiConfig: { + [key: string]: string; + }; +} + +// @internal +export interface InternalJourneyClientConfig { + // (undocumented) + error?: GenericError; + // (undocumented) + serverConfig: ResolvedServerConfig; +} + +export { isValidWellknownUrl }; + +// @public +export function journey(input: { + config: JourneyClientConfig; + requestMiddleware?: RequestMiddleware[]; + logger?: { + level: LogLevel; + custom?: CustomLogger; + }; +}): Promise; + +// @public +export interface JourneyClient { + // (undocumented) + next: (step: JourneyStep, options?: NextOptions) => Promise; + // (undocumented) + redirect: (step: JourneyStep) => Promise; + // (undocumented) + resume: (url: string, options?: ResumeOptions) => Promise; + // (undocumented) + start: (options?: StartParam) => Promise; + // (undocumented) + subscribe: (listener: () => void) => () => void; + // (undocumented) + terminate: (options?: { query?: Record }) => Promise; +} + +export { JourneyClientConfig }; + +// @public (undocumented) +export type JourneyLoginFailure = AuthResponse & { + type: StepType.LoginFailure; + payload: Step; + getCode: () => number; + getDetail: () => FailureDetail | undefined; + getMessage: () => string | undefined; + getProcessedMessage: (messageCreator?: MessageCreator) => ProcessedPropertyError[]; + getReason: () => string | undefined; +}; + +// @public (undocumented) +export type JourneyLoginSuccess = AuthResponse & { + type: StepType.LoginSuccess; + payload: Step; + getRealm: () => string | undefined; + getSessionToken: () => string | undefined; + getSuccessUrl: () => string | undefined; +}; + +// @public (undocumented) +export type JourneyResult = JourneyStep | JourneyLoginSuccess | JourneyLoginFailure | GenericError; + +export { JourneyServerConfig }; + +// @public (undocumented) +export type JourneyStep = AuthResponse & { + type: StepType.Step; + payload: Step; + callbacks: BaseCallback[]; + getCallbackOfType: (type: CallbackType) => T; + getCallbacksOfType: (type: CallbackType) => T[]; + setCallbackValue: (type: CallbackType, value: unknown) => void; + getDescription: () => string | undefined; + getHeader: () => string | undefined; + getStage: () => string | undefined; +}; + +// @public +export class KbaCreateCallback extends BaseCallback { + constructor(payload: Callback); + getPredefinedQuestions(): string[]; + getPrompt(): string; + isAllowedUserDefinedQuestions(): boolean; + // (undocumented) + payload: Callback; + setAnswer(answer: string): void; + setQuestion(question: string): void; +} + +export { LegacyServerConfig }; + +export { LogLevel }; + +// @public (undocumented) +export interface MessageCreator { + // (undocumented) + [key: string]: ( + propertyName: string, + params?: { + [key: string]: unknown; + }, + ) => string; +} + +// @public +export class MetadataCallback extends BaseCallback { + constructor(payload: Callback); + getData(): T; + // (undocumented) + payload: Callback; +} + +// @public +export class NameCallback extends BaseCallback { + constructor(payload: Callback); + getPrompt(): string; + // (undocumented) + payload: Callback; + setName(name: string): void; +} + +export { NameValue }; + +// @public (undocumented) +export interface NextOptions { + // (undocumented) + query?: Record; +} + +// @public +export class PasswordCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): string[]; + getPrompt(): string; + // (undocumented) + payload: Callback; + setPassword(password: string): void; +} + +// @public +export class PingOneProtectEvaluationCallback extends BaseCallback { + constructor(payload: Callback); + getPauseBehavioralData(): boolean; + // (undocumented) + payload: Callback; + setClientError(errorMessage: string): void; + setData(data: string): void; +} + +// @public +export class PingOneProtectInitializeCallback extends BaseCallback { + constructor(payload: Callback); + getConfig(): + | Record + | { + envId: string; + consoleLogEnabled: boolean; + deviceAttributesToIgnore: string[]; + customHost: string; + lazyMetadata: boolean; + behavioralDataCollection: boolean; + deviceKeyRsyncIntervals: number; + enableTrust: boolean; + disableTags: boolean; + disableHub: boolean; + }; + // (undocumented) + payload: Callback; + // (undocumented) + setClientError(errorMessage: string): void; +} + +export { PolicyKey }; + +export { PolicyParams }; + +export { PolicyRequirement }; + +// @public +export class PollingWaitCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + getWaitTime(): number; + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export interface ProcessedPropertyError { + // (undocumented) + detail: FailedPolicyRequirement; + // (undocumented) + messages: string[]; +} + +// @public +export class ReCaptchaCallback extends BaseCallback { + constructor(payload: Callback); + getSiteKey(): string; + // (undocumented) + payload: Callback; + setResult(result: string): void; +} + +// @public +export class ReCaptchaEnterpriseCallback extends BaseCallback { + constructor(payload: Callback); + getApiUrl(): string; + getElementClass(): string; + getSiteKey(): string; + // (undocumented) + payload: Callback; + setAction(action: string): void; + setClientError(error: string): void; + setPayload(payload: unknown): void; + setResult(result: string): void; +} + +// @public +export class RedirectCallback extends BaseCallback { + constructor(payload: Callback); + getRedirectUrl(): string; + // (undocumented) + payload: Callback; +} + +export { RequestMiddleware }; + +// @public +export interface ResolvedServerConfig { + // (undocumented) + baseUrl: string; + // (undocumented) + paths: { + authenticate: string; + sessions: string; + }; +} + +// @public (undocumented) +export interface ResumeOptions { + // (undocumented) + journey?: string; + // (undocumented) + query?: Record; +} + +// @public +export class SelectIdPCallback extends BaseCallback { + constructor(payload: Callback); + getProviders(): IdPValue[]; + // (undocumented) + payload: Callback; + setProvider(value: string): void; +} + +// @public (undocumented) +export interface StartParam { + // (undocumented) + journey: string; + // (undocumented) + query?: Record; +} + +export { Step }; + +export { StepDetail }; + +export { StepType }; + +// @public +export class SuspendedTextOutputCallback extends TextOutputCallback { + constructor(payload: Callback); + // (undocumented) + payload: Callback; +} + +// @public +export class TermsAndConditionsCallback extends BaseCallback { + constructor(payload: Callback); + getCreateDate(): Date | null; + getTerms(): string; + getVersion(): string; + // (undocumented) + payload: Callback; + setAccepted(accepted?: boolean): void; +} + +// @public +export class TextInputCallback extends BaseCallback { + constructor(payload: Callback); + getPrompt(): string; + // (undocumented) + payload: Callback; + setInput(input: string): void; +} + +// @public +export class TextOutputCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + getMessageType(): string; + // (undocumented) + payload: Callback; +} + +// @public +export class ValidatedCreatePasswordCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setPassword(password: string): void; + setValidateOnly(value: boolean): void; +} + +// @public +export class ValidatedCreateUsernameCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setName(name: string): void; + setValidateOnly(value: boolean): void; +} + +export { WellknownResponse }; + +// (No @packageDocumentation comment for this package) +``` diff --git a/packages/journey-client/package.json b/packages/journey-client/package.json index 285cb707cb..5419237516 100644 --- a/packages/journey-client/package.json +++ b/packages/journey-client/package.json @@ -13,6 +13,7 @@ "type": "module", "exports": { ".": "./dist/src/index.js", + "./_utils": "./dist/src/_utils.js", "./device": "./dist/src/lib/device/device-profile.js", "./package.json": "./package.json", "./policy": "./dist/src/lib/policy/policy.js", diff --git a/packages/journey-client/src/_utils.ts b/packages/journey-client/src/_utils.ts new file mode 100644 index 0000000000..595323c038 --- /dev/null +++ b/packages/journey-client/src/_utils.ts @@ -0,0 +1,9 @@ +/** + * @private + * @description Creates a decorated `Step` object with convenience functions. + * Used mostly for mocking and some advanced, edge cases. + */ +export { createJourneyStep } from './lib/step.utils.js'; +export type { JourneyStep } from './lib/step.types.js'; +export type { CallbackFactory } from './lib/callbacks/factory.js'; +export { BaseCallback } from './lib/callbacks/base-callback.js'; diff --git a/packages/journey-client/src/index.ts b/packages/journey-client/src/index.ts index d26a3fa0b9..ce264eaeca 100644 --- a/packages/journey-client/src/index.ts +++ b/packages/journey-client/src/index.ts @@ -8,8 +8,5 @@ export * from './lib/client.store.js'; export * from './types.js'; -// required to help deserialize a step after it's been serialized -export { createJourneyStep } from './lib/step.utils.js'; - // Re-export types from internal packages that consumers need export { callbackType } from '@forgerock/sdk-types'; diff --git a/packages/journey-client/src/lib/client.store.ts b/packages/journey-client/src/lib/client.store.ts index 32c2689a64..bf0161408f 100644 --- a/packages/journey-client/src/lib/client.store.ts +++ b/packages/journey-client/src/lib/client.store.ts @@ -115,31 +115,44 @@ export async function journey({ const store = createJourneyStore({ requestMiddleware, logger: log }); - const { wellknown } = config.serverConfig; + if ('baseUrl' in config.serverConfig) { + const { baseUrl } = config.serverConfig; - if (!isValidWellknownUrl(wellknown)) { - const message = `Invalid wellknown URL: ${wellknown}. URL must use HTTPS (or HTTP for localhost) and end with /.well-known/openid-configuration.`; - log.error(message); - throw new Error(message); - } + // Check for `baseUrl` to support non-AIC, Platform Login edge cases + // Warn of the use of `baseUrl`. This is used only in advanced use cases, and is not recommended for "normal consumer use". + log.warn( + 'Wellknown configuration is disabled due to the presence of `serverConfig.baseUrl`. It is recommended that you remove `baseUrl` and just use the `wellknown` property.', + ); - const { data: wellknownResponse, error: fetchError } = await store.dispatch( - wellknownApi.endpoints.configuration.initiate(wellknown), - ); + store.dispatch(configSlice.actions.set({ type: 'baseUrl', baseUrl })); + } else { + const { wellknown } = config.serverConfig; + // This is the normal use case for using the Wellknown endpoint for proper configuration. + if (!isValidWellknownUrl(wellknown)) { + const message = `Invalid wellknown URL: ${wellknown}. URL must use HTTPS (or HTTP for localhost) and end with /.well-known/openid-configuration.`; + log.error(message); + throw new Error(message); + } + + const { data: wellknownResponse, error: fetchError } = await store.dispatch( + wellknownApi.endpoints.configuration.initiate(wellknown), + ); - if (fetchError || !wellknownResponse) { - const error = createWellknownError(fetchError); - const message = `${error.error}: ${error.message}`; - log.error(message); - throw new Error(message); + if (fetchError || !wellknownResponse) { + const error = createWellknownError(fetchError); + const message = `${error.error}: ${error.message}`; + log.error(message); + throw new Error(message); + } + + store.dispatch( + configSlice.actions.set({ + type: 'wellknown', + wellknownResponse: wellknownResponse, + }), + ); } - store.dispatch( - configSlice.actions.set({ - wellknownResponse: wellknownResponse, - }), - ); - const configError = store.getState().config.error; if (configError) { diff --git a/packages/journey-client/src/lib/config.slice.test.ts b/packages/journey-client/src/lib/config.slice.test.ts index 0affaf7cce..ae7dcb70fb 100644 --- a/packages/journey-client/src/lib/config.slice.test.ts +++ b/packages/journey-client/src/lib/config.slice.test.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Ping Identity Corporation. All rights reserved. + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -28,6 +28,7 @@ describe('journey-client config.slice', () => { describe('configSlice_ValidAmWellknown_SetsResolvedServerConfig', () => { it('should derive baseUrl and paths from a standard AM well-known response', () => { const payload: ResolvedConfig = { + type: 'wellknown', wellknownResponse: createMockWellknown(), }; @@ -47,6 +48,7 @@ describe('journey-client config.slice', () => { describe('configSlice_NonAmIssuer_SetsError', () => { it('should set a GenericError when the issuer is not a ForgeRock AM issuer', () => { const payload: ResolvedConfig = { + type: 'wellknown', wellknownResponse: createMockWellknown({ issuer: 'https://auth.pingone.com/env-id/as', authorization_endpoint: 'https://auth.pingone.com/env-id/as/authorize', @@ -64,6 +66,7 @@ describe('journey-client config.slice', () => { describe('configSlice_MissingAuthEndpoint_SetsError', () => { it('should set a GenericError when authorization_endpoint is empty', () => { const payload: ResolvedConfig = { + type: 'wellknown', wellknownResponse: createMockWellknown({ authorization_endpoint: '', }), diff --git a/packages/journey-client/src/lib/config.slice.ts b/packages/journey-client/src/lib/config.slice.ts index 02cac72102..a3a2a2886e 100644 --- a/packages/journey-client/src/lib/config.slice.ts +++ b/packages/journey-client/src/lib/config.slice.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Ping Identity Corporation. All rights reserved. + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -16,9 +16,15 @@ import { convertWellknown } from './wellknown.utils.js'; * to internal server config via {@link convertWellknown}. */ export interface ResolvedConfig { + type: 'wellknown'; wellknownResponse: WellknownResponse; } +interface BaseConfig { + type: 'baseUrl'; + baseUrl: string; +} + const initialState: InternalJourneyClientConfig = { serverConfig: { baseUrl: '', paths: { authenticate: '', sessions: '' } }, }; @@ -35,13 +41,17 @@ export const configSlice = createSlice({ name: 'config', initialState, reducers: { - set(state, action: PayloadAction) { - const wellknown = convertWellknown(action.payload.wellknownResponse); - if ('error' in wellknown) { - state.error = wellknown; + set(state, action: PayloadAction) { + if (action.payload.type === 'baseUrl') { + state.serverConfig.baseUrl = action.payload.baseUrl; } else { - state.serverConfig = wellknown; - state.error = undefined; + const config = convertWellknown(action.payload.wellknownResponse); + if ('error' in config) { + state.error = config; + } else { + state.serverConfig = config; + state.error = undefined; + } } }, }, diff --git a/packages/journey-client/src/lib/config.types.test-d.ts b/packages/journey-client/src/lib/config.types.test-d.ts index d314f040fe..3536b20c2e 100644 --- a/packages/journey-client/src/lib/config.types.test-d.ts +++ b/packages/journey-client/src/lib/config.types.test-d.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Ping Identity Corporation. All rights reserved. + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,6 +8,7 @@ import { describe, expectTypeOf, it } from 'vitest'; import type { JourneyClientConfig, JourneyServerConfig, + LegacyServerConfig, InternalJourneyClientConfig, } from './config.types.js'; import type { AsyncLegacyConfigOptions } from '@forgerock/sdk-types'; @@ -19,18 +20,30 @@ describe('Config Types', () => { expectTypeOf().toExtend(); }); - it('should narrow serverConfig to JourneyServerConfig', () => { - expectTypeOf().toExtend(); - expectTypeOf().toBeString(); + it('should type serverConfig as JourneyServerConfig | LegacyServerConfig', () => { + expectTypeOf().toEqualTypeOf< + JourneyServerConfig | LegacyServerConfig + >(); }); - it('should reject config without wellknown', () => { - // @ts-expect-error - wellknown is required on serverConfig + it('should have wellknown only on the JourneyServerConfig branch', () => { + type WellknownBranch = Extract; + expectTypeOf().toBeString(); + }); + + it('should reject serverConfig with neither wellknown nor baseUrl', () => { + // @ts-expect-error - serverConfig must satisfy JourneyServerConfig (wellknown) or LegacyServerConfig (baseUrl) const config: JourneyClientConfig = { serverConfig: {} }; - // This assertion verifies the variable's runtime shape doesn't satisfy the full type. expectTypeOf(config).not.toMatchObjectType>(); }); + it('should accept LegacyServerConfig with baseUrl', () => { + const config: JourneyClientConfig = { + serverConfig: { baseUrl: 'https://am.example.com' }, + }; + expectTypeOf(config).toExtend(); + }); + it('should allow AsyncLegacyConfigOptions properties', () => { const config: JourneyClientConfig = { clientId: 'test-client', @@ -53,8 +66,9 @@ describe('Config Types', () => { expectTypeOf(config).toExtend(); }); - it('should have optional timeout on serverConfig', () => { - expectTypeOf().toHaveProperty('timeout'); + it('should have optional timeout on both serverConfig branches', () => { + expectTypeOf().toHaveProperty('timeout'); + expectTypeOf().toHaveProperty('timeout'); }); }); diff --git a/packages/journey-client/src/lib/config.types.ts b/packages/journey-client/src/lib/config.types.ts index 34dc7ec0e4..b1877746ee 100644 --- a/packages/journey-client/src/lib/config.types.ts +++ b/packages/journey-client/src/lib/config.types.ts @@ -8,7 +8,11 @@ import type { GenericError } from '@forgerock/sdk-types'; import type { ResolvedServerConfig } from './wellknown.utils.js'; -export type { JourneyServerConfig, JourneyClientConfig } from '@forgerock/sdk-types'; +export type { + JourneyServerConfig, + JourneyClientConfig, + LegacyServerConfig, +} from '@forgerock/sdk-types'; /** * Internal configuration after wellknown discovery and path resolution. diff --git a/packages/sdk-types/src/lib/config.types.ts b/packages/sdk-types/src/lib/config.types.ts index 3752b970b4..060df3ab33 100644 --- a/packages/sdk-types/src/lib/config.types.ts +++ b/packages/sdk-types/src/lib/config.types.ts @@ -39,6 +39,11 @@ export interface OidcConfig extends AsyncLegacyConfigOptions { log?: LogLevel; } +export interface LegacyServerConfig { + baseUrl: string; + timeout?: number; +} + export interface JourneyServerConfig { wellknown: string; timeout?: number; @@ -53,7 +58,7 @@ export interface JourneyServerConfig { * journey-client — a warning is logged when they are provided. */ export interface JourneyClientConfig extends AsyncLegacyConfigOptions { - serverConfig: JourneyServerConfig; + serverConfig: JourneyServerConfig | LegacyServerConfig; log?: LogLevel; } diff --git a/packages/sdk-utilities/src/lib/config/config.test.ts b/packages/sdk-utilities/src/lib/config/config.test.ts index a48407f9d8..d09e1ab29c 100644 --- a/packages/sdk-utilities/src/lib/config/config.test.ts +++ b/packages/sdk-utilities/src/lib/config/config.test.ts @@ -235,14 +235,16 @@ describe('parseToJourneyConfig', () => { it('parseToJourneyConfig_MinimalConfig_MapsWellknown', () => { const data = Either.getOrThrow(parseToJourneyConfig({ oidc: minimalOidc })); - expect(data.serverConfig.wellknown).toBe( + expect(data.serverConfig).toHaveProperty( + 'wellknown', 'https://example.com/.well-known/openid-configuration', ); }); it('parseToJourneyConfig_JourneyOnlyConfig_MapsWellknown', () => { const data = Either.getOrThrow(parseToJourneyConfig(journeyOnlyConfig)); - expect(data.serverConfig.wellknown).toBe( + expect(data.serverConfig).toHaveProperty( + 'wellknown', 'https://example.com/.well-known/openid-configuration', ); }); @@ -463,7 +465,8 @@ describe('makeOidcConfig', () => { describe('makeJourneyConfig', () => { it('makeJourneyConfig_ValidConfig_ReturnsMappedJourneyConfig', () => { const result = makeJourneyConfig(fullConfig); - expect(result.serverConfig.wellknown).toBe( + expect(result.serverConfig).toHaveProperty( + 'wellknown', 'https://example.com/.well-known/openid-configuration', ); expect(result.realmPath).toBe('alpha'); From 906d57dc37ac6b468fcd1a174811a9e6bad02364 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Wed, 15 Jul 2026 09:48:41 -0600 Subject: [PATCH 3/3] chore: update-paths --- .../src/lib/client.store.test.ts | 106 ++++++++++++++++++ .../journey-client/src/lib/client.store.ts | 17 ++- .../src/lib/config.slice.test.ts | 25 +++++ .../journey-client/src/lib/config.slice.ts | 7 +- 4 files changed, 152 insertions(+), 3 deletions(-) diff --git a/packages/journey-client/src/lib/client.store.test.ts b/packages/journey-client/src/lib/client.store.test.ts index 7d0ddf9345..9f2f3de96d 100644 --- a/packages/journey-client/src/lib/client.store.test.ts +++ b/packages/journey-client/src/lib/client.store.test.ts @@ -561,6 +561,112 @@ describe('journey-client', () => { }); }); + describe('baseUrl mode', () => { + const baseUrl = 'https://am.example.com/am'; + const mockStepResponse: Step = { authId: 'test-auth-id', callbacks: [] }; + + function setupBaseUrlFetch() { + mockFetch.mockImplementation((input: RequestInfo | URL) => { + const url = getUrlFromInput(input); + if (url.includes('/authenticate')) { + return Promise.resolve(new Response(JSON.stringify(mockStepResponse))); + } + if (url.includes('/sessions')) { + return Promise.resolve(new Response(null, { status: 200 })); + } + return Promise.reject(new Error(`Unexpected fetch: ${url}`)); + }); + } + + test('journey_BaseUrl_NoRealmPath_UsesRootAuthenticate', async () => { + setupBaseUrlFetch(); + + const client = await journey({ + config: { serverConfig: { baseUrl } }, + }); + await client.start(); + + expect(mockFetch).toHaveBeenCalledTimes(1); + const request = mockFetch.mock.calls[0][0] as Request; + expect(request.url).toBe('https://am.example.com/am/json/realms/root/authenticate'); + }); + + test('journey_BaseUrl_WithRealmPath_UsesRealmAuthenticate', async () => { + setupBaseUrlFetch(); + + const client = await journey({ + config: { serverConfig: { baseUrl }, realmPath: 'alpha' }, + }); + await client.start(); + + expect(mockFetch).toHaveBeenCalledTimes(1); + const request = mockFetch.mock.calls[0][0] as Request; + expect(request.url).toBe( + 'https://am.example.com/am/json/realms/root/realms/alpha/authenticate', + ); + }); + + test('journey_BaseUrl_TrailingSlashInput_NormalizesCorrectly', async () => { + setupBaseUrlFetch(); + + const client = await journey({ + config: { serverConfig: { baseUrl: `${baseUrl}/` } }, + }); + await client.start(); + + const request = mockFetch.mock.calls[0][0] as Request; + expect(request.url).toBe('https://am.example.com/am/json/realms/root/authenticate'); + }); + + test('journey_BaseUrl_Terminate_UsesRootSessionsUrl', async () => { + setupBaseUrlFetch(); + + const client = await journey({ + config: { serverConfig: { baseUrl } }, + }); + await client.terminate(); + + expect(mockFetch).toHaveBeenCalledTimes(1); + const request = mockFetch.mock.calls[0][0] as Request; + expect(request.url).toBe( + 'https://am.example.com/am/json/realms/root/sessions/?_action=logout', + ); + }); + + test('journey_BaseUrl_WithRealmPath_Terminate_UsesRealmSessionsUrl', async () => { + setupBaseUrlFetch(); + + const client = await journey({ + config: { serverConfig: { baseUrl }, realmPath: 'alpha' }, + }); + await client.terminate(); + + expect(mockFetch).toHaveBeenCalledTimes(1); + const request = mockFetch.mock.calls[0][0] as Request; + expect(request.url).toBe( + 'https://am.example.com/am/json/realms/root/realms/alpha/sessions/?_action=logout', + ); + }); + + test('journey_BaseUrl_WithRealmPath_DoesNotWarn', async () => { + setupBaseUrlFetch(); + const customLogger = { + error: vi.fn(), + warn: vi.fn(), + info: vi.fn(), + debug: vi.fn(), + }; + + await journey({ + config: { serverConfig: { baseUrl }, realmPath: 'alpha' }, + logger: { level: 'warn', custom: customLogger }, + }); + + const warnCalls = customLogger.warn.mock.calls.map((c) => c[0] as string); + expect(warnCalls.find((msg) => msg.includes('realmPath'))).toBeUndefined(); + }); + }); + describe('unified JSON config entry', () => { test('accepts unified JSON config and initializes successfully', async () => { setupMockFetch(); diff --git a/packages/journey-client/src/lib/client.store.ts b/packages/journey-client/src/lib/client.store.ts index bf0161408f..5ea9604ed6 100644 --- a/packages/journey-client/src/lib/client.store.ts +++ b/packages/journey-client/src/lib/client.store.ts @@ -11,6 +11,7 @@ import { isGenericError, isValidWellknownUrl, createWellknownError, + getEndpointPath, } from '@forgerock/sdk-utilities'; import type { GenericError } from '@forgerock/sdk-types'; import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; @@ -93,7 +94,6 @@ export async function journey({ 'oauthThreshold', 'platformHeader', 'prefix', - 'realmPath', 'redirectUri', 'scope', 'tokenStore', @@ -124,7 +124,20 @@ export async function journey({ 'Wellknown configuration is disabled due to the presence of `serverConfig.baseUrl`. It is recommended that you remove `baseUrl` and just use the `wellknown` property.', ); - store.dispatch(configSlice.actions.set({ type: 'baseUrl', baseUrl })); + // resolve() treats the base URL as a directory: it drops the last path segment for relative + // paths. A trailing slash ensures the full context path is preserved (e.g. /am/json/...). + const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`; + + store.dispatch( + configSlice.actions.set({ + type: 'baseUrl', + baseUrl: normalizedBaseUrl, + paths: { + authenticate: getEndpointPath({ endpoint: 'authenticate', realmPath: config.realmPath }), + sessions: getEndpointPath({ endpoint: 'sessions', realmPath: config.realmPath }), + }, + }), + ); } else { const { wellknown } = config.serverConfig; // This is the normal use case for using the Wellknown endpoint for proper configuration. diff --git a/packages/journey-client/src/lib/config.slice.test.ts b/packages/journey-client/src/lib/config.slice.test.ts index ae7dcb70fb..54ec3aa51a 100644 --- a/packages/journey-client/src/lib/config.slice.test.ts +++ b/packages/journey-client/src/lib/config.slice.test.ts @@ -63,6 +63,31 @@ describe('journey-client config.slice', () => { }); }); + describe('configSlice_BaseUrlPayload_StoresVerbatim', () => { + it('should store baseUrl and paths exactly as dispatched', () => { + const state = configSlice.reducer( + undefined, + configSlice.actions.set({ + type: 'baseUrl', + baseUrl: 'https://am.example.com/am/', + paths: { + authenticate: 'json/realms/root/realms/alpha/authenticate', + sessions: 'json/realms/root/realms/alpha/sessions/', + }, + }), + ); + + expect(state.serverConfig).toEqual({ + baseUrl: 'https://am.example.com/am/', + paths: { + authenticate: 'json/realms/root/realms/alpha/authenticate', + sessions: 'json/realms/root/realms/alpha/sessions/', + }, + }); + expect(state.error).toBeUndefined(); + }); + }); + describe('configSlice_MissingAuthEndpoint_SetsError', () => { it('should set a GenericError when authorization_endpoint is empty', () => { const payload: ResolvedConfig = { diff --git a/packages/journey-client/src/lib/config.slice.ts b/packages/journey-client/src/lib/config.slice.ts index a3a2a2886e..46e6ee2730 100644 --- a/packages/journey-client/src/lib/config.slice.ts +++ b/packages/journey-client/src/lib/config.slice.ts @@ -23,6 +23,10 @@ export interface ResolvedConfig { interface BaseConfig { type: 'baseUrl'; baseUrl: string; + paths: { + authenticate: string; + sessions: string; + }; } const initialState: InternalJourneyClientConfig = { @@ -43,7 +47,8 @@ export const configSlice = createSlice({ reducers: { set(state, action: PayloadAction) { if (action.payload.type === 'baseUrl') { - state.serverConfig.baseUrl = action.payload.baseUrl; + const { baseUrl, paths } = action.payload; + state.serverConfig = { baseUrl, paths }; } else { const config = convertWellknown(action.payload.wellknownResponse); if ('error' in config) {