The registration is the process of creating an account for a user at the XignIn-Manager. The SDK supports a variety of
server configurations with respect to the required information. Currently, the minimum set of valid information consists
of an email address and an activation PIN. The registration process is managed by the Registry component of
the XignSdk.
Important: Not every XignIn-Manager supports the registration feature of the XignSys SDK. There are cases where the registration is managed by a third party application. For example, in the case of the Servicekonto.NRW setup, the registration is done by the Servicekonto.NRW platform. In this case a registration via the SDK is not possible.
The following diagram shows the flow of a basic registration process within the XignSys SDK.
The registration starts with fetching the configuration from the XignIn-Manager by calling the function
Registry.initializationSynchronous(url:). This function requires the URL of the manager to register at and an
optional serviceMappingId as String. The service mapping id is used to create an account at a third party service
after the first activation/personalization. The result of the initialization is a RegistrationParameters object which
contains the required parameters and must be filled with the demanded data. It contains a PinSpecification as well as
a set of IdentificationCategory. The PinSpecification container handles the activation PIN. It carries the minimal
and maximal length of the PIN and offers a function to supply it. The following example shows the initialization of the
registration and the setting of the activation PIN.
Since version 13.0.0 of the XignIn-Manager it is possible to configure a linked third party service within the configuration of a XignIn-Manager as described in the chapter [TODO: XignIn-Manager chapter link]. If specified this creates an account for the user at the linked service after the first activation/personalization.
private func startRegistration() throws {
// get the registration component of the XignSys SDK
let registry: Registry = XignSdk.shared.registry
// request the registration parameters from the server to register against
let parameters: RegistrationParameters = try registry.initializationSynchronous(
url: URL(string: "https://xign.me/")!,
// an optional service mapping id, if the registration on the XignIn-Manager should also
// create an account at a third party service, e.g. like a Keycloak
// Note: This id is only for example purposes and does not exist on any XignIn-Manager!
serviceMappingId: "a4212c28-7fb0-5ec9-8cf2-5ecda1f81329"
)
// retrieve the pin specification
let pinSpec: PinSpecification = parameters.pinSpec
var isPinValid: Bool = false
var errorText: String? = nil
while !isPinValid {
// TODO query the user for the pin and optionally show previous errors
let pin: String = YourImplementation.askUserForPin(
minLength: pinSpec.minLength,
maxLength: pinSpec.maxLength,
errorText: errorText
)
let pinError: PinValidator.ResultCode = pinSpec.setPin(pin)
switch pinError {
case .valid:
// pin is ok, continue
isPinValid = true
case .empty:
errorText = "PIN must not be empty"
case .onlyNumbers:
errorText = "Only numbers are allowed"
case .tooShort:
errorText = "PIN must be at least \(pinSpec.minLength) digits long"
case .tooLong:
errorText = "PIN can't be longer than \(pinSpec.maxLength) digits"
}
}
// continue the registration
try registrationSelectCategory(parameters: parameters)
}
private fun startRegistration(xignSdk: XignSdk) {
// get the registration component of the XignSys SDK
val registry: Registry = xignSdk.registry
// request the registration parameters from the server to register against
val parameters: RegistrationParameters = registry.initializationSynchronous(
url = URL("https://xign.me/"),
// an optional service mapping id, if the registration on the XignIn-Manager should also
// create an account at a third party service, e.g. like a Keycloak
// Note: This id is only for example purposes and does not exist on any XignIn-Manager!
serviceMappingId = "a4212c28-7fb0-5ec9-8cf2-5ecda1f81329"
)
// retrieve the pin spec
val pinSpec: PinSpecification = parameters.pinSpec
var isPinValid: Boolean = false
var errorText: String? = null
while (!isPinValid) {
// TODO query the user for the pin and optionally display previous errors
val pin: String = YourImplementation.askUserForPin(
minLength = pinSpec.minLength,
maxLength = pinSpec.maxLength,
errorText = errorText
)
val pinError: PinValidator.ErrorCode = pinSpec.setPin(pin)
when (pinError) {
PinValidator.ErrorCode.VALID -> {
// pin is ok, continue
isPinValid = true
}
PinValidator.ErrorCode.EMPTY -> {
errorText = "PIN must not be empty"
}
PinValidator.ErrorCode.ONLY_NUMBERS -> {
errorText = "Only numbers are allowed"
}
PinValidator.ErrorCode.TOO_SHORT -> {
errorText = "PIN must be at least ${pinSpec.minLength} digits long"
}
PinValidator.ErrorCode.TOO_LONG -> {
errorText = "PIN can't be longer than ${pinSpec.maxLength} digits"
}
}
}
// continue the registration
registrationSelectCategory(parameters)
}
After supplying the activation PIN the user data must be set as well. In order to do so, an identification category must
be selected. RegistrationParameters.categories returns a set of available categories. Every category contains the
following:
types: Defines the type of identification that must be passed. This is a set which can contain at least one of the
values anonymous, email, phone or idCard. For example, if the set only contains the value email, only the
email address must be confirmed for that particular identification category.info: An object of the type RegistrationPersonalInformation which must be supplied with the user data needed for
the registration.identificationAttributes: A set of user personal attributes that must be confirmed for the identification process.
For example, if the types set consists only of the type idCard the identificationAttributes will contain the
attributes firstName, lastName, placeOfBirth and birthdate.Most commonly the XignIn-Manager is configured to only allow registrations via email. If that is the case,
the RegistrationParameters.categories contain one category only. To set a category as selected the
function RegistrationParameters.setSelection() must be called with that particular category.
The RegistrationPersonalInformation must be supplied with the user's personal information to the respective
attributes. The info object contains a RegistrationInformation object representing each attribute. To check whether
the attribute is required or not the property RegistrationInformation.isRequired can be used. If an attribute is
marked as required it must be set, otherwise the registration fails. If it is not required, it can still be specified
optionally. Which attributes are required depends on the configuration of the XignIn-Manager. To set the value of an
attribute the function RegistrationInformation.setValue(value:) must be invoked. The result of the function is a
status code that informs whether the value is accepted or not. If the code valid is returned the value is accepted, in
the other cases there is an error, and the value has been reset. If that happens the error needs to be corrected and the
value has to be set again. This process must be done for every RegistrationInformation within
the RegistrationPersonalInformation object.
The following example illustrates the selection of a category and supplying of the email attributes value where the XignIn-Manager is configured to only require an email address for a registration.
private func registrationSelectCategory(parameters: RegistrationParameters) throws {
// select an available category to register with. Currently, the
// XignIn-Manager will only supply one, so the first can be picked.
let firstCategory: IdentificationCategory = parameters.categories.first!
// set the picked category as selected
_ = parameters.setSelection(firstCategory)
// retrieve the info object that collects the user attributes
let info: RegistrationPersonalInformation = firstCategory.info
// check if the email address is required for the registration
// Note: this can be done for every attribute, depending on the
// XignIn-Managers configuration other attributes can also be mandatory
if info.email.isRequired {
var errorText: String? = nil
var isEmailValid: Bool = false
while !isEmailValid {
// query the users email address
let email: String = YourImplementation.askUserForEmail(errorText: errorText)
// set the users email address and handle possible errors
let emailError: EmailValidator.ResultCode = info.email.setValue(value: email)
switch emailError {
case .valid:
// email is ok, continue
isEmailValid = true
case .empty:
errorText = "Email is empty"
case .invalid:
errorText = "Input is not a valid email format"
}
}
}
// either supply additional optional data, like the activation alias or Keycloak user attributes
// continuing with the example function:
try registrationSupplyOptionalData(parameters: parameters)
// or finish the registration directly with:
try finishRegistration(parameters: parameters)
}
private fun registrationSelectCategory(parameters: RegistrationParameters) {
// select an available category to register with. Currently, the
// XignIn-Manager will only supply one, so the first can be picked.
val firstCategory: IdentificationCategory = parameters.categories.first()
// set the picked category as selected
parameters.setSelection(firstCategory)
// retrieve the info object that collects the user attributes
val info: RegistrationPersonalInformation = firstCategory.info
// check if the email address is required for the registration
// Note: this can be done for every attribute, depending on the
// XignIn-Managers configuration other attributes can also be mandatory
if (info.email.isRequired) {
var errorText: String? = null
var isEmailValid: Boolean = false
while (!isEmailValid) {
// query the users email address
val email: String = YourImplementation.askUserForEmail(errorText)
// set the users email address and handle possible errors
val emailError: EmailValidator.ErrorCode = info.email.setValue(email)
when (emailError) {
EmailValidator.ErrorCode.VALID -> {
// email is ok, continue
isEmailValid = true
}
EmailValidator.ErrorCode.EMPTY -> {
errorText = "Email is empty"
}
EmailValidator.ErrorCode.INVALID -> {
errorText = "Input is not a valid email format"
}
}
}
}
// either supply additional optional data, like the activation alias or Keycloak user attributes
// continuing with the example function:
registrationSupplyOptionalData(parameters)
// or finish the registration directly with:
finishRegistration(parameters)
}
After all demanded data is set (the activation PIN within the PinSpecification, the category within the
RegistrationParameters and all required attribute values within the RegistrationPersonalInformation object) the
registration process can either be continued by supplying additional optional data or finished directly.
Optional data are the alias for the first activation or additional arbitrary user attributes for a linked third party service like a Keycloak. In case no additional data should be supply, the next chapter can be skipped and the registration be finished as described in Finalization.
Every activation of the XignIn technology features an alias, a name that can be freely chosen by the user to identify
the activation. Besides a character limit, this alias is not restricted by any other means, i.e. it must not be unique.
It can be supplied by invoking the function setAlias() of the instance of the RegistrationParameters. The validation
of the alias is similar to the validation of email attribute mention in the previous
chapter Category Selection. The validator of the alias can also be directly accessed from the
instance property validatorAlias of the RegistrationParameters. If no custom alias is supplied during the
registration process, the XignIn-Manager will generate a generic one, for example like "Activation 1". The activation
alias is shown in the registration email or displayed on the XignIn-Fronted. It can also be accessed
from the instance property alias of an IdentityManagerEntity object after the personalization process. This object
can be fetched as described in the
chapter List personalized identity managers.
Besides the activation alias, there is also an option to supply additional arbitrary user attributes for a linked third
party service like a Keycloak. Prerequisite for this option is the assignment of a valid serviceMappingId to the
Registry.initializationSynchronous(url:) function. If provided, the function setArbitraryUserAttributes() of
the RegistrationParameters can be used to supply these data. The additional arbitrary user attributes are only stored
temporary within the XignIn-Manager and will be automatically supplied to the linked third party service after the first
personalization/activation of the newly created registration. Neither the XignSys SDK nor the XignIn-Manager validates
these data. It is the responsibility of the caller to provide only data that can be accepted by the linked service. If
the data are rejected by the linked service during the personalization/activation process, they are lost, since there is
currently no procedure to repeat this step and correct the data. Regardless of whether the data are accepted by the
linked service or not, they will be deleted from the temporary memory of the XignIn-Manager after the first successful
personalization/activation. Furthermore, if no third party services are linked and additional arbitrary user attributes
are supplied during the registration, these data will be silently discarded by the XignIn-Manager directly.
The following example shows how an alias and additional arbitrary user attributes could be supplied during the registration process:
private func registrationSupplyOptionalData(parameters: RegistrationParameters) throws {
// optionally ask the user for the alias of the first activation
var isActivationAliasValid = false
var errorText: String? = nil
while !isActivationAliasValid {
// query the users for an alias
let alias: String = YourImplementation.askUserForActivationAlias(errorText: errorText)
// set the alias for the first activation and handle possible errors
let aliasError: AliasValidator.ResultCode = parameters.setAlias(alias)
switch aliasError {
case .valid:
// alias is ok, continue
isActivationAliasValid = true
case .empty:
errorText = "The alias must not be empty."
case .tooLong:
errorText = "The alias must be at least \(parameters.validatorAlias.minLength) characters long."
case .tooShort:
errorText = "The alias cannot be longer than \(parameters.validatorAlias.maxLength) characters long."
}
}
// optionally supply additional user attributes for a links third party service, e.g. a Keycloak
// Important: Neither the XignSys SDK nor the XignIn-Manager validates these data. You must make
// sure to only supply data that can be accepted by the linked service.
// gather these data by any means of your choosing
let arbitraryUserAttributes: Dictionary<String, String> = [
"your_first_key": "some data",
"your_second_key": "some data"
]
// these data can than be supply by invoking:
parameters.setArbitraryUserAttributes(arbitraryUserAttributes)
// finish the registration
try finishRegistration(parameters: parameters)
}
private fun registrationSupplyOptionalData(parameters: RegistrationParameters) {
// optionally ask the user for the alias of the first activation
var isActivationAliasValid = false
var errorText: String? = null
while (!isActivationAliasValid) {
// query the users for an alias
val alias: String = YourImplementation.askUserForActivationAlias(errorText)
// set the alias for the first activation and handle possible errors
val aliasError: AliasValidator.ErrorCode = parameters.setAlias(alias)
when (aliasError) {
AliasValidator.ErrorCode.VALID -> {
// alias is ok, continue
isActivationAliasValid = true
}
AliasValidator.ErrorCode.EMPTY -> {
errorText = "The alias must not be empty."
}
AliasValidator.ErrorCode.TOO_SHORT -> {
errorText = "The alias cannot be longer than (parameters.validatorAlias.maxLength) characters long."
}
AliasValidator.ErrorCode.TOO_LONG -> {
errorText = "The alias must be at least (parameters.validatorAlias.minLength) characters long."
}
}
}
// optionally supply additional user attributes for a links third party service, e.g. a Keycloak
// Important: Neither the XignSys SDK nor the XignIn-Manager validates these data. You must make
// sure to only supply data that can be accepted by the linked service.
// gather these data by any means of your choosing
val arbitraryUserAttributes = mapOf(
"your_first_key" to "some data",
"your_second_key" to "some data"
)
// these data can than be supply by invoking:
parameters.setArbitraryUserAttributes(arbitraryUserAttributes)
// finish the registration
finishRegistration(parameters)
}
After all demanded data (activation PIN, requested user attributes) and optional values haven been set, the registration
process can be completed by invoking Registry.registerSynchronous(data:) as the following example illustrates:
private func finishRegistration(parameters: RegistrationParameters) throws {
// get the registration component of the XignSys SDK
let registry: Registry = XignSdk.shared.registry
// finish the registration with the filled parameter object
let result: RegistrationResponse = try registry.registerSynchronous(data: parameters)
switch result {
case .success(managerUrl: _):
// done
return
case .failure(let returnedParameter):
// TODO check the `returnedParameter` object for errors, correct them
// and repeat the process, for example:
switch returnedParameter.selection!.info.email.error {
case .none:
// no error
return
case .invalid:
// illegal format etc.
return
case .notAvailable:
// email is already in use or blocked etc.
return
case .required:
// email was not supplied
return
}
}
}
private fun finishRegistration(parameters: RegistrationParameters) {
// get the registration component of the XignSys SDK
val registry: Registry = XignSdk.shared.registry
// finish the registration with the filled parameter object
val result: RegistrationResponse = registry.registerSynchronous(parameters)
when (result) {
// registration was successful, now checking if any further steps are necessary
is RegistrationResponseSuccessDone -> {
result.managerUrl
return
}
is RegistrationResponseFailure -> {
// TODO check the `returnedParameter` object for errors, correct them
// and repeat the process, for example:
when (result.parameters.selection!!.info.email.error) {
RegistrationInformation.ErrorCode.NONE -> {
// no error
return
}
RegistrationInformation.ErrorCode.INVALID -> {
// illegal format etc.
return
}
RegistrationInformation.ErrorCode.NOT_AVAILABLE -> {
// email is already in use or blocked etc.
return
}
RegistrationInformation.ErrorCode.REQUIRED -> {
// email was not supplied
return
}
}
}
}
}
The result of the function Registry.registerSynchronous() is an enum (a sealed class in Kotlin) with two
cases success and failure.
The success case returns an additional result, a RegistrationResponseSuccess. This result is also an
enum (a sealed class in Kotlin) that determines which further steps are necessary. Currently, there is only one
case done. It signalizes the end of registration process, no further steps a needed. This case returns a struct of the
type RegistrationResponseSuccessDone (in Kotlin the sealed class is of this type) which contains a publicly accessible
property managerUrl, the URL of the XignIn-Manager the registration was performed at. Note that,
the RegistrationResponseSuccessDone contains additional information, but they should not be used outside the scope of
the XignSys SDK. With that done an activation can be started, please refer to the
chapter Personalization for that matter.
In the failure case the previously supplied RegistrationParameters object with updated error states is returned.
Every RegistrationInformation has a property named error of the type RegistrationInformationErrorCode. Those codes
describe the server error state of the attribute.
For example, if the email attribute error property, which can be queried by calling
RegistrationParameters.selection.info.email.error, has the value notAvailable it means that the server rejected the
email address because it is already in use by another account, or it is on a blacklist. If the registration should be
revised, the error states of all RegistrationInformation must be checked and corrected by supplying a new value. After
that the registration can be retried by invoking Registry.registerSynchronous(data:) with the updated info object.
Important: If the XignIn-Manager is set up with a Keycloak server as identity provider, the manager should only require the email address for a registration. All other user attributes data should be stored within the user's Keycloak account. This account is created after the user authenticates against the Keycloak service for the first time.