> ## 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.

# Enrollment lifecycle

> Enroll and un-enroll users for app verification as part of your mobile app's authentication lifecycle.

Whether you are implementing app verification for [push](/authentication-methods/app-verification/push), [QR code](/authentication-methods/app-verification/qr-code), or [in-app](/authentication-methods/app-verification/in-app) flows, you'll need to consider when to enroll and un-enroll users in your mobile app.

This guide will cover the appropriate points in your app's authentication lifecycle to enroll and un-enroll users by adding and removing device credentials. The code examples below use the `push` namespace. If you're implementing QR code or in-app verification, replace `push` with `qr` or `inapp`.

## When to enroll

App verification enrollment for users is typically an invisible process which happens automatically when they're signed in to your mobile app.
Your app uses one of our [Mobile SDKs](/sdks/client/mobile/setup) to generate a cryptographic device credential and bind it to an authenticated user.

The recommended place to enroll users by adding a device credential is within a **post-authentication handler**.
This handler should be triggered in the following two scenarios.

1. Immediately after the user signs in to the app.
2. When the app is launched and the user is already authenticated (e.g. due to a persistent session).

If your app uses long-lived sessions and doesn’t require frequent sign-ins, the second scenario is especially important.
It will ensure that users who launch the app after installing your updated version are enrolled for app verification without explicitly having to sign in again.

The following code should be run in both of the above scenarios to enroll a new credential if none exists on the device.

<CodeGroup>
  ```swift iOS theme={null}
  func enrollCredential() async {
      let response = await authsignal.push.getCredential()

      if response.data != nil {
          // A credential already exists
          return
      }

      // Fetch an enrollment token from your backend
      let token = try await getEnrollmentToken()

      // Silently enroll a new device credential with attestation
      await authsignal.push.addCredential(token: token, performAttestation: true)
  }
  ```

  ```kotlin Android theme={null}
  suspend fun enrollCredential() {
      val response = authsignal.push.getCredential()

      if (response.data != null) {
          // A credential already exists
          return
      }

      // Fetch an enrollment token from your backend
      val token = getEnrollmentToken()

      // Silently enroll a new device credential with attestation
      authsignal.push.addCredential(token = token, performAttestation = true)
  }
  ```

  ```ts React Native theme={null}
  async function enrollCredential() {
    const response = await authsignal.push.getCredential();

    if (response.data) {
      // A credential already exists
      return;
    }

    // Fetch an enrollment token from your backend by calling track
    const token = await getEnrollmentToken();

    // Silently enroll a new device credential with attestation
    await authsignal.push.addCredential({ token, performAttestation: true });
  }
  ```

  ```dart Flutter theme={null}
  Future<void> enrollCredential() async {
    final response = await authsignal.push.getCredential();

    if (response.data != null) {
      // A credential already exists
      return;
    }

    // Fetch an enrollment token from your backend
    final token = await getEnrollmentToken();

    // Silently enroll a new device credential
    await authsignal.push.addCredential(token);
  }
  ```
</CodeGroup>

This code should be run after a user signs in.

<CodeGroup>
  ```swift iOS theme={null}
  func postSignInHandler() async {
      await enrollCredential()
  }
  ```

  ```kotlin Android theme={null}
  suspend fun postSignInHandler() {
      enrollCredential()
  }
  ```

  ```ts React Native theme={null}
  async function postSignInHandler() {
    await enrollCredential();
  }
  ```

  ```dart Flutter theme={null}
  Future<void> postSignInHandler() async {
    await enrollCredential();
  }
  ```
</CodeGroup>

It should also be run when the user launches the app while already authenticated.

<CodeGroup>
  ```swift iOS theme={null}
  func postLaunchHandler() async {
      if isAuthenticated {
          await enrollCredential()
      }
  }
  ```

  ```kotlin Android theme={null}
  suspend fun postLaunchHandler() {
      if (isAuthenticated) {
          enrollCredential()
      }
  }
  ```

  ```ts React Native theme={null}
  async function postLaunchHandler() {
    if (isAuthenticated) {
      await enrollCredential();
    }
  }
  ```

  ```dart Flutter theme={null}
  Future<void> postLaunchHandler() async {
    if (isAuthenticated) {
      await enrollCredential();
    }
  }
  ```
</CodeGroup>

Your backend code to generate the enrollment token should be implemented within an authenticated endpoint.

<CodeGroup>
  ```js Node.js theme={null}
  // A mock example of an endpoint for generating enrollment tokens
  app.post("/generate-enrollment-token", async (req, res) => {
    // Determine user from authenticated request context
    const userId = req.user.sub;

    const { token } = await authsignal.track({
      userId,
      action: "addCredential",
      attributes: {
        scope: "add:authenticators",
      },
    });

    res.json({ token });
  });
  ```
</CodeGroup>

The **add:authenticators** scope is required here since we are enrolling a new authentication method in an [authenticated context](/advanced-usage/authenticator-binding).
The **action** can be any value which describes the enrollment activity and will be used for observability in the Authsignal Portal.

### Handling app updates

A common question is how to enroll users who are **already signed in and just received an app update** that introduces app verification.

The post-launch handler shown above covers this scenario automatically. No separate flow is required.

When an existing authenticated user opens the updated app for the first time:

1. The post-launch handler runs because the user's session is still valid.
2. `getCredential` returns no credential (since the device hasn't been enrolled yet on this version).
3. A new device credential is silently enrolled via `addCredential`.

For users who already had a credential from a prior version, `getCredential` short-circuits and no re-enrollment occurs, so the flow is safe to run on every launch.

## Keeping credentials alive

By default a device credential never expires. You can optionally configure a **credential lifetime** on the push authenticator so that credentials expire if the device goes quiet for too long. This is useful for pruning credentials belonging to devices a user no longer has. Set the lifetime in the Authsignal Portal under the push authenticator configuration. When it's unset, credentials never expire and no keep-alive is needed.

When a lifetime is configured, a credential expires if it isn't kept alive within that window. An expired credential is removed: it's excluded from push challenges and `getCredential` returns no credential. To stay enrolled, the device extends its own lease by calling `updateCredential` with `resetExpiry` set to `true`.

<CodeGroup>
  ```swift iOS theme={null}
  await authsignal.push.updateCredential(resetExpiry: true)
  ```

  ```kotlin Android theme={null}
  authsignal.push.updateCredential(resetExpiry = true)
  ```

  ```ts React Native theme={null}
  await authsignal.push.updateCredential({ resetExpiry: true });
  ```
</CodeGroup>

The natural place to make this call is the same post-launch handler you already use to enroll, since it runs whenever an authenticated user opens the app. And because `getCredential` returns no credential once one expires, the `enrollCredential` function shown above already handles the expired case: it enrolls a fresh credential when none is found.

## When to un-enroll

The recommended point in your app to un-enroll users for app verification is after sign-out.
This means that once signed out users will no longer be enrolled for app verification on that device.

<CodeGroup>
  ```swift iOS theme={null}
  func postSignOutHandler() async {
      await authsignal.push.removeCredential()
  }
  ```

  ```kotlin Android theme={null}
  suspend fun postSignOutHandler() {
      authsignal.push.removeCredential()
  }
  ```

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

  ```dart Flutter theme={null}
  Future<void> postSignOutHandler() async {
    await authsignal.push.removeCredential();
  }
  ```
</CodeGroup>
