# 2️⃣ Enrollment

Enrollment is the process of registering a new user by connecting their facial biometrics to a Keyless account. During this process, a full and unobstructed view of the user's face is required.

{% tabs %}
{% tab title="Android" %}

```kotlin
val configuration = BiomEnrollConfig()

Keyless.enroll(
  configuration = configuration,
  onCompletion = { result ->
    when (result) {
      is Keyless.KeylessResult.Success -> Log.d("KeylessSDK ", "Enroll success - userId ${result.value.keylessId}")
      is Keyless.KeylessResult.Failure -> Log.d("KeylessSDK ", "Enroll failure - error code ${result.error.code}")
    }
  }
)
```

{% endtab %}

{% tab title="iOS" %}

```swift
let configuration = BiomEnrollConfig()

Keyless.enroll(
  configuration: configuration,
  onCompletion: { result in
    switch result {
    case .success(let enrollmentSuccess):
        print("Enrollment finished successfully. UserID: \(enrollmentSuccess.keylessId)")
    case .failure(let error):
        print("Enrollment finished with error: \(error.message)
    }
  })
```

{% endtab %}

{% tab title="Android 4.6" %}

```kotlin
val configuration = EnrollmentConfiguration.builder.build()

Keyless.enroll(
  enrollmentConfiguration = configuration,
  onCompletion = { result ->
    when (result) {
      is Keyless.KeylessResult.Success -> Log.d("KeylessSDK ", "Enroll success - userId ${result.value.keylessId}")
      is Keyless.KeylessResult.Failure -> Log.d("KeylessSDK ", "Enroll failure - error code ${result.error.code}")
    }
  }
)
```

{% endtab %}

{% tab title="iOS 4.6" %}

```swift
let configuration = Keyless.EnrollmentConfiguration.builder.build()

Keyless.enroll(
  enrollmentConfiguration: configuration,
  onCompletion: { result in
    switch result {
    case .success(let enrollmentSuccess):
        print("Enrollment finished successfully. UserID: \(enrollmentSuccess.keylessId)")
    case .failure(let error):
        print("Enrollment finished with error: \(error.message)
    }
  })
```

{% endtab %}

{% tab title="Flutter" %}

```dart
import 'package:keyless_flutter_sdk/keyless.dart';
import 'package:keyless_flutter_sdk/models/configurations/enrollment_configuration.dart';

final configuration = BiomEnrollConfig();

try {
  final result = await Keyless.instance.enroll(configuration);
  print("Enrollment finished successfully. UserID: ${result.keylessId}");
} catch (error) {
  print("Enrollment finished with error: $error");
}
```

{% endtab %}

{% tab title="React Native" %}

```javascript

import Keyless, { BiomEnrollConfig } from '@react-native-keyless/sdk';

const enrollUser = async () => {
  const enroll = new BiomEnrollConfig();

  const result = await Keyless.enroll(enroll);

  result.fold({
    onSuccess: (data) => {
      console.log(`Enrollment finished successfully. UserID: ${data.keylessId}`);
    },
    onFailure: (error) => {
      console.error('Enrollment finished with error:', error);
    },
  });
};
```

enrollUser();
{% endtab %}
{% endtabs %}

## Enrollment configuration

You can configure the enrollment process with optional parameters in your `BiomEnrollConfig()` instance or using the builder pattern methods from the `EnrollmentConfiguration` builder.

{% tabs %}
{% tab title="Android" %}

```kotlin
public data class BiomEnrollConfig(
    public val cameraDelaySeconds: Int = 2,
    public val jwtSigningInfo: JwtSigningInfo?,
    public val livenessConfiguration: LivenessSettings.LivenessConfiguration = LEVEL_1,
    public val livenessEnvironmentAware: Boolean = true,
    public val operationInfo: OperationInfo?,
    public val shouldRetrieveEnrollmentFrame: Boolean = false,
    public val showSuccessFeedback: Boolean = true,
    public val showInstructionsScreen: Boolean = true,
    public val showFailureFeedback: Boolean = true,
    public val generatingClientState: ClientStateType? = null,
    public val clientState: String? = null,
    public val savingSecret: KeylessSecret? = null,
    public val deletingSecret: KeylessSecret.Identifier? = null,
    public val retrievingSecret: KeylessSecret.Identifier? = null,
    public val shouldRetrieveSecretIDs: Boolean = false,
    public val presentation: PresentationStyle = PresentationStyle.FULL_SCREEN
)
```

{% endtab %}

{% tab title="iOS" %}

```swift
public struct BiomEnrollConfig {
    public let cameraDelaySeconds: Int
    public let customSecret: String?
    public let jwtSigningInfo: JwtSigningInfo?
    public let livenessConfiguration: Keyless.LivenessConfiguration
    public let livenessEnvironmentAware: Bool
    public let operationInfo: Keyless.OperationInfo?
    public let shouldReturnEnrollmentFrame: Bool
    public let showSuccessFeedback: Bool,
    public let showInstructionsScreen: Bool,
    public let showFailureFeedback: Bool,
    public let generatingClientState: ClientStateType?,
    public let clientState: String?,
    public let deletingSecret: KeylessSecret.Identifier?
    public let retrievingSecret: KeylessSecret.Identifier?
    public let shouldRetrieveSecretIDs: Bool
    public let presentationStyle: PresentationStyle
}
```

{% endtab %}

{% tab title="Android 4.6" %}

```kotlin
public interface EnrollmentConfigurationBuilder {

    public fun retrievingBackup(): EnrollmentConfigurationBuilder

    public fun retrievingTemporaryState(): EnrollmentConfigurationBuilder

    public fun savingSecret(customSecret: String): EnrollmentConfigurationBuilder

    public fun withBackup(backupKey: ByteArray, backupData: ByteArray): EnrollmentConfigurationBuilder

    public fun withDelay(cameraDelaySeconds: Int): EnrollmentConfigurationBuilder

    public fun withEnrollmentSelfie(): EnrollmentConfigurationBuilder

    public fun withLivenessSettings(
        livenessConfiguration: LivenessSettings.LivenessConfiguration,
        livenessTimeout: Int
    ): EnrollmentConfigurationBuilder

    public fun withOperationInfo(
        operationId: String,
        payload: String? = null,
        externalUserId: String? = null
    ): EnrollmentConfigurationBuilder

    public fun withPin(pin: String): EnrollmentConfigurationBuilder

    public fun withTemporaryState(temporaryState: String): EnrollmentConfigurationBuilder

    public fun build(): EnrollmentConfiguration
}
```

{% endtab %}

{% tab title="iOS 4.6" %}

```swift
public protocol EnrollmentConfigurationBuilder {

    public func retrievingBackup() -> EnrollmentConfigurationBuilder

    public func retrievingTemporaryState() -> EnrollmentConfigurationBuilder

    public func savingSecret(_ customSecret: String) -> EnrollmentConfigurationBuilder

    public func withBackup(_ backup: Keyless.Backup) -> EnrollmentConfigurationBuilder

    public func withDelay(seconds: Int) -> EnrollmentConfigurationBuilder

    public func withEnrollmentSelfie() -> EnrollmentConfigurationBuilder

    public func withLivenessSettings(
        livenessConfiguration: Keyless.LivenessConfiguration,
        livenessTimeout: Int
    ) -> EnrollmentConfigurationBuilder

    public func withOperationInfo(
        id: String,
        payload: String?,
        externalUserId: String?
    ) -> EnrollmentConfigurationBuilder

    public func withPin(_ pin: String) -> EnrollmentConfigurationBuilder

    public func withTemporaryState(_ temporaryState: String) -> EnrollmentConfigurationBuilder

    public func build() -> Keyless.EnrollmentConfiguration
}
```

{% endtab %}

{% tab title="Flutter" %}

```dart
class BiomEnrollConfig {
    final String? customSecret;
    final String? temporaryState;
    final OperationInfo? operationInfo;
    final LivenessConfiguration? livenessConfiguration;
    final int? livenessTimeout;
    final bool? shouldRetrieveTemporaryState;
    final int? cameraDelaySeconds;
    final JwtSigningInfo? jwtSigningInfo;
    final DynamicLinkingInfo? dynamicLinkingInfo;
    final bool? showScreenInstructions;
    final bool? showScreenSuccessFlow;
}
```

{% endtab %}

{% tab title="React Native" %}

```typescript

// The BiomEnrollConfig can be instantiated with these optional properties.
// example: new BiomEnrollConfig({ customSecret: 'your-secret', cameraDelaySeconds: 3 });
 class BiomEnrollConfig {
  public readonly livenessConfiguration: LivenessConfiguration;
  public readonly livenessEnvironmentAware: boolean;
  public readonly cameraDelaySeconds: number;
  public readonly generatingClientState: ClientStateType | null;
  public readonly shouldRetrieveEnrollmentFrame: boolean;
  public readonly clientState: string | null;
  public readonly customSecret: string | null;
  public readonly iamToken: string | null;
  public readonly operationInfo: OperationInfo | null;
  public readonly jwtSigningInfo: JwtSigningInfo | null;
  public readonly showSuccessFeedback: boolean;
  public readonly showFailureFeedback: boolean;
  public readonly showScreenInstructions: boolean;
 }

```

{% endtab %}
{% endtabs %}

### Enrollment success result

Depending on the configuration you created for the Enrollment, Keyless will populate the corresponding fields in the `EnrollmentSuccess` result reported below.

{% tabs %}
{% tab title="Android" %}

```kotlin
data class EnrollmentSuccess(  
    val keylessId: String,
    val enrollmentFrame: Bitmap?,
    val signedJwt: String?,
    val clientState: String?,
    val secret: KeylessSecret?,
    val secretIDs: Set<KeylessSecret.Identifier>?
) : KeylessSdkSuccess()
```

{% endtab %}

{% tab title="iOS" %}

```swift
public struct EnrollmentSuccess {
    public let keylessId: String?
    public let enrollmentFrame: CGImage?
    public let signedJwt: String?
    public let clientState: String?
    public let secret: KeylessSecret?
    public let secretIDs: Set<KeylessSecret.Identifier>?
}
```

{% endtab %}

{% tab title="Flutter" %}

```dart
class EnrollmentSuccess {
    final String keylessId;
    final String? customSecret;
    final String? signedJwt;
    final String? clientState;
}
```

{% endtab %}

{% tab title="React Native" %}

```typescript

class EnrollmentSuccess {
  public readonly keylessId: string;
  public readonly customSecret: string;
  public readonly enrollmentFrame: string | null;
  public readonly signedJwt: string | null;
  public readonly clientState: string | null;
  }
  
```

{% endtab %}
{% endtabs %}

### Delaying the Keyless evaluation/decision

By default, our biometric decision making process initiates immediately on the camera being invoked and the camera preview showing. We believe this offers the maximum level of security in that an attacker is given no additional preparation time in front of the camera view, while at the same time we have looked to optimise the experience for genuine users i.e. delivering both approve and reject decisions in a way that feels natural and understandable to users.\
\
However, we recognise that our customers, and their users, have different contexts and preferences and therefore the `cameraDelaySeconds` configuration is available to specify the delay (in seconds) between when the camera preview appears, and when the liveness processing starts. In effect, no decision, whether \\

{% hint style="danger" %}
Please note we advise careful consideration when implementing this feature for two reasons:\
\
i) While this allows users to frame themselves and have longer to understand what is happening, is also time for any attackers to also optimise their framing.\
ii) Implementing will ultimately mean that the "happy path" flow for all users is extended. If the delay is set for too long, some customers have noted that there is also the potential for some users to become frustrated and cancel/drop the flow.\
\
We're happy to engage further in what the best trade-offs may be for customers, given our wide-ranging experience of assisting customers in live implementations.
{% endhint %}

### Secret management

During enrollment you can **create**, **update**, **retrieve**, and **delete** secrets, as well as list all stored secret IDs. Use `savingSecret`, `retrievingSecret`, `deletingSecret`, and `shouldRetrieveSecretIDs` configuration parameters. Retrieved secrets and secret IDs are available in the `EnrollmentSuccess` response. See [Secret Management](#secret-management) for full details.

### JWT Signing info

You can specify a payload to be added to a JWT signed by Keyless with the `jwtSigningInfo` parameter, more in [JWT signing](https://docs.keyless.io/consumer/mobile-sdk-reference/jwt-signing).

### Liveness Settings

Using `livenessConfiguration` you can configure the liveness security level during enrollment. The possible liveness configuration are under `LivenessSettings.LivenessConfiguration` :

```
DEVELOPMENT
LEVEL_1        //recommended configuration
LEVEL_2
```

You can also specify a `livenessEnvironmentAware` that is by default se to `true` to enhance liveness detection. This parameters helps to ensure the user is in a suitable setting for verification.

More details on liveness in the dedicated [liveness settings](https://docs.keyless.io/consumer/mobile-sdk-reference/liveness-settings) section.

### Operation info

The parameter `operationInfo` specifies a customizable *unique* operation identifier and associated payload stored on the Keyless backend if the enrollment succeeds. Use this to add an extra level of confirmation in your operations.

Details on how to query our backend for stored operations are available on [Operations API](https://docs.keyless.io/consumer/server-api/operations).

### Client State

Keyless users can be enrolled via [IDV-Bridge](https://docs.keyless.io/idv-bridge), Identity Verification Bridge. As a result of IDV-Bridge enrollment you receive a client state useful to register users in your app without undergoing the full enrollment flow.

Use the `clientState` parameter to register users from a client state obtained through IDV-Bridge.

You can also use a client state to recover an account of an existing user who lost access to the account. Follow the guide on [account recovery](https://docs.keyless.io/consumer/mobile-sdk-use-cases/guide-account-recovery).

### Enrollment Frame

Integrators can specify whether or not to retrieve an enrollment frame, acquired during the user’s selfie capture during the enrollment or account recovery flow. You can achieve this by setting `shouldReturnEnrollmentFrame` to `true` on the `BiomEnrollmentConfig` (defaults to `false`). If the enrollment succeeds, you can retrieve the frame from the returned `EnrollmentSuccess`, via `enrollmentFrame`.

### Enrollment circuits

To speed up enrollment you can use the `numberOfEnrollmentCircuits` to upload less than the default of five circuits upon enrollment. The remainder (`50 - numberOfEnrollmentCircuits`) are uploaded asynchronously.

### Presentation Style

Use the `presentationStyle` parameter in `BiomEnrollConfig` to control how the enrollment UX/UI is presented during the enrollment flow.

* `Full Screen` (default): Shows the standard full-screen enrollment experience.
* `Overlay`: Shows a streamlined and faster UX/UI, consistent with the authentication experience.

***

Go [here](https://docs.keyless.io/consumer/mobile-sdk-reference/ui-customization) to view the UI customization for this flow.
