Repository: logto-io/logto
File: packages/core/src/routes/experience/classes/experience-interaction.ts (TypeScript)

Code before:
  async guardCaptcha() {
    if (this.captcha.verified || this.captcha.skipped) {
      return;
    }

    await this.signInExperienceValidator.guardCaptcha();
  }

  /** Convert the current interaction to JSON, so that it can be stored as the OIDC provider interaction result */
  public async toJson(): Promise<InteractionStorage> {
    const { interactionEvent, userId, captcha } = this;
    const injectedHeaders = await this.getInjectedHeaders();

    return {
      interactionEvent,
      userId,
      profile: this.profile.data,
      mfa: this.mfa.data,
      verificationRecords: this.verificationRecordsArray.map((record) => record.toJson()),
      captcha,
      ...conditional(injectedHeaders && { injectedHeaders }),
    };
  }

  public async toSanitizedJson(): Promise<SanitizedInteractionStorageData> {
    const rawJson = await this.toJson();

    return {
      ...rawJson,
      profile: this.profile.sanitizedData,
      mfa: this.mfa.sanitizedData,
      verificationRecords: this.verificationRecordsArray.map((record) => record.toSanitizedJson()),
    };
  }

  private async getInjectedHeaders(): Promise<Optional<Record<string, string>>> {
    const shouldPersistInjectedHeaders =
      await this.adaptiveMfaValidator.shouldPersistInjectedHeaders();
    const injectedHeaders = shouldPersistInjectedHeaders
      ? this.adaptiveMfaValidator.getInjectedHeaders()
      : undefined;
    return injectedHeaders;
  }

  private get verificationRecordsArray() {
    return this.verificationRecords.array();
  }

  /**
   * Assert the interaction is identified and return the identified user.
   * @throws RequestError with 404 if the if the user is not identified or not found

Review comment (security):
```suggestion
  public async toSanitizedJson(): SanitizedInteractionStorageData {

```

Code after:
    if (this.captcha.verified || this.captcha.skipped) {
      return;
    }

    await this.signInExperienceValidator.guardCaptcha();
  }

  /** Convert the current interaction to JSON, so that it can be stored as the OIDC provider interaction result */
  public toJson(): InteractionStorage {
    const { interactionEvent, userId, captcha } = this;
    const signInContext = this.adaptiveMfaValidator.getSignInContext();

    return {
      interactionEvent,
      userId,
      profile: this.profile.data,
      mfa: this.mfa.data,
      verificationRecords: this.verificationRecordsArray.map((record) => record.toJson()),
      captcha,
      ...conditional(signInContext && { signInContext }),
    };
  }

  public toSanitizedJson(): SanitizedInteractionStorageData {
    return {
      ...this.toJson(),
      profile: this.profile.sanitizedData,
      mfa: this.mfa.sanitizedData,
      verificationRecords: this.verificationRecordsArray.map((record) => record.toSanitizedJson()),
    };
  }

  private get verificationRecordsArray() {
    return this.verificationRecords.array();
  }

  /**
   * Assert the interaction is identified and return the identified user.
   * @throws RequestError with 404 if the if the user is not identified or not found
   */
  private async getIdentifiedUser(): Promise<User> {
    if (this.userCache) {
      return this.userCache;
    }

    // Identified
    assertThat(
      this.userId,
      new RequestError({
        code: 'session.identifier_not_found',
        status: 404,