Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #96: Test for maximum attempts for OTP verification during SCA step #97

Merged
merged 1 commit into from
Sep 6, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ void testScaFailedPresenceCheck() throws Exception {
initPresenceCheck(processId);
if (!config.isSkipResultVerification()) {
verifyStatusBeforeOtp();
verifyOtpCheckFailed(processId);
verifyOtpCheckFailed(processId, IdentityVerificationPhase.PRESENCE_CHECK);
assertIdentityVerificationStateWithRetries(
new IdentityVerificationState(IdentityVerificationPhase.PRESENCE_CHECK, IdentityVerificationStatus.NOT_INITIALIZED));
verifyProcessNotFinished(processId);
Expand All @@ -236,9 +236,9 @@ void testScaFailedOtpCheck() throws Exception {
initPresenceCheck(processId);
if (!config.isSkipResultVerification()) {
verifyStatusBeforeOtp();
verifyOtpCheckFailedInvalidCode(processId);
verifyOtpCheckFailedInvalidCode(processId, IdentityVerificationPhase.OTP_VERIFICATION);
assertIdentityVerificationStateWithRetries(
new IdentityVerificationState(IdentityVerificationPhase.PRESENCE_CHECK, IdentityVerificationStatus.NOT_INITIALIZED));
new IdentityVerificationState(IdentityVerificationPhase.OTP_VERIFICATION, IdentityVerificationStatus.OTP_VERIFICATION_PENDING));
verifyProcessNotFinished(processId);
}

Expand Down Expand Up @@ -638,6 +638,28 @@ void initDocumentVerificationSdkTest() throws Exception {
assertNotNull(responseOK.getMac());
}

@Test
void testFailedScaOtpMaxFailedAttemptsIdentityRestart() throws Exception {
final TestContext context = prepareActivation();
final String activationId = context.activationId;
final String processId = context.processId;

processDocuments(context);

initPresenceCheck(processId);
if (!config.isSkipResultVerification()) {
for (int i = 0; i < 4; i++) {
verifyStatusBeforeOtp();
verifyOtpCheckFailedInvalidCode(processId, IdentityVerificationPhase.OTP_VERIFICATION);
}
// Verify restart of identity verification
verifyStatusBeforeOtp();
verifyOtpCheckFailedInvalidCode(processId, null);
}

powerAuthClient.removeActivation(activationId, "test");
}

private TestContext prepareActivation() throws Exception {
return prepareActivation("");
}
Expand Down Expand Up @@ -1085,68 +1107,60 @@ private OnboardingStatus checkProcessStatus(String processId) throws Exception {
return status;
}

private void verifyOtpCheckFailed(String processId) throws Exception {
private void verifyOtpCheckFailed(String processId, IdentityVerificationPhase allowedPhase) throws Exception {
final String otpCode = getOtpCode(processId, OtpType.USER_VERIFICATION);
verifyOtpCheck(processId, false, otpCode);
verifyOtpCheck(processId, false, otpCode, allowedPhase);
}

private void verifyOtpCheckFailedInvalidCode(String processId) throws Exception {
private void verifyOtpCheckFailedInvalidCode(String processId, IdentityVerificationPhase allowedPhase) throws Exception {
final String otpCode = "invalid";
verifyOtpCheck(processId, false, otpCode);
verifyOtpCheck(processId, false, otpCode, allowedPhase);
}

private void verifyOtpCheckSuccessful(String processId) throws Exception {
final String otpCode = getOtpCode(processId, OtpType.USER_VERIFICATION);
verifyOtpCheck(processId, true, otpCode);
verifyOtpCheck(processId, true, otpCode, IdentityVerificationPhase.COMPLETED);
}

private void verifyOtpCheck(final String processId, final boolean expectedResult, final String otpCode) throws Exception {
private void verifyOtpCheck(final String processId, final boolean expectedResult, final String otpCode, IdentityVerificationPhase allowedPhase) throws Exception {
if (config.isSkipOtpVerification()) {
return;
}
boolean otpVerified = false;
boolean verificationComplete = false;
for (int i = 0; i < 10; i++) {
IdentityVerificationState idState = checkIdentityVerificationState();
if (idState.getStatus() == IdentityVerificationStatus.OTP_VERIFICATION_PENDING) {
IdentityVerificationOtpVerifyRequest otpVerifyRequest = new IdentityVerificationOtpVerifyRequest();
otpVerifyRequest.setProcessId(processId);
otpVerifyRequest.setOtpCode(otpCode);
stepLogger = new ObjectStepLogger(System.out);
encryptModel.setData(objectMapper.writeValueAsBytes(new ObjectRequest<>(otpVerifyRequest)));
encryptModel.setUriString(config.getEnrollmentOnboardingServiceUrl() + "/api/identity/otp/verify");
encryptModel.setScope("activation");
new EncryptStep().execute(stepLogger, encryptModel.toMap());
assertTrue(stepLogger.getResult().isSuccess());
assertEquals(200, stepLogger.getResponse().getStatusCode());

otpVerified = stepLogger.getItems().stream()
.filter(isStepItemDecryptedResponse())
.map(StepItem::getObject)
.map(Object::toString)
.map(it -> safeReadValue(it, new TypeReference<ObjectResponse<OtpVerifyResponse>>() {}))
.filter(Objects::nonNull)
.map(ObjectResponse::getResponseObject)
.map(OtpVerifyResponse::isVerified)
.findFirst()
.orElse(false);

if (otpVerified) {
// Force status refresh
continue;
}
}
if (idState.getStatus() == IdentityVerificationStatus.ACCEPTED) {
verificationComplete = true;
break;
} else if (idState.getPhase() == IdentityVerificationPhase.PRESENCE_CHECK) {
verificationComplete = true;
break;
} else {
Thread.sleep(1000);
}
IdentityVerificationState idState = checkIdentityVerificationState();
if (idState.getStatus() == IdentityVerificationStatus.OTP_VERIFICATION_PENDING) {
IdentityVerificationOtpVerifyRequest otpVerifyRequest = new IdentityVerificationOtpVerifyRequest();
otpVerifyRequest.setProcessId(processId);
otpVerifyRequest.setOtpCode(otpCode);
stepLogger = new ObjectStepLogger(System.out);
encryptModel.setData(objectMapper.writeValueAsBytes(new ObjectRequest<>(otpVerifyRequest)));
encryptModel.setUriString(config.getEnrollmentOnboardingServiceUrl() + "/api/identity/otp/verify");
encryptModel.setScope("activation");
new EncryptStep().execute(stepLogger, encryptModel.toMap());
assertTrue(stepLogger.getResult().isSuccess());
assertEquals(200, stepLogger.getResponse().getStatusCode());

otpVerified = stepLogger.getItems().stream()
.filter(isStepItemDecryptedResponse())
.map(StepItem::getObject)
.map(Object::toString)
.map(it -> safeReadValue(it, new TypeReference<ObjectResponse<OtpVerifyResponse>>() {}))
.filter(Objects::nonNull)
.map(ObjectResponse::getResponseObject)
.map(OtpVerifyResponse::isVerified)
.findFirst()
.orElse(false);

// Force status refresh
idState = checkIdentityVerificationState();
}
if (idState.getStatus() == IdentityVerificationStatus.ACCEPTED) {
verificationComplete = true;
} else if (idState.getPhase() == allowedPhase) {
verificationComplete = true;
}
assertTrue(verificationComplete, "Verification should complete, either valid OTP or returning to PRESENCE_CHECK phase");
assertTrue(verificationComplete, "Verification should complete, either valid OTP or phase set to " + allowedPhase);
assertEquals(expectedResult, otpVerified);
}

Expand Down