okta / okta-idx-android

okta-idx-android
https://github.com/okta/okta-idx-android
Apache License 2.0
10 stars 13 forks source link

Submitting wrong passcode challenge produces successful result. Remediation messages are empty as well. #180

Open LoserAntbear opened 1 year ago

LoserAntbear commented 1 year ago

Describe the bug?

Greetings. I've implemented user self-registration flow regarding code examples provided. On the step of mobile phone verification user has to submit code challenge. Although, even if they supply wrong code challenge okta-idx proceeds with that as a successful result.

Generalised schematic description of the flow calls in the code: (I cannot provide actual code pieces, sorry)

// Resuming flow 
var resumeResult: OidcClientResult<IdxResponse> = IdxFlow.resume() ; //  OidcClientResult.Success
var response: IdxResponse = resumeResult.response;
------

// Providing passcode submitted by user
var remediation:  IdxRemediation =  response.remediations[IdxRemediation.Type.ENROLL_AUTHENTICATOR];
remediation.form["credentials.passcode"]?.value = passcode;
------

// proceeding with filled remediation form
when (val resumeResult = flow?.proceed(remediation)) {
      is OidcClientResult.Success -> {
          handleSuccess(resumeResult.result);
      }
      is OidcClientResult.Error -> {
          handleFailure(errorCode.toString(), resumeResult.exception);
      }
      else -> {
          handleFailure(errorCode.toString(), FlowProceedUnknownException("Unknown result"));
      }
  }

Despite wrong passcode is submitted (not the one, given in SMS) and api call returns 401 and form field with errors, okta does not result in OidcClientResult.Error, it produces OidcClientResult.Success.

okta_401

I've tried to access for messages to rely upon them in such case:

private fun  handleSuccess(result: IdxResponse) {
  if (result.messages.isNotEmpty()) {
      throw ResultMessagesNotEmpty(result.messages.messages.joinToString());
  }
}

But the messages collection is empty all the time. Could you, please help me handle the case?

Note: the same process on swift is handled via OidcClientResult.Error (swift counterpart of it). I presumed that the process should match between platforms

What is expected to happen?

Okta produces OidcClientResult.Error when proceeding with remediation, which returns 401 code. Or okta populates related remediation messages.

What is the actual behavior?

Okta produces OidcClientResult.Success when proceeding with remediation, which returns 401 code. Remedation messages collection is empty.

Reproduction Steps?

Sadly, I cannot provide a repo due to security resctrictions, but the steps are described above in details.

Additional Information?

No response

SDK Version

okta-idx-kotlin:2.1.0

Build Information

gradle-7.5.1 com.android.tools.build:gradle:7.2.2

rajdeepnanua-okta commented 1 year ago

@LoserAntbear, in this case, okta-idx-kotlin should ideally return an error. I will look into this issue and see why it's not doing that.

LoserAntbear commented 1 year ago

@rajdeepnanua-okta Thanks a lot! It'll help us to fix the process. :)

rajdeepnanua-okta commented 1 year ago

@LoserAntbear, I looked more into this and it seems to be expected behavior. OidcClientResult.<Success/Error> indicates whether the call to proceed failed. In this case, the response is 401, but there is valid data in the response from calling proceed which indicates how the IDX form should be rendered next. The only case that Error is returned is if the response JSON from the server is empty or invalid.

dynamic-app sample in this repo provides a sample of how it handles the same response here: https://github.com/okta/okta-idx-android/blob/f643c176f15dbd0e2e30ca88f4acf066c7a736de/dynamic-app/src/main/java/com/okta/idx/android/dynamic/auth/DynamicAuthViewModel.kt#L425 The response should be treated as if it's not an error since it returned valid data to render the next form.

LoserAntbear commented 1 year ago

@rajdeepnanua-okta Thanks for the explanation of the current behaviour! Alas, there's another piece of a puzzle: it seems that this error should appear in the messages collection, connected to the response received, isn't it? But response.messages reports empty collection in this case when I trey to get this message to work with it. Am I doing something wrong?

LoserAntbear commented 1 year ago

Okay. Figured that out. For some reasons messages from fields do not get to remediation.form.visibleFields. Also, since allFields is private param and there's no other way to iterate through form fields... You have to access every field by direct name, no matter what. :/ A bit inconvenient, but I was able to access field's messages!

pedroAtBU commented 1 year ago

I struggled to find out this, I created a ktx to check for the error, the same happens with other errors.

fun IdxResponse.checkInvalidPasscodeError(): IdxMessage? { val messageCollection = remediations[IdxRemediation.Type.CHALLENGE_AUTHENTICATOR]?.get("credentials.passcode")?.messages if(messageCollection?.isNotEmpty() == true) { return messageCollection.first() } return null }

rajdeepnanua-okta commented 1 year ago

I agree this seems a bit inconvenient. Looking into how this can be made easier, and how other idx SDKs are doing the same.