I am developing an application using ForgeRock's SDK. ForgeRock SDK returns promise based callbacks within each step of the user's authentication journey. I am using *ngSwitchCases on the template and on the main component to add the necessary callbacks for the given step, build the authentication form, sending the response to ForgeRock, and instantiating the given components (components being callbacks) based off what ForgeRock returns.
The problem is that ForgeRock returns multiple different callbacks within a single step (ChoiceCallback for OTP method selection, TextOutputCallback for displaying text, HiddenCallbacks for sending metadata back to the server, etc).
Here is an image for reference.
As you will see there are 3 TextOutputCallbacks and 3 HiddenValueCallbacks returned in this step. The code below will cause 3 separate instances of the app-hidden-callback component to be initialized, when I only need one instance of the component with all three HiddenValueCallbacks inside the object sent to the component.
<ng-container class="mt-3" *ngFor="let callback of currentStep ? currentStep.callbacks : null; let i = index">
<ng-container [ngSwitch]="callback.getType()">
<ng-container *ngSwitchCase="'NameCallback'">
<app-name-callback [nameCallbackObject]="nameCallbackObject"></app-name-callback>
</ng-container>
<ng-container *ngSwitchCase="'PasswordCallback'">
<app-password-callback [passwordCallbackObject]="passwordCallbackObject"></app-password-callback>
</ng-container>
<ng-container *ngSwitchCase="'ChoiceCallback'">
<app-choice-callback [choiceCallbackObject]="choiceCallbackObject[i]"></app-choice-callback>
</ng-container>
<ng-container *ngSwitchCase="'ConfirmationCallback'">
<app-confirmation-callback [confirmationCallbackObject]="confirmationCallbackObject">
</app-confirmation-callback>
</ng-container>
<ng-container *ngSwitchCase="'TextOutputCallback'">
<app-text-callback [textCallbackObject]="textCallbackObject[i]">
</app-text-callback>
</ng-container>
<ng-container *ngSwitchCase="'HiddenValueCallback'">
<app-hidden-callback [hiddenCallbackObject]= "hiddenCallbackObject">
</app-hidden-callback>
</ng-container>
<ng-container *ngSwitchDefault>
</ng-container>
</ng-container>
</ng-container>
The code above is the main component template that handles each step of the authentication journey and initializes the appropriate callback(s) based on the response received for ForgeRock.
/**
*
*
* @param {FRCallback[]} callbacks
* @memberof MainComponent
*/
buildAuthnForm(callbacks: FRCallback[]): void {
let i = 0;
callbacks.forEach((callback) => {
i++;
switch (callback.getType() as CallbackType) {
case CallbackType.NameCallback: {
const formControlGroup = 'nameCallbackForm';
this.authnForm.addControl(
formControlGroup,
this.fb.group({
nameControl: ['', Validators.required],
})
);
this.nameCallbackObject = {
callbackValue: callback,
input: callback.getInputValue(),
form: this.authnForm.get(formControlGroup),
};
break;
}
case CallbackType.PasswordCallback: {
const formControlGroup = 'passwordCallbackForm';
this.authnForm.addControl(
formControlGroup,
this.fb.group({
passwordControl: ['', Validators.required],
})
);
this.passwordCallbackObject = {
callbackValue: callback,
form: this.authnForm.get(formControlGroup),
};
break;
}
case CallbackType.ChoiceCallback: {
const formControlGroup = 'choiceCallbackForm' + String(i);
this.authnForm.addControl(
formControlGroup,
this.fb.group({
choiceControl: ['', Validators.required],
})
);
this.choiceCallbackObject.push({
callbackValue: callback,
form: this.authnForm.get(formControlGroup),
});
break;
}
case CallbackType.ConfirmationCallback: {
const formControlGroup = 'confirmationCallbackForm';
this.authnForm.addControl(
formControlGroup,
this.fb.group({
confirmationControl: ['', Validators.required],
})
);
this.confirmationCallbackObject = {
callbackValue: callback,
form: this.authnForm.get(formControlGroup),
};
break;
}
case CallbackType.TextOutputCallback: {
this.tempTextCBarray.push(callback as TextOutputCallback);
if(this.tempTextCBarray.length !== 3 || this.tempTextCBarray.length > 3){
console.log('hi')
return;
}else{
this.textCallbackObject.push({
callbackValue: this.tempTextCBarray
});
break;
}
}
case CallbackType.HiddenValueCallback: {
this.hiddenCBarray.push(callback as HiddenValueCallback);
if(this.hiddenCBarray.length !== 3 || this.hiddenCBarray.length > 3){
return;
}else{
console.log(this.hiddenCBarray);
// const formControlGroup = 'hiddenCallbackForm';
// this.authnForm.addControl(
// formControlGroup,
// this.fb.group({
// hiddenControl: [''],
// })
// );
this.hiddenCallbackObject.push({
callbackValue: this.hiddenCBarray,
showHidden: true
});
// form: this.authnForm.get(formControlGroup),
break;
}
}
// }
default:
break;
}
console.log(this.hiddenCallbackObject);
});
// Display built form.
this.loading = false;
}