# Delete Mobile PIN

div
div
Client SDK
div
Mobile approve
div
SSO
div
Sub-journey
> Deletes an app-specific PIN code previously registered by the user for authentication


## About client-facing steps

A journey is a sequence of steps that are executed when it's invoked by a client application (known as the "client"). Some steps require involvement from the client, such as to collect user input. Other steps, like validating a token, are executed in the backend by the Mosaic journey engine alone.

When invoked, the journey begins executing the steps while the client waits for further instructions. When a journey reaches a client-facing step, the journey asks the client for the required input and then waits for the client to respond. The client presents their own UI if user interaction is required, and returns input to the journey. The journey then proceeds in a similar manner until it's completed.

## Description

This client-facing step unbinds the Mosaic mobile app from the device by deleting cryptographic seed and PIN code used to protect it. Once deleted, the user can no longer authenticate using the [Mobile PIN Authentication](/guides/orchestration/journeys/authenticate_mobile_pin) step.

The PIN-based credentials are bound to a specific device. When deleting a PIN code, the user may be provided implicitly by the journey if already authenticated; otherwise, a user identifier must be specified in the step configuration.

When triggered, this step instructs the client to list devices with configured PIN codes and prompt the user to delete a PIN code for current device. The client uses the Authentication SDK to perform the client-side deregistration (removing the cryptographic seed) while the SDK signals Mosaic to revoke credentials associated with the key ID. The client sends this result back to the journey using the Orchestration SDK.

The deletion is only finalized once the journey responds with a `PinCodeUnregistrationCommit` instruction, which the client must handle by invoking `unregistrationContext().commit()`. If PIN deletion fails or is cancelled, the journey either aborts or proceeds to a failure branch, depending on configuration.

To support PIN deletion, you must:

* Implement the Authentication SDK calls required for deleting (see [Example](#example))
* Implement the Orchestration SDK call to submit the client response back to the journey


Note
For Android, the Authentication SDK requires `compileSdk` 34 (or greater) and `minSdk` 23.

## Configuration

div
| Field | Description |
|  --- | --- |
| **User auth state** | Indicates if the user has authenticated in this journey. If the user is authenticated (default), the user context is provided implicitly by the journey. If not, a user identifier must be configured. |
| **Identifiers** | Only configured if the journey doesn't authenticate the user before invoking this step. Can be an external user ID, email, phone number, username, or a [custom identifier](/guides/user/manage_user_schema), if configured for B2C users in your tenant. |
| **Error output variable** | Name of the variable that stores any errors returned by the step |
| **Failure behavior** | Determines the behavior in case of failure, which either aborts the journey or proceeds to a failure branch of the control flow (default). " |


## Example

After requesting to delete a PIN code, the client uses the Authentication SDK to deregister the PIN-protected credential. The resulting public key, key ID, and key type are removed. The result is then submitted to the journey using the Orchestration SDK.

The journey responds with a `PinCodeUnregistrationCommit` instruction, which the client must handle by calling `unregistrationContext().commit()` to finalize the PIN deletion.


```kotlin
  data class PinCodeDeletionInput(@SerializedName("publicKeyId") val publicKeyId: String)

  private fun processServiceResponse(idoResponse: TSIdoServiceResponse) {
      when (idoResponse.journeyStepId) {
          TSIdoJourneyActionType.PinCodeDeletion.type -> unregisterPinCode(userId)
      }
  }

  private fun unregisterPinCode(userId: String) {
          TSAuthentication.unregisterPinCode(userId, object : TSAuthCallback<TSPinCodeUnregistrationResult, TSPinCodeUnregistrationError> {
              override fun error(error: TSPinCodeUnregistrationError) {
                  //TODO: handle error
              }

              override fun success(result: TSPinCodeUnregistrationResult) {
                  TSIdo.submitClientResponse(TSIdoClientResponseOptionType.ClientInput.type, PinCodeDeletionInput(
                      publicKeyId = result.keyId()
                  ),
                  object: TSIdoCallback<TSIdoServiceResponse> {
                      override fun idoSuccess(serviceResponse: TSIdoServiceResponse) {
                          //TODO: handle service response
                      }

                      override fun idoError(error: TSIdoSdkError) {
                          //TODO: handle error
                      }

                      override fun idoInstruction(instruction: TSIdoInstruction) {
                          commitPinCodeUnregistration(result, instruction)
                      }
                  })
              }
          })
      }

      private fun commitPinCodeUnregistration(result: TSPinCodeUnregistrationResult, instruction: TSIdoInstruction) {
          val keyID = instruction.data?.optString("public_key_id")
          if (instruction.type == TSIdoInstructionType.PinCodeUnregistrationCommit
              && result.keyId() == keyID) {
                  result.unregistrationContext().commit()
          } else {
              //TODO: handle key id or instruction type mismatch
          }
      }
```


```swift
  private func runPinCodeDeletionAction() {
      TSAuthentication.shared.initialize(baseUrl: "your_base_url",
                        clientId: "your_client_id")

      Task {
        do {
          let result = try await TSAuthentication.shared.unregisterPinCode(username: "userIdentifier")

          outputData.publicKeyId = result.publicKeyId

          submitClientResponse(clientResponseOptionId: .clientInput, data: ["publicKeyId": result.publicKeyId]) { instruction in

            guard let publicKeyId = instruction.data["public_key_id"] as? String else { return }

            if instruction.type == .PinCodeUnregistrationCommit,
              result.publicKeyId == publicKeyId {
              do {
                try result.unregistrationContext.commit()
              } catch {
                debugPrint("[ERROR] Pin deregistration failed with error: \(error)")
              }
            }
          }

        } catch {
          goToNextStep(clientResponseOption: .fail)
          debugPrint("[ERROR] Pin deregistration failed with error: \(error)")
        }
      }
    }

    func submitClientResponse(clientResponseOptionId: TSIdoClientResponseOptionType, data: [String: Any]?, instructions: TSIdo.TSSDKInstructionsCallback?) {
      do {
        try TSIdo.submitClientResponse(clientResponseOptionId: clientResponseOptionId, data: data, handleInstruction: instructions)
      } catch {
        debugPrint("Journey error")
      }
    }
```