Skip to content

Android — native (Kotlin + AppAuth-Android)

Use a public client with PKCE. Drive the flow with AppAuth-Android.

Pick your path: Follow the guide below, or jump to the AI prompt.

Follow the guide

A — Login vs enrol

  • Login: build the AuthorizationRequest as usual → /authorize.
  • Enrol: add prompt=create via AuthorizationRequest.Builder.setAdditionalParameters(mapOf("prompt" to "create"))/authorize?prompt=create.

Choose one of:

  • Option 1 (recommended): "Sign in" and "Create account" buttons, the latter setting prompt=create.
  • Option 2: attempt login; if the result carries error=access_denied + error_description=user_not_registered, retry with prompt=create.

B — Redirect callback setup

  • Use Chrome Custom Tabs (AppAuth uses them automatically). Do not use a WebView.
  • Use a custom URL scheme. EntryIdP does not host assetlinks.json, so App Links are not available.
  • Add an <intent-filter> with your android:scheme to the redirect activity in AndroidManifest.xml. The redirect URI (e.g. com.yourapp://callback) must exactly match the registered value.

C — Secure token storage

TokenWhere
Access tokenEncryptedSharedPreferences backed by the Android Keystore.
Refresh tokenSame store (persist AppAuth's serialized AuthState).

Use an AI prompt

Add EntryIdP biometric OIDC login to this native Android app (Kotlin) using AppAuth-Android.

EntryIdP is an OpenID Connect provider. Users authenticate ONLY with a face liveness
check — no typed credentials, OTPs, or social logins. Do not build any sign-in form or
credential-entry UI.
Issuer: https://idp-test.entryidp.com (use the issuer from my client registration; read
from config — never hardcode in logic).

Before writing code:
1. Add net.openid:appauth to build.gradle if missing. Also add inside defaultConfig:
     manifestPlaceholders["appAuthRedirectScheme"] = "<your-custom-scheme>"
   This is required — AppAuth's bundled manifest uses this placeholder and the Gradle
   manifest merger will fail without it.
2. Open AndroidManifest.xml and declare net.openid.appauth.RedirectUriReceiverActivity
   with tools:node="merge" and an <intent-filter> for your custom scheme.

Implementation:
- Authorization Code + PKCE only. Never implicit flow or response_type=token.
- PUBLIC client: no client_secret anywhere. Use NoClientAuthentication.INSTANCE when
  calling performTokenRequest.
- PKCE: AppAuth does NOT add PKCE automatically. Call
  setCodeVerifier(CodeVerifierUtil.generateRandomCodeVerifier()) on the request builder
  (S256 challenge is derived from the verifier automatically).
- Discovery: use AuthorizationServiceConfiguration.fetchFromIssuer(Uri.parse(issuer)) and
  build the request from the config it hands back. EntryIdP's discovery document carries
  all six fields AppAuth treats as mandatory (issuer, authorization_endpoint, jwks_uri,
  response_types_supported, subject_types_supported,
  id_token_signing_alg_values_supported), so this succeeds — do not hardcode endpoints.
  Only if you are pinned to an old AppAuth release and actually hit
  MissingArgumentException, fall back to fetching
  {issuer}/.well-known/openid-configuration yourself on a background thread and
  constructing AuthorizationServiceConfiguration(authEndpointUri, tokenEndpointUri).
- Use Chrome Custom Tabs (AppAuth default). Do NOT use a WebView for auth.
- Redirect URI is a CUSTOM URL SCHEME (e.g. com.yourapp://callback) declared as an
  <intent-filter> in AndroidManifest.xml. EntryIdP does NOT support App Links — do not
  configure assetlinks.json.
- Persist AppAuth AuthState in EncryptedSharedPreferences (Android Keystore-backed). It
  holds both access and refresh tokens.

Login vs enrol (EntryIdP-specific):
- "Sign in" → standard AuthorizationRequest (login of an existing face).
- "Create account" → setAdditionalParameters(mapOf("prompt" to "create"))
  (first-time face enrolment).
- If the result is error=access_denied with error_description=user_not_registered,
  retry with prompt=create.

Guardrails:
- No sign-in form, OTP, or social-login UI.
- No client_secret.
- No WebView for authentication; no App Links.
- Custom URL scheme redirect only; endpoints from discovery, not hardcoded.

Done? Run through the pre-launch checklist before you ship, and see Refresh tokens rotate if you requested offline_access.

EntryIdP — Synapser