Appearance
React Native (Expo or bare)
Use a public client with PKCE. Expo managed → expo-auth-session; bare → react-native-app-auth.
Pick your path: Follow the guide below, or jump to the AI prompt.
Follow the guide
A — Login vs enrol
- Login: start the auth request as usual →
/authorize. - Enrol: add
prompt=create—expo-auth-sessionvia the request'sextraParams,react-native-app-authviaadditionalParameters→/authorize?prompt=create.
Choose one of:
- Option 1 (recommended): "Sign in" and "Create account" buttons, the latter passing
prompt=create. - Option 2: attempt login; on
error=access_denied+error_description=user_not_registered, retry withprompt=create.
B — Redirect callback setup
- Expo managed:
AuthSession.makeRedirectUri({ scheme: 'com.yourapp' })and setschemeinapp.json. Use a custom URL scheme — no Universal Links / App Links. - Bare: configure
redirectUrlto match the registered URI; register the scheme inInfo.plist(iOS) and an<intent-filter>inAndroidManifest.xml(Android), exactly as the native sections above.react-native-app-authlaunchesASWebAuthenticationSession/ Custom Tabs for you.
C — Secure token storage
| Token | Where |
|---|---|
| Access token | expo-secure-store (Expo) or react-native-keychain (bare). |
| Refresh token | Same secure store. |
Use an AI prompt
Before pasting: edit the
PLATFORMSline at the top of the prompt to match your project —mobile only,web only, ormobile + web. Each platform needs its own EntryIdP client registration (different redirect URI schemes), so tell the AI whichclient_idbelongs to which.
<!-- PLATFORMS: mobile only | web only | mobile + web — edit before pasting -->
Add EntryIdP biometric OIDC login to this app.
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.
Each platform has its own EntryIdP client registration and client_id (different redirect
URI schemes — do not share one client_id across platforms). Read all client_ids and the
issuer from env vars — never hardcode.
Issuer: https://idp-test.entryidp.com (use the issuer from my client registration).
Before writing code:
1. Confirm which platforms are in scope from the PLATFORMS line above.
2. For React Native mobile: determine Expo managed vs bare from app.json / package.json.
Expo managed → use expo-auth-session. Bare → use react-native-app-auth.
3. For web: use the browser's native fetch/redirect or a library such as oidc-client-ts
or AppAuth-JS — do not use a React Native auth library on web.
Implementation — applies to ALL platforms:
- Authorization Code + PKCE only (S256). Never implicit flow or response_type=token.
- PUBLIC client: no client_secret anywhere.
- Discover endpoints from {issuer}/.well-known/openid-configuration.
Mobile-specific:
- Redirect URI is a CUSTOM URL SCHEME (e.g. com.yourapp://auth/callback).
Expo: AuthSession.makeRedirectUri({ scheme: 'com.yourapp' }) + set scheme in app.json.
Bare: set redirectUrl and register the scheme in Info.plist (iOS) +
AndroidManifest.xml intent-filter (Android).
- EntryIdP does NOT support Universal Links / App Links — do not configure
apple-app-site-association or assetlinks.json.
- Store tokens with expo-secure-store (Expo) or react-native-keychain (bare). Never
AsyncStorage.
Web-specific:
- Redirect URI is an https:// URL (e.g. https://yourapp.com/auth/callback).
- Store the access token in memory only. Store the refresh token in an httpOnly cookie
or, if SPA-only with no backend, in sessionStorage — never localStorage.
- Handle the callback in the page mounted at the redirect URI; exchange the code
server-side if a backend is present, otherwise client-side via the auth library.
Login vs enrol (EntryIdP-specific — same on all platforms):
- "Sign in" → standard request (login of an existing face).
- "Create account" → add prompt=create — first-time face enrolment.
(expo: extraParams; app-auth: additionalParameters; oidc-client-ts/AppAuth-JS:
extraQueryParams / customRequestParameters)
- On 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 on any platform.
- Mobile: custom URL scheme redirects only; no Universal Links / App Links.
- Web: https:// redirects only; no custom schemes.
- No implicit flow; 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.