SenseCrypt Docs
Guides

Ship your own Authenticator app

Run a tenant on a mobile app you build and distribute yourself — what to set up with Google and Apple, what to change in the build, and how to register the app's identity, push credentials and app-link host on the console's Mobile App page.

By default a tenant's users install the SenseCrypt Authenticator from the App Store and Google Play, and SenseCrypt verifies that app. A tenant can instead run its own app: a build you distribute under your own developer accounts, with your own name, icon, package and bundle identifiers, signing keys and push project. There are two ways to get such an app:

  • Rebuild the Authenticator. SenseCrypt provides the Authenticator source together with the Mobile SDK artifacts and a licence. You change the identifiers and configuration described below and ship it from your own accounts.
  • Embed the SDK in your own app. Your app drives the SDK flows itself. The page Running as the tenant's app lists what the host app must declare.

Either way the server has to know the app it is talking to. That is what the console's Mobile App page registers, and this guide walks through it.

Nothing SenseCrypt-specific may be reused. The identifiers, signing certificates, Firebase project and store listings must all be your own. The server verifies your app against what you register here: app attestation checks the identity, push notifications go through your push project, and sign-in links are issued on your app-link host. A rebuilt app only works once both the build and the console page say the same thing.

What the registration controls

These server behaviours read the tenant's mobile-app registration:

SurfaceWhat changes for a tenant on its own app
App attestationPlay Integrity and App Attest results are checked against your package name, signing certificates, team ID and bundle ID, in the mode you choose (off, audit or enforce).
Push notificationsCIBA sign-in and Face PKI requests are pushed through your Firebase project, using the service-account key you upload, and the push text names your app. iOS push rides Firebase Cloud Messaging too, so there is no separate APNs credential to upload.
App links/.well-known/assetlinks.json and /.well-known/apple-app-site-association on your app-link host describe your app, so the OS opens it for sign-in links.
Sign-in linksQR codes, emails and push notifications for this tenant are issued on your app-link host, and tap-to-open links use your custom URL scheme.
Passkey originsThe Android app-signing fingerprints you register become the accepted android:apk-key-hash WebAuthn origins for the tenant.
Sign-in pages"Open the app" copy and the store buttons name and link your app.
End-user emails and SMSPIN, CIBA-approval and device-added emails call the authenticator by your app name and are branded with the application's Branding rather than SenseCrypt's; the SMS verification code names your app too.

A tenant with no registration, or one that keeps App source on SenseCrypt Authenticator, behaves exactly as before: the stock app, the deployment's own identity and push project. A stored custom configuration is inactive, not blended, while the source is the stock app, so you can switch back and forth without re-entering anything.

Before you start: accounts with lead time

AccountWhat you need from it
Google Cloud and Firebase, one projectRegister your Android and iOS apps in Firebase with the package name and bundle ID your builds use. Upload your APNs authentication key to Firebase so iOS push rides FCM. Link the same project in Play Console so Play Integrity can vouch for your app. Create a service account with the Play Integrity API and Firebase Cloud Messaging API roles and export its JSON key. You upload that key on the Mobile App page; it never goes into the source tree.
Apple DeveloperYour team ID, and two App IDs: the app with the AutoFill Credential Provider, App Attest, Associated Domains, Keychain Sharing and Push Notifications capabilities; the credential-provider extension with AutoFill Credential Provider, App Attest and Keychain Sharing; plus one App Group shared by both.
Google PlayA listing. An internal test track is enough to start, but it must exist: Play Integrity's Play-recognised verdict requires Play distribution.

Step 1: build the app under your own identity

If you are rebuilding the Authenticator source, these are the values to change. If you embed the SDK, the same items apply to your own project; see Running as the tenant's app.

Android

  • The application ID (applicationId in app/build.gradle.kts), for example com.example.authenticator. Leave namespace as it is unless you also move the Kotlin sources: the manifest resolves its activity and service classes against it.
  • The Play Integrity cloud project number of your Google Cloud project (0 turns Play Integrity off in the SDK; the server then sees an unattested request, which passes only while the tenant's attestation mode is off or audit).
  • The production SenseCrypt base URL.
  • The app-link host: the host you will register on the console (see App links below). It is the app_link_host string resource; the manifest's autoVerify App Links intent filter reads it and must claim https://<host>/v1/link/*.
  • google-services.json from your Firebase project.
  • Release signing with your own keystore. The SHA-256 of the signing certificate is what you register on the console. If Play App Signing manages your release key, use the certificate shown under App signing in Play Console.
  • Your custom URL scheme in the deep-link intent filter. Keeping sensecrypt collides with the official app on a phone that has both.

iOS

  • Bundle IDs for the app and the credential-provider extension, and your development team.
  • The SenseCrypt base URL (BACKEND_BASE_URL) in both the app's and the extension's Info.plist, byte-identical including the trailing slash: the extension treats any difference as a portal switch and wipes the enrolled keys. The app-link host goes in the app's Info.plist (APP_LINK_HOST) and, decisively, in the associated-domains entitlement below.
  • The associated-domains entitlement applinks:<your app-link host>, on the app target.
  • Your own Keychain Sharing group and App Group identifiers, in both targets.
  • GoogleService-Info.plist from your Firebase project.
  • Your custom URL scheme in URL Types.
  • The App Attest environment: production for store builds; development only while testing development-signed builds, paired with the console's Require production App Attest environment switch turned off.

SDK and licence

The SDK binaries and your mobile.lic are delivered to you directly. The licence goes into the app's assets or resources, never into the credential-provider extension. See Install.

Step 2: register the app on the console

Open Mobile App in the tenant's console. Reading the page needs the mobile_app:read capability; saving needs mobile_app:update. Everything below is also available as GET and PATCH /v1/admin/mobile-app, scoped to the tenant of the host you call.

App source

Choose Your own mobile app. The switch asks for confirmation because it takes effect immediately:

  • Push notifications stop reaching devices enrolled through the SenseCrypt Authenticator, because they belong to a different Firebase project.
  • Sign-in links and store buttons start pointing at your app.
  • Attestation starts in Audit mode unless the same PATCH names a mode or the stored configuration already holds Audit or Enforce; the console's switch sends neither, so from the console it always starts in Audit. One click never takes a tenant from the deployment's enforced floor to no attestation.

Users then install your app and enrol in it. Switching back to SenseCrypt Authenticator deactivates your configuration without deleting it; devices enrolled through your app stop receiving pushes in turn.

App name and custom URL scheme

  • App name: what sign-in pages and emails call the app ("Open Acme Authenticator", "Acme Authenticator is your verification code"). Up to 60 characters: letters, digits, spaces and . , ( ) -.
  • Custom URL scheme: the scheme your app claims for tap-to-open links of the form yourscheme://link/…. Lowercase, 2 to 30 characters, starting with a letter. http, https, javascript, data, file, intent and sensecrypt are refused. Set the same value in the Android intent filter and the iOS URL Types.

Leave the scheme empty and no tap-to-open link is minted for your app. Sign-in pages fall back to the store buttons, and OS app links still open the app from a universal link. A link is never minted with the stock sensecrypt scheme for a custom app, because it would open an app the configuration no longer matches.

App attestation

The device payload fetch, the passkey face report and App Attest enrolment are checked against Google Play Integrity on Android and Apple App Attest on iOS, using the identity you register below.

ModeBehaviour
offNo attestation checks run. For an app that is not yet distributed through the stores.
auditChecks run and the verdict is logged, but no request is blocked. Use it to confirm the setup before enforcing.
enforceA request that fails attestation is rejected. The user sees a generic error; the reason goes only to the server log.

Attestation is per tenant. Nothing on an individual application can weaken it, so a relying party never gets an exemption. A custom app whose identity fields are still empty fails closed under enforce: it is never accepted on the strength of the stock app's identity. audit exists so that this phase is observable without locking anyone out.

Android

  • Package name: the application ID of your build.
  • Signing-certificate SHA-256 fingerprints: one per certificate, up to 10, as colon-separated hex (AA:BB:…). Include the debug certificate while testing and remove it for production.
  • Required verdict: Device integrity accepts MEETS_DEVICE_INTEGRITY or stronger. Strong integrity additionally requires hardware-backed proof and recent security updates on the device.
  • Play Store URL: where sign-in pages send Android users who do not have the app. Empty derives the public listing from the package name, which only resolves once the app is published. Use the internal-test opt-in link until then.

iOS

  • Team ID (10 characters) and Bundle ID. Together they form the App ID your attestations are checked against.
  • Require production App Attest environment: on by default. Development-signed builds attest against Apple's sandbox; turn this off only while testing pre-release builds.
  • App Store URL: where sign-in pages send iPhone users who do not have the app. Empty hides the App Store button.

Google service account

Upload the JSON key of the service account from Before you start. One key serves both Play Integrity (decoding Android attestation tokens) and push (FCM in your Firebase project). The key is stored sealed and is write-only: the page shows its client email and a fingerprint prefix, the API reports the client email, upload time and full fingerprint, and neither ever returns the key. You can replace or remove it.

Two rules to know:

  • Replacing the key with one from a different Google Cloud project asks for confirmation. Push and Play Integrity only work through the project your app build is registered in, so a key from another project would break both.
  • No key means no push. Pushes for a custom-app tenant are never sent through SenseCrypt's own project. Until you upload a key, CIBA and Face PKI requests for this tenant reach users by email only.

Sign-in links in QR codes, emails and pushes are issued on the app-link host, and that host publishes the two well-known documents the operating systems read:

  • https://<host>/.well-known/apple-app-site-association lists <team id>.<bundle id> for the path /v1/link/*.
  • https://<host>/.well-known/assetlinks.json lists your package name and signing fingerprints with the handle_all_urls and get_login_creds relations.

Both are served per host from what you registered, so there is nothing to upload. Each returns 404 until the matching identity fields are set.

Leave the picker on This tenant's host and links use the tenant's own hostname, https://<slug>.sensecrypt.com. Pin exactly that host in the app's associated domains and App Links filter. Pick a verified custom domain only when the app is built for that domain, for example one app shared by several tenants on one domain. The host must be one of the tenant's own hosts; anything else is refused with the 422 rule app_link_host.not_tenant_host.

A custom app's links are never issued on SenseCrypt's shared app host. That host publishes the stock app's identity, so a link there would leave the OS with no verified handler for your app.

Step 3: roll out

  1. Save with attestation in Audit. Install your build, enrol a user, sign in. The server log records each attestation verdict without blocking.

  2. Confirm the two well-known documents on your app-link host return your identity:

    curl https://<host>/.well-known/assetlinks.json
    curl https://<host>/.well-known/apple-app-site-association
  3. Confirm a push arrives: start a CIBA request for an enrolled user.

  4. Switch to Enforce once real traffic passes in audit.

Simulators and emulators cannot attest. Under enforce their device flows fail with attestation_failed; under off or audit they work.

Under the hood: the Mobile App endpoint

# authenticated with the mobile_app:read capability, on the tenant's host
curl https://{slug}.sensecrypt.com/v1/admin/mobile-app
# authenticated with the mobile_app:update capability
curl -X PATCH https://{slug}.sensecrypt.com/v1/admin/mobile-app \
  -H 'Content-Type: application/json' \
  -d '{
    "app_source": "custom",
    "confirm_app_switch": true,
    "app_name": "Acme Authenticator",
    "custom_scheme": "acme",
    "mode": "audit",
    "android_package_name": "com.example.authenticator",
    "android_cert_sha256": ["AA:BB:…"],
    "android_verdict_level": "device",
    "ios_team_id": "ABCDE12345",
    "ios_bundle_id": "com.example.authenticator",
    "google_service_account_json": "{…}"
  }'

PATCH is a partial update, and every rule is checked before the first write, so a refused request leaves the configuration untouched. The read model adds eligible_app_link_hosts (what the picker offers), the service-account metadata, and the deployment's store links for the stock app.

Statuserror or rule codeMeaning
409mobile_app_switch_confirmation_requiredapp_source changed without confirm_app_switch: true.
409mobile_app_project_change_confirmation_requiredThe new service-account key belongs to a different Google Cloud project; resend with confirm_project_change: true.
422app_link_host.not_tenant_hostThe host is not the tenant's slug host or one of its verified domains.
422field rulesMalformed app name, package name, fingerprint, team ID, bundle ID, scheme, store URL, app-link hostname or key, in the validation envelope.

Every save that changes something is recorded in the audit log as mobile_app.updated, naming the fields that changed and the key fingerprint, never the key.

See the API reference for GET /v1/admin/mobile-app and PATCH /v1/admin/mobile-app.

On this page