User registration
This document explains adding user registration to your application. It assumes that the iOS SDK has already been added to your application as described in set up the the project.
The iOS SDK is accompanied by a separate Example App that provides a basic implementation and shows how to interact with the iOS SDK. The iOS SDK relies on the app to perform tasks that can not be performed by the iOS SDK itself.
The Example App demonstrates the essential steps that must be performed by the app developer.
User registration is performed using the UserClient
. The instance can be obtained by using the userClient
property of the Client
implementation object or by calling the SharedClient.instance
method. Attempting to obtain UserClient
without configuring the iOS SDK first, results in throwing an NSInternalInconsistencyException
exception.
Note
The iOS SDK does not provide any UI components. The result of every operation on the UserClient
is reported back to the app via a delegate
. Hence, the iOS SDK does provides delegate interfaces that must be implemented by the app. All callbacks are performed on the main queue ,so the app developer can invoke methods on UIKit components directly without having to deal with queue management. Delegates are triggered when an action is requested from the app or to return the result of a requested action back to the app.
The iOS SDK uses the OAuth 2.0 protocol to authenticate the device to access protected resources. To support this protocol, the iOS SDK acts as an OAuth 2.0 client.
Register user
User registration is one of the first things that you need to take care of. User registration essentially means connecting a user account to a mobile application installed on a device. It is possible to register multiple user accounts with the same application. If multiple users need to be registered, the registration flow that is described below needs to be triggered once for every user. There is one restriction: you cannot register the same user multiple times. If you try to register the same user account twice, you will essentially override the first registration for that user. Hence, it will not fail but it will reuse the profile that was already created for that user on the device.
The UserClient
offers the registerUser
method to register a new user profile. The new user profile value object will be generated during the authentication process and will be returned in the success callback.
The registerUser
method requires three arguments:
IdentityProvider
is a specific identity provider used for registration, if nil is provided, the IDAAS-core will pick a default identity provider.[String]
specifies the scopes array that authentication is requested for. When no scopes are requested, the default scopes of the application will be used.RegistrationDelegate
is the registration delegate that delegates control back to the app in case it needs to do something, or in case of a successful or failed registration attempt. Thedelegate
must implement the methods specified in theRegistrationDelegate
class.
The example below shows you how to call the registerUser
method and let the calling class also implement the required delegate:
Example: Register a new user with the default identity provider
SharedUserClient.instance.registerUser(with: identityProvider, scopes: ["read"], delegate: self)
// Handle specific registration challenge
func userClient(_ userClient: UserClient, didRegisterUser userProfile: UserProfile, identityProvider: IdentityProvider, info: CustomInfo?) {
// The user is successfully registered
}
[[ONGUserClient sharedInstance] registerUserWithIdentityProvider:nil scopes:@[@"read"] delegate:self];
// Handle specific registration challenge
- (void)userClient:(ONGUserClient *)userClient didRegisterUser:(ONGUserProfile *)userProfile identityProvider:(ONGIdentityProvider *)identityProvider info:(ONGCustomInfo *_Nullable)info
{
// The user is successfully registered
}
Register a stateless user
The iOS SDK supports custom user registration without creating a profile. The access token created in the process has the Stateless
type and is not stored in the user's device. This makes it possible to implement support for App2App authentication towards external identity schemas that do not allow you to make derived identities (like DigiD) in a custom registration script. This means the app will be registered just-in-time each time a user selects the custom registration method.
To register stateless user a new public API method is introduced registerStatelessUser
:
SharedUserClient.instance.registerStatelessUser(with: identityProvider, scopes: ["read"], delegate: self)
[[ONGUserClient sharedInstance] registerStatelessUserWithIdentityProvider:nil scopes:@[@"read"] delegate:self];
Limitations
- Mobile authentication enrollment is not possible.
- Implicit authentication is not possible.
- Changing the PIN is not possible, because a user does not set any PIN during the registration process.
- Works only with a custom API identity provider.
Stateless authentication has to be enabled for mobile clients in the IDAAS-core Admin UI, otherwise an error will be thrown while calling registerStatelessUser
.
To enable the feature, head to Configuration → Applications in Admin UI. There you can create a new application or edit an existing one. Either way, to enable stateless authentication, under User authentication, enable Stateless authentication.
Stateless authentication requires a configured custom API identity provider.
Identity providers
The platform supports two main types of identity providers: browser and custom API. Both types of identity providers can be defined and configured in the IDAAS-core configuration. All identity providers that are configured in the app are returned by the iOS SDK as IdentityProvider
.
Depending on the identity provider type, the iOS SDK starts the registration process accordingly.
Note
If you are using default identity provider you must know its type to handle appropriate methods from RegistrationDelegate
.
The object passed as the delegate must conform to the RegistrationDelegate
protocol. The iOS SDK will call the following methods on it:
userClientDidStartRegistration(_:)
is the method called when user registration is started.userClient(_:didReceiveBrowserRegistrationChallenge:)
is the method called when the registration action requires you to open a browser with the given URL.userClient(_:didReceiveCustomRegistrationInitChallenge:)
is the method called when the initialization of two-step custom registration requires optional data to continue.userClient(_:didReceiveCustomRegistrationFinishChallenge:)
is the method called when the second step of two-step or one-step custom registration requires optional data to continue.userClient(_:didRegisterUser:identityProvider:info:)
is the method called when a registration action is completed with success.userClient(_:didFailToRegisterWith:error:)
is the method called when a registration action failed with an error.userClient(_:didReceivePinRegistrationChallenge:)
is the method called when a registration action requires the creation of a PIN to continue.
Choose a PIN
The next step in the registration process is letting the user select a PIN. This process is triggered by the iOS SDK. It delegates control over to the app by calling the userClient(_:didReceivePinRegistrationChallenge:)
method on the RegistrationDelegate
.
The userClient(_:didReceivePinRegistrationChallenge:)
method has two parameters:
- The userClient (
UserClient
) is the user client performing registration. - The challenge (
CreatePinChallenge
) is the create PIN challenge used to complete registration.
The challenge object represents the create PIN challenge. It has the following parameters:
- The userProfile (
UserProfile
) is the user profile for which the create PIN challenge was sent. - The pinLength (
NSUInteger
) is the required length for a new PIN. - The error (
Error
) is the error describing the cause of the failure of the previous challenge response. Possible error domains are:ONGPinValidationErrorDomain
,ONGGenericErrorDomain
. - The sender (
CreatePinChallengeSender
) is the sender waiting for a response to the create PIN challenge.
The response to the create PIN challenge is sent using the respondWithCreatedPinChallenge:challenge:
method of the challenge sender.
Example: Implementation of userClient(_:didReceivePinRegistrationChallenge:) delegate method
func userClient(_ userClient: UserClient,
didReceivePinRegistrationChallenge challenge: CreatePinChallenge) {
askUserToCreatePin { createdPin in
if createdPin != nil {
// Once the user has entered the PIN call the delegate
createPinChallenge.sender.respond(with: createdPin, to: challenge)
} else {
// Or cancel challenge
createPinChallenge.sender.cancel(challenge)
}
}
}
- (void)userClient:(ONGUserClient *)userClient didReceivePinRegistrationChallenge:(ONGCreatePinChallenge *)challenge
{
[self askUserToCreatePin:^(NSString *createdPin) {
// Once the user has entered the PIN call the delegate
[challenge.sender respondWithCreatedPin:createdPin challenge:challenge];
// Or cancel challenge
[challenge.sender cancelChallenge:challenge];
}];
}
The respondWithCreatedPin:challenge:
method of the CreatePinChallengeSender
needs to be called in order to let the iOS SDK process the provided PIN. The iOS SDK might trigger an error in case the chosen PIN is invalid. This could be because it is not compliant with the PIN policy, for example. The possible errors are specified in the ONGPinValidationErrorDomain
. The following is an example of challenge error handling.
Note
The PIN must not be stored anywhere on the device. Storing the PIN on the device creates a security issue, as an attacker could potentially extract the PIN from the device. For information about handling the PIN correctly, see [({filename}/pages/omi-sdk/ios-sdk/ios-sdk-authenticators.md#authenticate-user-with-pin]
Example: Implementation of challenge error handling
func userClient(_ userClient: UserClient, didReceive createPinChallenge: CreatePinChallenge) {
askUserToCreatePin { createdPin in
if createdPin != nil {
// Once the user has entered the PIN call the delegate
createPinChallenge.sender.respond(with: createdPin, to: createPinChallenge)
} else {
// Or cancel challenge
createPinChallenge.sender.cancel(createPinChallenge)
}
}
}
- (void)userClient:(ONGUserClient *)userClient didReceivePinRegistrationChallenge:(ONGCreatePinChallenge *)challenge
{
// Once the user has entered the PIN call the delegate
[self askUserToCreatePin:^(NSString *createdPin) {
[challenge.sender respondWithCreatedPin:createdPin challenge:challenge];
};
NSString *errorDescription = nil;
if ([challenge.error.domain isEqualToString:ONGPinValidationErrorDomain]) {
switch (challenge.error.code) {
case ONGPinValidationErrorPinBlackListed:
errorDescription = @"Provided PIN was marked as blacklisted.";
break;
case ONGPinValidationErrorPinShouldNotBeASequence:
errorDescription = @"Provided PIN contains a not allowed sequence";
break;
case ONGPinValidationErrorPinTooShort:
errorDescription = @"Provided PIN is too short";
break;
case ONGPinValidationErrorPinShouldNotUseSimilarDigits: {
NSNumber *maxSimilarDigits = error.userInfo[ONGPinValidationErrorMaxSimilarDigitsKey];
errorDescription = [NSString stringWithFormat:@"A PIN with less %zd similar digits must be chosen", maxSimilarDigits];
}
break;
}
} else {
// general error
errorDescription = error.localizedDescription;
}
// show error to user
}
After the user has successfully provided the PIN, the registration process is finished. One of the following methods on your implementation of the RegistrationDelegate
will be called, depending on the outcome of the registration process.
A successful outcome of the user registration will result in a user profile object. This object is the connection between the user and the registered profile in the iOS SDK. We recommend to ask the user to provide a human-readable reference to this profile (such as a name). There is also an identity provider object returned for which the user registration succeeded.
To initiate authentication, this user profile object needs to be provided to the iOS SDK. We recommend letting the user choose a name for their profile, so they can easily remember which user account it is linked to. A failure outcome of the user registration will result in an error and an identity provider object for which an error occurred.
func userClient(_ userClient: UserClient,
didRegisterUser userProfile: UserProfile,
identityProvider: IdentityProvider,
info: CustomInfo?) {
// the user is successfully registered
}
func userClient(_ userClient: UserClient,
didFailToRegisterWith identityProvider: IdentityProvider,
error: Error) {
// handle authentication error
}
- (void)userClient:(ONGUserClient *)userClient didRegisterUser:(ONGUserProfile *)userProfile identityProvider:(ONGIdentityProvider *)identityProvider info:(ONGCustomInfo *_Nullable)info
{
// the user is successfully registered
}
- (void)userClient:(ONGUserClient *)userClient didFailToRegisterWithError:(NSError *)error identityProvider:(ONGIdentityProvider *)identityProvider
{
// handle authentication error
}
Deregister user
User deregistration implies the removal of all access and refresh tokens from the device for this user account. The iOS SDK will also send a request to the IDAAS-core to revoke all tokens server-side.
The deregisterUser:completion:
method requires two arguments:
- userProfile (
UserProfile
) is the user profile that must be deregistered - completion (
(Error?) -> Void
) is the completion block that notifies the caller about the result of the deregistration action and error (if any). Possible error domains areONGDeregistrationErrorDomain
andONGGenericErrorDomain
.
Example: Call the deregisterUser:completion:
method:
SharedUserClient.instance.deregister(user: profile) { error in
if let error = error {
// error
} else {
// deregistered
}
}
[[ONGUserClient sharedInstance] deregisterUser:user completion:^(BOOL deregistered, NSError * _Nullable error) {
if (deregistered) {
// Code invoked after the user is deregistered..
} else {
// depending on error reason the user has been deregistered locally but the communication with the server failed.
}
}];