Skip to content

Set up passkeys in your Android and iOS apps

Find the SHA-256 of your signing certificate, the Team ID and bundle id, host assetlinks.json and apple-app-site-association, and add Associated Domains.

PasskeysUpdated 26 October 20268 min read

Passkeys in an Android or iOS app are the same passkeys as on your website: made for your domain and checked by the same server calls. What the app adds is proof that it belongs to the domain, through a file you host on the domain. Do the website guide first, or at least its step 1, so the dashboard knows your domain.

The SignOps phone SDKs (Android, iOS, React Native, Flutter) make the passkey calls. They are not on Maven Central, Swift Package Manager, npm or pub.dev yet; the API reference lists the calls they make.

Android

1. Find your package name and certificate fingerprint

  • Package name: the applicationId in app/build.gradle.kts, such as com.kiranaexpress.app.
  • SHA-256 of the signing certificate, for each way the app is signed:
    • Google Play: Play Console, your app, App integrity, App signing, the SHA-256 under App signing key certificate. This is the one on users’ phones.
    • Debug builds from Android Studio:
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
# or, in your project:
./gradlew signingReport

Copy the line that starts SHA256:, such as 14:6D:E9:83:C5:73:...:44:E5.

2. Add the app in the dashboard

Passkeys, Setup, Android and iOS apps: type the package name and the SHA-256, and choose Add. Add one row per certificate (Play and debug). The dashboard then shows an assetlinks.json.

3. Host assetlinks.json

Put the file the dashboard shows at:

https://kiranaexpress.in/.well-known/assetlinks.json

On the domain from step 1 of the website guide (not a subdomain), over https, as application/json, with no redirect. In most frameworks that means a public/.well-known/assetlinks.json file. Check it with Google’s Statement List Generator and Tester.

4. Call the SDK

// minSdk 28. The registration token comes from your server, as for the web.
val signops = SignOps(context, "pk_live_...")

lifecycleScope.launch {
    signops.passkeys.register(this@MainActivity, registrationToken)   // add a passkey
    val r = signops.passkeys.signIn(this@MainActivity)                 // sign in
    api.signIn(r.signopsToken)   // your server checks it with POST /v1/passkeys/verify
}

The error no_passkey means the user has none for your domain on this phone; offer your other sign-in. cancelled means they closed the prompt.

iOS

1. Find your Team ID and bundle id

2. Add the app in the dashboard

Passkeys, Setup, Android and iOS apps: type the Team ID and the bundle id, and choose Add. The dashboard then shows an apple-app-site-association file.

3. Host apple-app-site-association

https://kiranaexpress.in/.well-known/apple-app-site-association

The name has no .json extension, and it must still be served as application/json, over https, with no redirect. Static hosts usually need a header rule for that one path. Apple fetches the file through its own cache, so a change can take time to reach phones.

4. Add Associated Domains in Xcode

Your app target, Signing and Capabilities, + Capability, Associated Domains, then add:

webcredentials:kiranaexpress.in

While developing, use webcredentials:kiranaexpress.in?mode=developer and turn on Associated Domains Development under Settings, Developer on the iPhone. The phone then reads the file from your site directly instead of Apple’s cache. Remove ?mode=developer before you ship.

5. Call the SDK

// iOS 16 and later
let signops = SignOps(key: "pk_live_...")

let made = try await signops.passkeys.register(token: registrationToken, anchor: view.window!)
let r = try await signops.passkeys.signIn(anchor: view.window!, autofill: true)
try await api.signIn(r.signopsToken)   // your server checks it with POST /v1/passkeys/verify

With autofill: true, the passkey is offered in the keyboard on a text field with textContentType = .username.

React Native and Flutter

The same two steps (dashboard, then the hosted file and Associated Domains) apply. In code:

// React Native, @signops/react-native
await signops.passkeys.register(registrationToken);
const { signopsToken } = await signops.passkeys.signIn();
// Flutter, signops
await signops.passkeys.register(registrationToken);
final signedIn = await signops.passkeys.signIn();   // signedIn.signopsToken to your server

Flutter’s MainActivity must extend FlutterFragmentActivity, and both need Android minSdk 28 and iOS 16.

Your server does not change

The registration token and the check after sign-in are the same calls as for the website. One server endpoint serves the browser, the Android app and the iOS app.

Frequently asked questions

More questions? Email us and the answer goes on this page.

hello@signops.dev
Which SHA-256 do I add for an app on Google Play?

The app signing key certificate from Play Console, App integrity, App signing. With Play App Signing, Google re-signs your app, so the fingerprint of your upload key is not the one on users' phones. Add your debug certificate as well while you develop.

Do passkeys made on my website work in my app?

Yes. Once the app is linked to your domain with assetlinks.json or apple-app-site-association, the same passkeys work in the browser and in the app, and the same server calls check them.

Why does iOS not see my apple-app-site-association file?

Apple fetches it through its own cache, not from the phone, so a new or changed file can take a while to reach devices. It must be at /.well-known/apple-app-site-association, served as application/json over https with no redirect. While developing, add ?mode=developer to the Associated Domains entry and turn on Associated Domains Development under Settings, Developer on the iPhone.

Which versions are supported?

Android 9 and later through Credential Manager, and iOS 16 and later through AuthenticationServices.

Send your first push today.

Free for 1,000 push devices, 100 approvals and 1,000 passkey sign-ins a month, and 500 email codes to try. No card until you pass a free amount.