# 5️⃣ User and device management

The Keyless SDK "caches" the enrolled user locally on the device.

There are some use cases where it is possible to [delete the user from server API](https://docs.keyless.io/consumer/server-api/users) and [delete the device from server API](https://docs.keyless.io/consumer/server-api/devices). The Keyless SDK will not be notified about such deletions. For this reason if you try to authenticate a user or a device that have been deleted from server API you will get an error.

Call `validateUserAndDeviceActive` **before** authenticating, to validate that both the user and the device are still active in the Keyless backend, to avoid asking the user for biometric data which will still not let them authenticate.

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

```kotlin
Keyless.validateUserAndDeviceActive(
    onCompletion = { result ->
        when (result) {
            is Keyless.KeylessResult.Success -> Log.d("KeylessSDK ", "user and device active")
            is Keyless.KeylessResult.Failure -> Log.d("KeylessSDK ", "user or device not ofund - error code ${result.error.code}")
            // error code 1131 = user is not enrolled on the device (not even locally so did not check on backend)
            // error code 534 = user not found or deactivated on backend
            // error code 535 = device not found or deactivated on backend
        }
    }
)
```

{% endtab %}

{% tab title="iOS" %}

```swift
Keyless.validateUserDeviceActive(
    completionHandler: { error in
        if let error = error {
            print("user or device deactivated")
            // error code 1131 = user is not enrolled on the device (not even locally so did not check on backend)
            // error code 534 = user not found or deactivated on backend
            // error code 535 = device not found or deactivated on backend
        } else {
            print("user and device active")
        }
    }
)
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
  await Keyless.validateUserAndDeviceActive();
  print("user and device active");
} catch (error) {
  print("user or device deactivated - error code: ${error.code}");
  // error code 1131 = user is not enrolled on the device (not even locally so did not check on backend)
  // error code 534 = user not found or deactivated on backend
  // error code 535 = device not found or deactivated on backend
}
```

{% endtab %}

{% tab title="React Native" %}

```javascript
import Keyless from '@react-native-keyless/sdk';

const validateUserAndDevice = async () => {
  const result = await Keyless.validateUserAndDeviceActive();

  result.fold({
    onSuccess: () => {
      console.log("user and device active");
    },
    onFailure: (error) => {
      console.error(`user or device deactivated - error code: ${error.code}`);
      // error code 1131 = user is not enrolled on the device (not even locally)
      // error code 534 = user not found or deactivated on backend
      // error code 535 = device not found or deactivated on backend
    }
  });
};
```

{% endtab %}
{% endtabs %}

## User identifier

Retrieve the user identifier with `Keyless.getUserId()`:

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

```kotlin
fun getUserId(): KeylessResult<String, KeylessSdkError>
```

{% endtab %}

{% tab title="iOS" %}

```swift
func getUserId() -> Result<String, KeylessSDKError>
```

{% endtab %}

{% tab title="Flutter" %}

```dart
Future<String> getUserId() async
```

{% endtab %}

{% tab title="React Native" %}

```javascript
import Keyless from '@react-native-keyless/sdk';

const getUserId = async () => {
  const result = await Keyless.getUserId();

  result.fold({
    onSuccess: (userId) => {
      console.log("User ID:", userId);
    },
    onFailure: (error) => {
      console.error("Failed to get user ID:", error);
    },
  });
};
```

{% endtab %}
{% endtabs %}

## Device identifier

The device is identified by its public signing key. To retrieve the public signing key use `Keyless.getDevicePublicSigningKey()`:

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

```kotlin
fun getDevicePublicSigningKey(): KeylessResult<ByteArray, KeylessSdkError>
```

{% endtab %}

{% tab title="iOS" %}

```swift
func getDevicePublicSigningKey() -> Result<String, KeylessSDKError>
```

{% endtab %}

{% tab title="Flutter" %}

```dart
Future<String> getDevicePublicSigningKey() async
```

{% endtab %}

{% tab title="React Native" %}

```javascript
import Keyless from '@react-native-keyless/sdk';

const getDevicePublicSigningKey = async () => {
  const result = await Keyless.getDevicePublicSigningKey();

  result.fold({
    onSuccess: (key) => {
      console.log("Device public key:", key);
    },
    onFailure: (error) => {
      console.error("Failed to get device public key:", error);
    },
  });
};
```

{% endtab %}
{% endtabs %}

## Keyless SDK reset

Resetting the Keyless SDK to a clean state deletes local data from the device, but does not de-enroll the user from the Keyless backend or deactivate the device from the Keyless backend:

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

```kotlin
fun reset(
  onCompletion: (KeylessResult<Unit, KeylessSdkError>) -> Unit
)
```

{% endtab %}

{% tab title="iOS" %}

```swift
func reset() -> KeylessSDKError?
```

{% endtab %}

{% tab title="Flutter" %}

```dart
Future<void> reset() async
```

{% endtab %}

{% tab title="React Native" %}

```javascript
import Keyless from '@react-native-keyless/sdk';

const resetSdk = async () => {
  const result = await Keyless.reset();

  result.fold({
    onSuccess: () => {
      console.log("SDK reset successfully");
    },
    onFailure: (error) => {
      console.error("SDK reset failed:", error);
    },
  });
};
```

{% endtab %}
{% endtabs %}

## Lockout management

The `getRateLimitInfo` API checks whether the user is currently rate-limited and, if so, for how many seconds.\
This API is typically used to provide feedback to users after multiple failed authentication attempts.

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

```kotlin
Keyless.getRateLimitInfo { result ->
    when (result) {
        is Keyless.KeylessResult.Success -> {
            val rateLimitInfo = result.value
            println("User is rate limited: ${rateLimitInfo.isRateLimited} with remaining seconds: ${rateLimitInfo.remainingSeconds}")
        }
        is Keyless.KeylessResult.Failure -> {
            println("Error: ${result.error.message}")
        }
    }
}
```

{% endtab %}

{% tab title="iOS" %}

```swift
Keyless.getRateLimitInfo(completion: { result in
    switch result {
    case .success(let success):
        print("User is rate limited: \(success.isRateLimited) with remaining seconds: \(success.remainingSeconds)")
    case .failure(let error):
        print("Error: \(error.message)")
    }
})
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
    final rateLimitInfo = await Keyless.instance.getRateLimitInfo();
    print("User is rate limited: ${rateLimitInfo.isRateLimited} with remaining seconds: ${rateLimitInfo.remainingSeconds}");
} catch (error) {
    print("Error: $error");
}
```

{% endtab %}

{% tab title="React Native" %}

```javascript
import Keyless from '@react-native-keyless/sdk';

const getRateLimitInfo = async () => {
  const result = await Keyless.getRateLimitInfo();

  result.fold({
    onSuccess: (info) => {
      console.log(`User is rate limited: ${info.isRateLimited}, remaining seconds: ${info.remainingSeconds}`);
    },
    onFailure: (error) => {
      console.error("Error fetching rate limit info:", error);
    },
  });
};
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keyless.io/consumer/mobile-sdk-guide/user-and-device-management.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
