User registration
This document explains adding user registration to your application. To initialize authentication, the client credentials are required. These credentials are received via Dynamic Client Registration (DCR). As an app developer, there is no need to initialize DCR as it is part of the default implementations in the SDK.
As a timestamp is used within the DCR protocol, it is mandatory that the time on the device is equal to the time on the Mobile Identity module, independent of time zones. In case of a wrong time, the SDK will return an error.
Start registration
User registration begins with enrolling a user on a device using a selected identity provider (IdP). The list of all possible IdPs for an app is configured in the IDAAS-core admin panel. The IDAAS-core also configures a default (primary) IdP that is used for backward compatibility.
To begin user registration, you have to call the registerUser
method. This method will return a URL that must be opened in a browser where the registration can be continued. Which browser or webview to open for this is up to the developer.
var registrationResponse = await Onegini.instance.userClient
.registerUser(identityProviderId, "read")
.catchError((error) {
print("Registration failed: " + error.message);
});
if (registrationResponse.userProfile.profileId != null) {
print("Registration success!");
}
With the identityProviderId
parameter from registerUser
, you can decide which IdP will be used during the registration process. The available providers on the MIDAAS-core can be obtained through the getIdentityProviders
method. If this parameter isn’t specified, or if its value is null, the default identity provider set on the IDAAS-core will be used.
Depending on the IdP, different registration flows will be followed to register on a device. There are two distinct flows that can be done:
- Registration with browser IdP
- Registration with custom IdP
After completing the flow for browser based or custom registration, you will have to register a PIN in order to complete the registration. Once this has succeeded the registration will be completed.
Registering with browser IdP
After starting registration with a browser-based IdP, the app will receive a URL from the SDK. This URL will have to be opened in a browser, and authentication will have to be completed there to continue the registration. This URL is sent to the application within the HandleRegisteredUrlEvent
event, which has to be listened to as described in event handling.
StreamSubscription<OWEvent> openPinSubscription = owEventController.stream.where((event) => event is HandleRegisteredUrlEvent).listen((event) {
// Your logic to handle the registration url
});
Currently, the SDK can open a browser and return the result of the browser registration in one step by using the following command:
await Onegini.instance.userClient.handleRegisteredUserUrl(url, signInType: WebSignInType.insideApp);
If this succeeds, the overlapping registration flow will continue again with the create PIN event.
Cancel browser registration
To cancel the browser registration flow, use the cancelBrowserRegistration
method:
await OneginiRegistrationCallback()
.cancelBrowserRegistration()
.catchError((error) {
// handle error
});
Registering with custom IdP
When a user tries to register with an IdP that has a custom API type, it has to be enabled and configured on the IDAAS-core side, and implemented in the app itself. Usually when the custom IdP is created on the server side, you will also provide the server-side script for handling the registration process on both client and server side.
The MSP platform expects that the registration process can have either one or two steps.
One-step registration
In case of one-step registration, the SDK asks the app for optional registration data. The data is sent to the IDAAS-core where the custom registration script is executed. The result of the custom script (status code and optional data) is sent back to the SDK and the registration result is propagated to the app in the form of an event.
Two-step registration
In case of two-step registration, the SDK asks the app for optional initial registration data. The data is sent to the IDAAS-core where the custom registration is initialized. The optional initialization result is sent back to the SDK. Then the SDK asks the app for a registration data, providing the optional initialization data provided by the IDAAS-core. The registration data is sent to the IDAAS-core, where the custom registration script is executed. The result of the custom script (status code and optional data) is sent back to the SDK and the registration result is propagated to the app.
Registering the custom IdP
To start registration with a custom IDP, you should call the Onegini.instance.userClient.registerUser
function, similar to registration with a browser IDP, with the custom identity provider ID string.
Handling the custom IDP
The application needs to communicate information to the SDK about the current step. The events that the application needs to listen to are: InitCustomRegistrationEvent
and FinishCustomRegistrationEvent
.
var broadCastController = Onegini.instance.userClient.owEventStreamController;
// Only needed for two-step registration
StreamSubscription<OWEvent> initCustomSub = broadCastController.stream.where((event) => event is InitCustomRegistrationEvent).listen((event) {
// Logic to respond to event
});
StreamSubscription<OWEvent> finishCustomSub = broadCastController.stream.where((event) => event is FinishCustomRegistrationEvent).listen((event) {
// Logic to respond to event
});
CustomRegistrationEvent customInfo Attribute
CustomRegistrationEvent customInfo Attribute
contains a status code and optional data provided by the backend script. The first step of the registration flow will always return an empty string. This means that data will be an empty string in the FinishCustomRegistrationEvent
method of the one-step registration flow, and in the InitCustomRegistrationEvent
method of the two-step registration. The only exception to this rule is a retry of the registration step. In case of a recoverable error returned by the SDK the resulting data object in the retried step will contain the error info. There are three groups of status codes that can be returned in the CustomInfo
object:
- Success status (2XXX): When the status code is in the 2000-2999 range, the registration step has been finished successfully.
- Recoverable errors (4XXX): When the status code is in the 4000-4999 range, the flow can be recovered and the SDK will call either the
InitCustomRegistrationEvent
or theFinishCustomRegistrationEvent
method again, depending on the step that has caused the error. - Unrecoverable errors (5XXX): When the status code is in the 5000-5999 range, the flow cannot be recovered and the SDK will return an error instead of retrying the step.
Responding to the events
Using the information from the data parameter, the user can decide if the data is valid and the registration flow can continue. Once this decision is made and the required data is acquired, we need to send the following callback for the InitCustomRegistrationEvent
or FinishCustomRegistrationEvent
events using one of the callbacks from CustomRegistrationCallback
. In case we would want to accept the request, we can use:
OneginiCustomRegistrationCallback().submitSuccessAction(data: String?)
After this succeeds, the overlapping registration flow will continue by starting the PIN creation flow as described in create PIN events.
To cancel the current custom registration flow, use:
OneginiCustomRegistrationCallback()
.submitErrorAction("Registration canceled": String)
.catchError((error) {
// handle error
})
Creating user PIN
After completing the flow for browser-based or custom registration, you register a PIN to complete the registration. This is communicated by the SDK to the Flutter Plugin through events. The app will have to listen to these events and respond to them accordingly.
For more information on how to handle events and which attribute information the events might contain, see event handling. The events that occur during the creation of the PIN are described below:
Create PIN events
Event | Description |
---|---|
OpenPinCreationEvent | Fired after starting the create PIN flow |
ClosePinCreationEvent | Fired after supplying a valid PIN for the registration and the PIN has been registered. |
PinNotAllowedEvent | Fired when a PIN is given that does not conform to the set PIN policy on the IDAAS-core. |
Create PIN flow
The first event thrown after the registration to set a PIN is the openPinRegistration
event. A user can listen to this event using the following example code:
var owEventController = Onegini.instance.userClient.owEventStreamController;
StreamSubscription<OWEvent> openPinSubscription = owEventController.stream.where((event) => event is OpenPinRegistrationEvent).listen((event) {
// Your logic to handle the open pin registration event
// For example navigate to a new screen where a user can fill in their pin
});
Once a user decides on a PIN, the PIN will then need to be communicated back to the Flutter SDK. This can be done through the following method:
// Check if the pin is valid
await Onegini.instance.userClient
.validatePinWithPolicy(pin)
.then((value) {
// pin is valid
OneginiPinRegistrationCallback()
.acceptAuthenticationRequest(context, pin: pin)
.catchError((error) {
// handle error
});
})
.catchError((error) {
// handle error
});
If an invalid PIN is passed on to the acceptAuthenticationRequest
method, a pinNotAllowedEvent
event is thrown. If successful, a ClosePinRegistrationEvent
event will be thrown and the registerUser
function will resolve with an OWRegistrationResponse
object.
Stateless registration
Stateless registration is a special type of registration that does not work with browser registration and works only with a custom IdP. Stateless registration does not create a user 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 times a user selects the custom registration method.
Stateless registration flow works the same as the other custom registration flows except the following:
- PIN creation is not triggered, which means no PIN for the user session.
- User profile is not stored, which results in limited SDK capabilities, like mobile authentication, implicit authentication, changing PIN is not possible.
- Refresh token is not stored, which means that once expired, it cannot refresh itself anymore and user needs to register again.
To implement stateless registration:
- Enable Stateless authentication in the IDAAS-core admin.
- Go to Configuration → Applications in the IDAAS-core admin. You can create a new application or edit an existing one. Either way, to enable stateless authentication, under User authentication, enable Stateless authentication.
- To use stateless registration, first you need to add a custom identity provider.
- To register a stateless user, you must call the
registerStatelessUser
method.
var registrationResponse = await Onegini.instance.userClient
.registerStatelessUser(identityProviderId, "read")
.catchError((error) {
print("Stateless Registration failed: " + error.message);
});
if (registrationResponse.userProfile.profileId == "stateless") {
print("Stateless Registration success!");
}
Stateless registration does not create a profile ID. The API returns stateless
as the profileId
, to indicate as stateless flow. It should not be used in other APIs.
If stateless authentication is not enabled in the IDAAS-core admin, an error will be thrown while calling registerStatelessUser
.
Deregister a user
Deregistering a user implies the removal of all of their data (including access and refresh tokens) from the device. It also includes a request to the IDAAS-core to revoke all tokens associated with the user. The client credentials will remain stored on the device.
The Flutter Plugin exposes the Onegini.instance.userClient.deregisterUser()
function to properly deregister the currently authenticated user.
Example code to deregister a user
await Onegini.instance.userClient
.deregisterUser()
.catchError((error) {
print("Deregistration failed: " + error.message);
});
Note that any existing user can be deregistered. They do not necessarily have to be logged in.