After all requested attributes have been confirmed as discussed in the previous chapter the signing step of the
authentication process can be commenced with an instance of AuthenticationUserInformation. During the signing the
identity of the user is determined by signing all requested attributes with the requested authenticators. Like the
attributes the authenticators requested can be configured within the service on the XignIn-Manager. Depending on the
configuration different authenticator combinations must be handled. For that purpose the AuthenticationUserInformation
contains a property named requestedAuthenticators which is a two-dimensional list of the enum AuthenticatorType. The
inner lists (the second dimension) represents mandatory combinations of factor types to be used in conjunction, the
outer list (first dimension) stands for alternatives of these conjunctions. For example, given the
list [[.pin, .device], [.biometric, .device]] the first inner list of [.pin, .device] requires the user to provide
both the PIN and the device factor, if this entry is chosen. On the other hand, the second inner list
of [.biometric, .device] offers the user to alternatively authenticate by providing both the biometric and the device
factor. In contrast, providing only a PIN factor or for example a combination of a PIN factor and a biometric factor
will fail to meet the specified requirements and therefore, the user can not authenticate successfully. Furthermore, the
list of requestedAuthenticators will only contain combinations the client should be able to supply. For example, if a
service is configured to allow biometric authentication but the app did not enroll biometrics within the XignSys SDK,
the requestedAuthenticators will not contain an entry with biometrics. However, if the app does not match any possible
combination, a list containing all configured combinations is returned, so the app can notify the user about which
factors have to be enrolled to authenticate against the service.
To sign the AuthenticationUserInformation a list from the requestedAuthenticators must be chosen from which every
authenticator must be supplied. Authenticators are supplied in form of an instance of AuthenticationFactor. With such
an instance the function sign(factor:) of the AuthenticationFactor can be invoked. Because authenticators may need
different data a more specific implementation exists for every authenticator which class name matches the case name of
the AuthenticatorType.
Currently, only three authenticators are fully implemented and can occur. The more specific AuthenticationFactor
classes are: DeviceFactor, PinFactor and BiometricFactor. A DeviceFactor can simply be created by invoking its
initializer (constructor). For the PinFactor the users activation PIN is required, with which the initializer (
constructor) of the object can be invoked. The creation of a BiometricFactor
needs a bit more handling, because the platforms biometric API must be used to locally authenticate the user in order to
create the factor. As this process is the same as for personalisation, it is discussed in detail in
chapter Creating a BiometricFactor. Note that, the creation of a BiometricFactor can
fail if the system managed biometric patterns (e.g. saved fingerprints) have been modified after the enrollment within
the XignSys SDK. In this case another combination could be supplied from requestedAuthenticators that does not require
biometrics. If that is not possible the authentication end at this point, because the required authenticators can not be
supplied.
Note: Prior to SDK version 4.0.0 the signing with a
PinFactorthrew an error, if the wrong PIN was supplied. This is not the case anymore. As for newer versions the PIN is only validated on the XignIn-Manager and produces a lockout, if the wrong PIN is supplied.
After the AuthenticationUserInformation object has been signed with all requested authenticators the authentication
can be finished. The following code shows an example on how the signing can be implemented:
private func signAuthentication(userInformation: AuthenticationUserInformation) throws {
// Retrieve the list of authenticators that must be supplied.
let requestedAuthenticators: Array<Array<AuthenticatorType>> =
userInformation.requestedAuthenticators
// Select a set of authenticators to provide, either automatically or let the user decide.
let authenticators: Array<AuthenticatorType> = requestedAuthenticators.first!
// Iterate over the selected authenticators and sign the userInformation respectively.
for authenticator in authenticators {
let factor: AuthenticationFactor
switch authenticator {
case .device:
// Just create a new instance of the `DeviceFactor` class.
factor = DeviceFactor()
case .pin:
// Query the user for the PIN.
let pin: String = YourImplementation.askUserForPin()
factor = PinFactor(pin)
case .biometric:
// Query the user for biometrics and create a `BiometricFactor`. For overview
// purposes this process is substituted by another function call.
let biometricFactor: BiometricFactor = try YourImplementation.getBiometricFactor()
factor = biometricFactor
case .otp, .yubikey:
// Handling for factors that are currently in development and not fully functional
throw YourError.yourErrorCase
}
// Sign the `userInformation` object with the factor.
try userInformation.sign(factor: factor)
}
// Continue the process with the signed userInformation object.
// Note: This is a call to the next documentation example function that illustrates the
// final step of the authentication process.
try finishAuthentication(userInformation: userInformation)
}
private fun signAuthentication(userInformation: AuthenticationUserInformation) {
// Retrieve the list of authenticators that must be supplied.
val requestedAuthenticators: List<List<AuthenticatorType>> =
userInformation.requestedAuthenticators
// Select a set of authenticators to provide, either automatically or let the user decide.
val authenticators: List<AuthenticatorType> = requestedAuthenticators.first()
// Iterate over the selected authenticators and sign the userInformation respectively.
authenticators.forEach { authenticator ->
val factor: AuthenticationFactor
when (authenticator) {
AuthenticatorType.DEVICE -> {
// Just create a new instance of the `DeviceFactor` class.
factor = DeviceFactor()
}
AuthenticatorType.PIN -> {
// Query the user for the PIN.
val pin: String = YourImplementation.askUserForPin()
factor = PinFactor(pin)
}
AuthenticatorType.BIOMETRIC -> {
// Query the user for his biometrics and create a `BiometricFactor`. For overview
// purposes this process is substituted by another function call.
val biometricFactor: BiometricFactor = YourImplementation.getBiometricFactor()
factor = biometricFactor
}
AuthenticatorType.OTP,
AuthenticatorType.YUBIKEY -> {
// Handling for factors that are currently in development and not fully functional
throw YourException()
}
}
// Sign the userInformation object with the factor.
userInformation.sign(factor)
}
// Continue the process with the signed userInformation object.
// Note: This is a call to the next documentation example function that illustrates the
// final step of the authentication process.
finishAuthentication(userInformation = userInformation)
}
After the UserInformation have been signed by all requested authenticators the authentication process can be finished
as described in the chapter: