> ## Documentation Index
> Fetch the complete documentation index at: https://docs.authsignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# QR code verification

> Implement app verification with QR codes using the Authsignal SDKs for iOS, Android, React Native, and Flutter.

<Info>
  Check out our end-to-end guide on [how to implement app verification with QR
  codes](/authentication-methods/app-verification/qr-code).
</Info>

## Adding a credential

Adding a QR code credential generates a private/public key pair, where the private key is secured on the user's mobile device and the public key is held by Authsignal.

This operation must be authorized with a short-lived token, which can be obtained by [tracking an action from your backend](/advanced-usage/authenticator-binding#tracking-an-action-to-generate-a-token) in an authenticated context.

<CodeGroup>
  ```swift iOS theme={null}
  await authsignal.qr.addCredential(token: "eyJhbGciOiJ...")
  ```

  ```kotlin Android theme={null}
  authsignal.qr.addCredential(token = "eyJhbGciOiJ...")
  ```

  ```ts React Native theme={null}
  await authsignal.qr.addCredential({ token: "eyJhbGciOiJ..." });
  ```

  ```dart Flutter theme={null}
  await authsignal.qr.addCredential("eyJhbGciOiJ...");
  ```
</CodeGroup>

### Parameters

<ResponseField name="token" type="string">
  A short-lived token obtained by [tracking an
  action](/advanced-usage/authenticator-binding#tracking-an-action-to-generate-a-token).
</ResponseField>

### Response

<ResponseField name="response" type="AuthsignalResponse<AppCredential>">
  <Expandable title="properties">
    <ResponseField name="error" type="string">
      An unstructured error description present if the SDK call encountered an error.
    </ResponseField>

    <ResponseField name="errorCode" type="string">
      A formatted error code which may additionally be present if the SDK call encountered an error.
      Possible values are: `token_not_set`, `expired_token` or `network_error`.
    </ResponseField>

    <ResponseField name="data" type="AppCredential | null">
      <Expandable title="properties">
        <ResponseField name="credentialId" type="string" required>
          The ID of the app credential.
        </ResponseField>

        <ResponseField name="createdAt" type="string" required>
          The date and time the app credential was created.
        </ResponseField>

        <ResponseField name="userId" type="string" required>
          The user ID of the user that the app credential belongs to.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

## Getting a credential

Get information about the QR code credential stored on the device, if one exists.

<CodeGroup>
  ```swift iOS theme={null}
  let response = await authsignal.qr.getCredential()
  ```

  ```kotlin Android theme={null}
  val response = authsignal.qr.getCredential()
  ```

  ```ts React Native theme={null}
  const response = await authsignal.qr.getCredential();
  ```

  ```dart Flutter theme={null}
  final response = await authsignal.qr.getCredential();
  ```
</CodeGroup>

### Response

<ResponseField name="response" type="AuthsignalResponse<AppCredential>">
  <Expandable title="properties">
    <ResponseField name="error" type="string">
      An unstructured error description present if the SDK call encountered an error.
    </ResponseField>

    <ResponseField name="errorCode" type="string">
      A formatted error code which may additionally be present if the SDK call encountered an error.
      Possible values are: `invalid_credential` when the credential exists on the device but has
      been removed from the server.
    </ResponseField>

    <ResponseField name="data" type="AppCredential">
      <Expandable title="properties">
        <ResponseField name="credentialId" type="string" required>
          The ID of the app credential.
        </ResponseField>

        <ResponseField name="createdAt" type="string" required>
          The date and time the app credential was created.
        </ResponseField>

        <ResponseField name="lastAuthenticatedAt" type="string">
          The date and time the app credential was last used for authentication. Will be null if the
          credential has never been used.
        </ResponseField>

        <ResponseField name="userId" type="string" required>
          The user ID of the user that the app credential belongs to.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

## Removing a credential

<CodeGroup>
  ```swift iOS theme={null}
  await authsignal.qr.removeCredential()
  ```

  ```kotlin Android theme={null}
  authsignal.qr.removeCredential()
  ```

  ```ts React Native theme={null}
  await authsignal.qr.removeCredential();
  ```

  ```dart Flutter theme={null}
  await authsignal.qr.removeCredential();
  ```
</CodeGroup>

### Response

<ResponseField name="response" type="AuthsignalResponse<boolean>">
  <Expandable title="properties">
    <ResponseField name="error" type="string">
      An unstructured error description present if the SDK call encountered an error. This could
      occur if the credential on the device is no longer valid because the corresponding user
      authenticator has been deleted in the Authsignal Portal.
    </ResponseField>

    <ResponseField name="data" type="boolean">
      True if the app credential was successfully removed.
    </ResponseField>
  </Expandable>
</ResponseField>

## Claiming a challenge

When the user scans the QR code, you should call `claimChallenge` to set the user attempting to complete the challenge.
This will return some context about the desktop or kiosk device initiating the challenge such as ip address, location, user agent and custom data.
This data can be shown to the user to help them decide if they want to approve or decline the challenge.

<CodeGroup>
  ```swift iOS theme={null}
  await authsignal.qr.claimChallenge(
      challengeId: challengeId
  )
  ```

  ```kotlin Android theme={null}
  authsignal.qr.claimChallenge(
      challengeId = challengeId
  )
  ```

  ```ts React Native theme={null}
  await authsignal.qr.claimChallenge({
    challengeId,
  });
  ```

  ```dart Flutter theme={null}
  await authsignal.qr.claimChallenge(
      challengeId,
  );
  ```
</CodeGroup>

### Response

<ResponseField name="response" type="AuthsignalResponse<ClaimChallengeResponse>">
  <Expandable title="properties">
    <ResponseField name="error" type="string">
      An unstructured error description present if the SDK call encountered an error. This could
      occur if the credential on the device is no longer valid because the corresponding user
      authenticator has been deleted in the Authsignal Portal.
    </ResponseField>

    <ResponseField name="data" type="ClaimChallengeResponse">
      <Expandable title="properties">
        <ResponseField name="ipAddress" type="string" required>
          The IP address of the device which initiated the challenge.
        </ResponseField>

        <ResponseField name="userAgent" type="string" required>
          The user agent of the device which initiated the challenge.
        </ResponseField>

        <ResponseField name="custom" type="object">
          Action-scope custom data passed to the challenge. Only [custom data points marked as
          public](/actions-rules/rules/custom-data-points#public-custom-data-points) are returned.
        </ResponseField>

        <ResponseField name="user" type="object">
          <Expandable title="properties">
            <ResponseField name="custom" type="object">
              User-scope custom data for the user being challenged. Only [custom data points marked
              as public](/actions-rules/rules/custom-data-points#public-custom-data-points) are
              returned.
            </ResponseField>
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

## Updating a challenge

After presenting the user with a prompt to approve or reject the request, you should update the challenge with their response.

<CodeGroup>
  ```swift iOS theme={null}
  await authsignal.qr.updateChallenge(
      challengeId: challengeId,
      approved: true
  )
  ```

  ```kotlin Android theme={null}
  authsignal.qr.updateChallenge(
      challengeId = challengeId,
      approved = true
  )
  ```

  ```ts React Native theme={null}
  await authsignal.qr.updateChallenge({
    challengeId,
    approved: true,
  });
  ```

  ```dart Flutter theme={null}
  await authsignal.qr.updateChallenge(
      challengeId,
      true,
  );
  ```
</CodeGroup>

### Response

<ResponseField name="response" type="AuthsignalResponse<boolean>">
  <Expandable title="properties">
    <ResponseField name="error" type="string">
      An unstructured error description present if the SDK call encountered an error. This could
      occur if the credential on the device is no longer valid because the corresponding user
      authenticator has been deleted in the Authsignal Portal.
    </ResponseField>

    <ResponseField name="data" type="boolean">
      True if the device challenge was successfully updated.
    </ResponseField>
  </Expandable>
</ResponseField>

## Requiring user authentication

When adding a credential for QR code verification, it is possible to require that the user authenticate via their OS biometrics or PIN whenever they access the credential (e.g. when approving or rejecting a challenge).

### iOS

To require user authentication on iOS, set the `userPresenceRequired` flag to true when adding the credential.

```swift theme={null}
await authsignal.push.addCredential(
    token: token,
    userPresenceRequired: true
)
```

This is all that's needed - iOS will automatically handle displaying the biometrics or PIN prompt when [updating a challenge](#updating-a-challenge) or [verifying a device](#verifying-a-device).

### Android

To require user authentication on Android, set the `userAuthenticationRequired` flag to true when adding the credential.

```kotlin theme={null}
authsignal.push.addCredential(
    token = token,
    userAuthenticationRequired = true
)
```

It's also possible to specify additional [user authentication parameters](https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setUserAuthenticationParameters\(int,%20int\)).

```kotlin theme={null}
authsignal.push.addCredential(
    token = token,
    userAuthenticationRequired = true,
    timeout = 60,
    authorizationType = 0
)
```

The `timeout` param determines the time (in seconds) that the credential can be accessed after authenticating - or `0` if authentication must occur for every credential use.
The `authorizationType` param determines if authentication is required via biometrics and/or device credential (e.g. pin).

If user authentication is required for a credential, you **must** call `updateChallenge` in a [biometric prompt authentication callback](https://developer.android.com/identity/sign-in/biometric-auth).

1. Initialize a signature before displaying the biometric prompt

```kotlin theme={null}
val signature = authsignal.push.startSigning()

val cryptoObject = CryptoObject(signature)

// Initialize biometric prompt and prompt info
val biometricPrompt = ...
val promptInfo = ...

biometricPrompt.authenticate(promptInfo, cryptoObject);
```

2. Retrieve the signature from the crypto object in your prompt's authentication callback

```kotlin theme={null}
override fun onAuthenticationSucceeded(result: AuthenticationResult) {
    super.onAuthenticationSucceeded(result)

    val cryptoObject = result.cryptoObject
    val signer = cryptoObject.signature

    authsignal.push.updateChallenge(
        challengeId = challengeId,
        approved = true,
        signer = signer
    )
}
```
