Skip to main content

Decide pending Operations

You can start the decision flow using three methods, from a link, from a notification or from fetched pending events. First, you need to start a decision flow, there are three ways to do it:

Client requirements

Create a decision flow

// using passing the operation object
let decision = try trustfactorClient.startOperationDecisionFlow(for: <TFOperation.ID>, applicationProfileId: <TFApplicationProfile.ID>, context: .history)

// using a decision URL (context is infered as ".deeplink")
let decision = try trustfactorClient.startOperationDecisionFlow(url: <URL>)

// using a notification (context is infered as ".pushNotification")
let decision = try trustfactorClient.startOperationDecisionFlow(notification: <UNNotification>)

Get operations details

// get details: mandatory
decision.getOperationDetails() { result in
switch result {
case .success(let operation):
// handle success

case .failure(let error):
// handle error
}
}

Add checkpoints

Checkpoints are not mandatory but we encourage their usage.

// Add this checkpoint if/when a Summary of the operation is shown to the user.
decision.addCheckpoint(.didShowOperationSummary)
// Add this checkpoint when the full list of details is presented to the user.
decision.addCheckpoint(.didShowOperationDetails)

Authentication

Some Operations require the user to authenticate using a PIN, Biometry or both. It's important to keep in mind that the validation of the required authentication mechanisms is only done during the decision.

Note: There's no need to set authentications if the user is rejecting the operation.

 // add PIN authentication
if operationDetails.requiredAuthentications?.contains(.pin) {
try decision.addAuthentication(.pin(<string>))
}
// add biometric authentication
if operation.requiredAuthentications?.contains(.biometrics) {
try decision.addAuthentication(.biometric(context: <LAContext>))
}

Precise location

An Operation may require the user to share a precise location with the decision — read it from the operation details (TFOperation.requiresPreciseLocation on iOS, TFOperation.isPreciseLocationRequired() on Android). Only approving needs one: a user can always reject an Operation, whatever it requires.

The fix has to be taken for this decision. The position shared through updateLocation(_:) is the device's last known location, is kept without expiry and may not have come from GPS, so it never satisfies the requirement.

Approving without a usable fix fails with operationDecision(.requirePreciseLocation) / OPERATION_DECISION / REQUIRE_PRECISE_LOCATION — locally when the SDK can tell, and from TrustFactor itself (E_400080018) otherwise. Unlike a wrong confirmation code, this leaves the Operation pending, so the user can allow the permission and decide again.

// A location the app captured for this decision — CLLocationManager.requestLocation(), not a cached one.
if operation.requiresPreciseLocation {
decision.setPreciseLocation(<CLLocation>)
}
Reduced accuracy

If your app declares NSLocationDefaultAccuracyReduced, a user who granted location access still gives you a fuzzed position. Call requestTemporaryFullAccuracyAuthorization(withPurposeKey:) before capturing the fix, and declare the purpose key in NSLocationTemporaryUsageDescriptionDictionary.

Confirmation code

An Operation may require the user to pick a confirmation code. The client application shows the correct code on its own screen; TrustFactor sends the device a shuffled list of candidates and never says which one is right, so an attacker holding the device can only guess.

Read the options from the operation details, show them in the order they arrived, and submit the 1-based position of the one the user picked — never the code itself. Do not sort, filter or de-duplicate the list: the position is the answer.

Only approving needs a code; an Operation can always be rejected without one.

A wrong code is final

Every other decision-time requirement — PIN, biometrics, a precise location — leaves the Operation pending so the user can try again. A wrong confirmation code does the opposite: the Operation is recorded as failed, the client application is notified, and it cannot be decided again. Do not offer a retry; a new Operation has to be created.

Approving without picking anything therefore fails locally with operationDecision(.requireConfirmationCode) / OPERATION_DECISION / REQUIRE_CONFIRMATION_CODE rather than being sent — spending the user's only attempt on a question they were never asked would be the wrong outcome. A wrong pick returns .wrongConfirmationCode / WRONG_CONFIRMATION_CODE (E_400080019).

if operation.requiresConfirmationCode {
// Show operation.confirmationCodeOptions in this order and let the user choose.
let picked: Int = <index of the option the user tapped> // 0-based, as displayed
decision.setConfirmationCodeOption(picked + 1) // submitted 1-based
}

Decision

decision.reject() { result, correlationId in
// handle result
}

// OR

decision.approve() { result, correlationId in
// handle result
}