Skip to content

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=createexpo-auth-session via the request's extraParams, react-native-app-auth via additionalParameters/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 with prompt=create.

B — Redirect callback setup

  • Expo managed: AuthSession.makeRedirectUri({ scheme: 'com.yourapp' }) and set scheme in app.json. Use a custom URL scheme — no Universal Links / App Links.
  • Bare: configure redirectUrl to match the registered URI; register the scheme in Info.plist (iOS) and an <intent-filter> in AndroidManifest.xml (Android), exactly as the native sections above. react-native-app-auth launches ASWebAuthenticationSession / Custom Tabs for you.

C — Secure token storage

TokenWhere
Access tokenexpo-secure-store (Expo) or react-native-keychain (bare).
Refresh tokenSame secure store.

Use an AI prompt

Before pasting: edit the PLATFORMS line at the top of the prompt to match your project — mobile only, web only, or mobile + web. Each platform needs its own EntryIdP client registration (different redirect URI schemes), so tell the AI which client_id belongs 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.

EntryIdP — Synapser