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

# Passkey setup

> Associate your native Android or iOS app with its relying party domain and configure passkeys in the OwnID Console.

Passkeys require a verified association between your native app and its relying party domain. Configure the platform association file, app signing, and the OwnID Console before adding an authentication or enrollment flow.

<Info>
  <span>
    OwnID Android SDK supports API 23+, but Android passkeys require API 28+.<br />
    OwnID iOS SDK supports iOS 13+, but passkeys require iOS 16+.
  </span>
</Info>

## Configure your platform

<Tabs>
  <Tab title="Android" icon="android" iconType="brands">
    OwnID uses Android Credential Manager for passkey creation and authentication.

    <Steps>
      <Step title="Add Credential Manager">
        Use the current AndroidX Credentials version for both artifacts:

        <a href="https://developer.android.com/jetpack/androidx/releases/credentials" style={{ borderBottom: "none", display: "inline-flex", lineHeight: 0 }}>
          <span className="sr-only">Latest stable AndroidX Credentials version</span>

          <img src={"https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fdl.google.com%2Fdl%2Fandroid%2Fmaven2%2Fandroidx%2Fcredentials%2Fcredentials%2Fmaven-metadata.xml&filter=%21%2A-%2A&label=Latest%20AndroidX%20Credentials"} alt="AndroidX Credentials version badge" aria-hidden="true" style={{ margin: 0 }} />
        </a>

        ```kotlin build.gradle.kts theme={null}
        dependencies {
            implementation("androidx.credentials:credentials:<latest-version>")
            implementation("androidx.credentials:credentials-play-services-auth:<latest-version>")
        }
        ```

        See the [AndroidX Credentials release notes](https://developer.android.com/jetpack/androidx/releases/credentials) for version-specific changes.
      </Step>

      <Step title="Publish Digital Asset Links">
        Host `assetlinks.json` at this exact URL:

        ```text theme={null}
        https://<relying-party-domain>/.well-known/assetlinks.json
        ```

        The endpoint must be public over HTTPS, return `HTTP 200` with a JSON content type, and must not redirect. Allow automated retrieval of `/.well-known/assetlinks.json`; if the domain uses `robots.txt`, make sure it does not block `/.well-known/`.

        ```json assetlinks.json theme={null}
        [
          {
            "relation": [
              "delegate_permission/common.handle_all_urls",
              "delegate_permission/common.get_login_creds"
            ],
            "target": {
              "namespace": "android_app",
              "package_name": "<android_application_id>",
              "sha256_cert_fingerprints": [
                "<sha256_signing_certificate_fingerprint>"
              ]
            }
          }
        ]
        ```
      </Step>

      <Step title="Cover every signed app variant">
        Add an entry for every application ID and SHA-256 signing certificate that should use passkeys, including relevant debug, release, and product-flavor builds.

        You can inspect a keystore certificate with:

        ```bash theme={null}
        keytool -list -v -keystore <path-to-keystore>
        ```
      </Step>
    </Steps>

    See Android's [Credential Manager prerequisites](https://developer.android.com/identity/credential-manager/prerequisites) and [Digital Asset Links guidance](https://developers.google.com/digital-asset-links) for validation requirements.
  </Tab>

  <Tab title="iOS" icon="apple" iconType="brands">
    OwnID uses Apple's AuthenticationServices framework for passkey creation and authentication.

    <Steps>
      <Step title="Add the Associated Domains capability">
        In Xcode, open your app target, select **Signing & Capabilities**, and add **Associated Domains**. Add the relying party domain without a protocol or path:

        ```text theme={null}
        webcredentials:<relying-party-domain>
        ```

        <Frame caption="Associated Domains capability configured for web credentials">
          <img src="https://mintcdn.com/ownid/i3kGqVa3mIaTdz4d/images/supporting-associated-domains.png?fit=max&auto=format&n=i3kGqVa3mIaTdz4d&q=85&s=9554818957d4d2d74e72ab54eec05505" alt="Xcode Associated Domains capability with a webcredentials domain entry" width="1436" height="272" data-path="images/supporting-associated-domains.png" />
        </Frame>

        Enable **Associated Domains** for the matching App ID in Apple Developer as well. If the app uses manual signing, regenerate and install its provisioning profiles after enabling the capability.
      </Step>

      <Step title="Publish the Apple App Site Association file">
        Host the extensionless file at this exact URL:

        ```text theme={null}
        https://<relying-party-domain>/.well-known/apple-app-site-association
        ```

        The endpoint must be public over HTTPS, return `HTTP 200` with a JSON content type, must not redirect, and its uncompressed content must stay at or below 128 KB.

        ```json apple-app-site-association theme={null}
        {
          "webcredentials": {
            "apps": [
              "<APP_ID_PREFIX>.<BUNDLE_ID>"
            ]
          }
        }
        ```
      </Step>

      <Step title="Verify the signed application identifier">
        Add every app target that should use passkeys. Each `<APP_ID_PREFIX>.<BUNDLE_ID>` value must match the signed app's `application-identifier` entitlement.

        The App ID prefix is often your Apple Team ID, but it can differ. Use the prefix from the signed app or provisioning profile.
      </Step>
    </Steps>

    See Apple's [Supporting associated domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains) and [Associated Domains entitlement](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.associated-domains) references for signing and validation requirements.
  </Tab>
</Tabs>

## Configure the OwnID Console

Use the Android application ID and signing certificate fingerprint or iOS bundle ID that corresponds to the values in each association file.

<Steps>
  <Step title="Open the native app settings">
    Sign in to the [OwnID Console](https://console.ownid.com/), open your application, and go to **Integration > Native Apps**.

    <Frame caption="Native Apps configuration in the OwnID Console">
      <img src="https://mintcdn.com/ownid/FZ6hme-wnXgWbl15/images/native-passkeys-console.png?fit=max&auto=format&n=FZ6hme-wnXgWbl15&q=85&s=792ca55e2ce75044668e67c829e61473" alt="OwnID Console Native Apps configuration screen" width="1786" height="1860" data-path="images/native-passkeys-console.png" />
    </Frame>
  </Step>

  <Step title="Add platform identifiers">
    <Tabs>
      <Tab title="Android" icon="android" iconType="brands">
        Enter the Android application ID and SHA-256 signing certificate fingerprint. They must match an entry in `assetlinks.json`.
      </Tab>

      <Tab title="iOS" icon="apple" iconType="brands">
        Enter the iOS bundle identifier. It must match the bundle ID used in the Apple App Site Association file and the signed app.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Save the configuration">
    Save your changes before testing passkey creation or authentication.
  </Step>
</Steps>

## Verify the association

<Warning>
  The association files must be publicly accessible in every environment where passkeys are tested or used. The endpoints must not require a password, Basic Auth, SSO, cookies, VPN access, mTLS, an IP allowlist, or an interactive WAF or bot-protection challenge.
</Warning>

Verify the association files on the exact HTTPS host used as the relying party domain.

<Tabs>
  <Tab title="Android" icon="android" iconType="brands">
    Fetch the association file directly without following redirects:

    ```text theme={null}
    https://<relying-party-domain>/.well-known/assetlinks.json
    ```

    Confirm that the endpoint:

    * Returns `HTTP 200` with `Content-Type: application/json`
    * Does not redirect
    * Returns valid JSON rather than an HTML login or error page
    * Is accessible from a public network without authentication or VPN access
    * Allows automated retrieval and is not blocked by `robots.txt`

    Confirm that the Android application ID and certificate fingerprints match the installed build and the OwnID Console configuration.

    Validate the hosted file with Google's [Statement List Generator and Tester](https://developers.google.com/digital-asset-links/tools/generator). Select **Credential Sharing** as the relation type.
  </Tab>

  <Tab title="iOS" icon="apple" iconType="brands">
    Fetch the association file directly without following redirects:

    ```text theme={null}
    https://<relying-party-domain>/.well-known/apple-app-site-association
    ```

    Confirm that the endpoint:

    * Returns `HTTP 200` with a JSON content type
    * Does not redirect
    * Returns valid JSON rather than an HTML login or error page
    * Is accessible from a public network without authentication or VPN access
    * Is accessible regardless of client IP address, region, or User-Agent

    Verify the [Associated Domains capability](https://developer.apple.com/documentation/xcode/configuring-an-associated-domain) for your app target in Xcode:

    1. Open the app target's signing and capabilities settings and find **Associated Domains**. If the capability is not present, add it to the target.
    2. Confirm that its domain list contains the exact relying party host in this format:

       ```text theme={null}
       webcredentials:<relying-party-domain>
       ```

       Do not include `https://`, a path, query parameters, or a trailing slash.
    3. Confirm that the same host is used for the passkey relying party domain and the `apple-app-site-association` URL.
    4. Confirm that `webcredentials.apps` contains the app's exact `<APP_ID_PREFIX>.<BUNDLE_ID>` value.
  </Tab>
</Tabs>

### Allow time for updates

Association files may be cached by your hosting provider, CDN, and the platform verification services. Updating the file on the origin server does not guarantee that Android or Apple devices receive the new version immediately.

Inspect the response headers for each association file:

```bash theme={null}
curl -sS -D - -o /dev/null \
  https://<relying-party-domain>/.well-known/<association-file>
```

Review `Cache-Control`, `Age`, `Expires`, `ETag`, and `Last-Modified`. Avoid long cache lifetimes, such as one week, if you expect to update the association files. A long `max-age` can keep an earlier version valid after the origin file has changed.

* Google Digital Asset Links determines statement validity from the underlying data TTL, subject to implementation limits. The [Digital Asset Links API](https://developers.google.com/digital-asset-links/reference/rest/v1/statements/list) returns `maxAge`, which indicates how much longer its result should be considered valid.
* Apple's CDN normally requests the `apple-app-site-association` file within 24 hours. Devices check for updates approximately once per week after installation. Apple does not provide manual cache invalidation. See [Supporting associated domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains).

After an update, first confirm that the public endpoint returns the new file and review its current cache headers.

For complete platform setup and troubleshooting, see the [Android SDK passkey guide](https://github.com/OwnID/ownid-android-sdk/blob/master/docs/setup/passkeys.md) or [iOS SDK passkey guide](https://github.com/OwnID/ownid-ios-sdk/blob/master/docs/setup/passkeys.md).
