Appearance
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
AuthorizationRequestas usual →/authorize. - Enrol: add
prompt=createviaAuthorizationRequest.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 withprompt=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 yourandroid:schemeto the redirect activity inAndroidManifest.xml. The redirect URI (e.g.com.yourapp://callback) must exactly match the registered value.
C — Secure token storage
| Token | Where |
|---|---|
| Access token | EncryptedSharedPreferences backed by the Android Keystore. |
| Refresh token | Same 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.