Change PIN
This article explains all the steps required to add the change PIN functionality to your app.
In some cases the user may want to change their PIN. For this purpose, the iOS SDK provides an API. The iOS SDK requires that the user is authenticated before they can change the PIN. The new PIN that the user chooses will get validated against the PIN policy and either accepted by the iOS SDK or declined (based on the policy rules). In case of an invalid current PIN, a new attempt to enter the PIN is given.
Note
Changing the PIN is not possible for a stateless user.
Initialize the change PIN flow
The UserClient
offers a method to change the PIN for the currently logged in user. This methods is called changePin
and requires one argument:
delegate
- the object that conforms to theChangePinDelegate
protocol.
Note
The iOS SDK must have an authenticated user (SharedUserClient.instance.authenticatedUserProfile
cannot be nil), otherwise an error will be returned.
SharedUserClient.instance.changePin(delegate)
[[ONGUserClient sharedClient] changePin: delegate];
On successful invocation, the iOS SDK will immediately notify the delegate method ChangePinDelegate
userClient(_:didStartPinChangeForUser:)
this receives the following arguments:
userClient
- the instance of theUserClient
that is responsible for the PIN change. It corresponds to theSharedUserClient.instance.changePin
receiver.userProfile
- the instance of theUserProfile
for which the PIN is going to be changed. It is the same profile as the profile of the currently authenticated user returned by invoking theSharedUserClient.instance.authenticatedUserProfile
method.
func userClient(_ userClient: UserClient, didStartPinChangeForUser profile: UserProfile)
{
// handle change pin start if needed
}
- (void)userClient:(ONGUserClient *)userClient didStartPinChangeForUser:(ONGUserProfile *)userProfile
{
// handle change pin start if needed
}
If there is no authenticated user found, or something else went wrong, the iOS SDK notifies you about the raised error by calling the ChangePinDelegate userClient(_:didFailToChangePinForUser:error:)
method. It receives the following arguments:
userClient
- the instance of theUserClient
that is responsible for the PIN change. It is the same as the-SharedUserClient.instance.changePin
receiver.userProfile
- an instance of theUserProfile
for which the PIN is going to be changed. It is the same profile as the profile of the currently authenticated user returned by invoking theSharedUserClient.instance.authenticatedUserProfile
method.error
- an instance of theNSError
class. The possible error domains areONGGenericErrorDomain
,ONGChangePinErrorDomain
, orONGPinAuthenticationErrorDomain
.
func userClient(_ userClient: UserClient, didFailToChangePinForUser profile: UserProfile, error: Error)
{
// handle change pin failure
}
- (void)userClient:(ONGUserClient *)userClient didFailToChangePinForUser:(ONGUserProfile *)userProfile error:(NSError *)error
{
// handle change pin failure
}
Both methods mentioned above are optional.
Authentication
After the change PIN flow has been started, it asks the delegate to let the user provide the current PIN. This can be done by implementing the ChangePinDelegateuserClient(_:didReceivePinChallenge:)
, which is required. It receives the following arguments:
userClient
is the instance of theUserClient
that is responsible for the PIN change. It is the same as theSharedUserClient.instance.changePin
receiver.challenge
is an instance of thePinChallenge
. This object encapsulates all required information for successful authentication.
func userClient(_ userClient: UserClient, didReceivePinChallenge challenge: PinChallenge)
{
if challenge.error != nil {
//show remaining pin attempts
print(challenge.remainingFailureCount)
}
// assume that we have method that dispay input and responds by either a cancel or pin
presentPinViewController { pin, cancelled in
if cancelled {
// user declined pin change for some reason
challenge.sender.cancelChallenge(challenge)
} else {
// provide pin to the SDK
challenge.sender.respondWithPin(pin, challenge: challenge)
}
}
}
- (void)userClient:(ONGUserClient *)userClient didReceivePinChallenge:(ONGPinChallenge *)challenge
{
if (challenge.error) {
//show remaining pin attempts
NSLog(@"%d", challenge.remainingFailureCount);
}
// assume that we have method that dispay input and responds by either a cancel or pin
[self presentPinViewController:^(NSString *pin, BOOL cancelled) {
if (cancelled) {
// user declined pin change for some reason
[challenge.sender cancelChallenge:challenge];
} else {
// provide pin to the SDK
[challenge.sender respondWithPin:pin challenge:challenge];
}
}];
}
Create a new PIN
After the current PIN has been successfully entered the iOS SDK forces the end-user to create a new one by calling the ChangePinDelegateuserClient(_:didReceiveCreatePinChallenge:)
method that receives following arguments:
userClient
is the instance of theUserClient
that is responsible for the PIN change. It is the same as theSharedUserClient.instance.changePin
receiver.challenge
is an instance of theCreatePinChallenge
.
Lets take a more detailed look at the CreatePinChallenge
. This class provides the required information for creating a new PIN and also a sender awaiting for the response:
userProfile
is the instance of theUserProfile
for which the change PIN action was started.pinLength
is the required length for a new PIN.error
is the error that describes the previous failure reason (if any). Possible error domains areONGPinValidationErrorDomain
andONGGenericErrorDomain
.sender
is the object that conforms to theCreatePinChallengeSender
protocol. After the PIN has been entered, you have to provide it to the sender object to proceed.
Generally, creation of a new PIN is quite similar to create PIN step of the registration process.
Here is sample code that demonstrates the implementation of the create PIN challenge:
func userClient(userClient: UserClient, didReceiveCreatePinChallenge challenge: CreatePinChallenge)
{
if challenge.error != nil {
// show error description
}
// assume that we have method that dispay input and responds with a new pin
presentNewPinViewController { pin, cancelled in
if cancelled {
// user declined pin change for some reason
challenge.sender.cancelChallenge(challenge)
} else {
// provide pin to the SDK
challenge.sender.respondWithPin(pin, challenge: challenge)
}
}
- (void)userClient:(ONGUserClient *)userClient didReceiveCreatePinChallenge:(ONGCreatePinChallenge *)challenge
{
if (challenge.error) {
// show error description
}
// assume that we have method that dispay input and responds with a new pin
[self presentNewPinViewController:^(NSString *pin, BOOL cancelled) {
if (cancelled) {
// user declined pin change for some reason
[challenge.sender cancelChallenge:challenge];
} else {
// provide pin to the SDK
[challenge.sender respondWithPin:pin challenge:challenge];
}
}];
}
Validation
The new PIN always gets validated by the iOS SDK using the PIN policy, provided on the IDAAS-core admin console. A policy can include the following elements:
- max number of similar digits
- whether sequences are allowed or not
- PIN length
- deny-listed PINs
Before the new PIN is passed to the iOS SDK, you can validate the provided PIN with the policy by calling the SharedUserClient.validatePolicyCompliance
method, which expects the following arguments:
pin
-String
is the PIN that is going to be validated.completion
- completion block((Error?) -> Void))
that is invoked by the iOS SDK when validation is completed. The possible error domains are:ONGPinValidationErrorDomain
andONGGenericErrorDomain
.
Let's take a look a the sample code for validating a PIN with the PIN policy:
func validatePolicyCompliance(for pin: String, completion: @escaping ((Error?) -> Void)) {
if let error = error {
switch error.code {
case ONGPinValidationError.PinBlackListed:
// pin has been black listed on the TS and can not be used anymore
case ONGPinValidationError.PinShouldNotBeASequence:
// pin represents a sequence which is not allowed by policy
case ONGPinValidationError.WrongPinLength:
// pin lenghts is invalid (too short or too long)
case ONGPinValidationError.PinShouldNotUseSimilarDigits:
// pin uses too many similar digits.
if let maxSimilarDigits = error.userInfo[ONGPinValidationErrorMaxSimilarDigitsKey] as? Int {
print("You can not use more than \(maxSimilarDigits) in the pin")
}
default:
// we fall into generic error domain
break
}
}
}
[[ONGUserClient sharedClient] validatePinWithPolicy:@"1234" completion:^(BOOL valid, NSError * _Nullable error) {
if (error) {
// our pin is invalid
switch (error.code) {
case ONGPinValidationErrorPinBlackListed:
// pin has been black listed on the TS and can not be used anymore
break;
case ONGPinValidationErrorPinShouldNotBeASequence:
// pin represents a sequence which is not allowed by policy
break;
case ONGPinValidationErrorWrongPinLength:
// pin lenghts is invalid (too short or too long)
break;
case ONGPinValidationErrorPinShouldNotUseSimilarDigits: {
// pin uses too many similar digits.
NSNumber *maxSimilarDigits = error.userInfo[ONGPinValidationErrorMaxSimilarDigitsKey];
NSLog(@"You can not use more than %@ similar digits in the pin", maxSimilarDigits);
}
break;
default:
// we fall into generic error domain
break;
}
}
}];
Error Handling
Various errors may occur during the change PIN process: from PIN validation to generic iOS SDK errors.
Validate current PIN
The most common error when validating the current PIN ChangePinDelegateuserClient(:didReceivePinChallenge:)
is the PinAuthenticationErrorInvalidPin
of the PinAuthenticationErrorDomain
that happens in case of an invalid PIN. However, it doesn't complete the change PIN flow.
The iOS SDK gives several attempts for the user to provide the correct PIN. The information on the max failure count and attempt count are reflected in the PinChallenge#maxFailureCount
and PinChallenge#previousFailureCount
.
In case the user has entered an invalid PIN too many times, the iOS SDK deregisters the user and completes the change PIN flow with the GenericErrorUserDeregistered
error of the GenericErrorDomain
and will notify the delegate via the ChangePinDelegate userClient(:didFailToChangePinForUser:error:)
method. When this happens, you must also clean up any local data that you might have stored about this user profile, because the user is no longer registered on the device and must register again.
Other errors such as ONGGenericErrorNetworkConnectivityFailure
or ONGGenericErrorServerNotReachable
do not affect on the PIN attempts counter and thus can not lead to a user deregistration error.
Create PIN
During create PIN ChangePinDelegateuserClient:didReceive:
, the most common errors come from the ONGPinValidationErrorDomain
. Other errors such as ONGGenericErrorNetworkConnectivityFailure
or ONGGenericErrorServerNotReachable
need to be handled appropriately by notifying the user about a missing internet connection.