MobiWeb Help

OAuth Integration (Google, Facebook, OIDC)

The app runs social/OAuth sign-in natively on both platforms. Google (and most identity providers) reject OAuth inside an embedded WebView with disallowed_useragent, so the authorization step must run in a system browser and return to the app via a redirect.

Both platforms use the same thin model:

  • Native performs the authorization step only — it opens the provider in a system browser (with PKCE where supported) and receives the authorization code.

  • Native does not exchange the code. It hands the (unconsumed) code — plus the PKCE code_verifier — back to the web app at /_oauth_callback. The web app completes the token exchange — server-side for any backend, or client-side only for public PKCE clients (Google); Facebook is confidential and must exchange with its app secret server-side.

The flow works for any kind of website — server-rendered / MPA or client-rendered — not just a SPA. This keeps the web app the single owner of the token exchange and gives one identical contract on iOS and Android.

Flow

  1. The web app navigates to the provider's authorization URL as it would on the web (e.g. https://accounts.google.com/o/oauth2/v2/auth?client_id=<web-client>&…).

  2. Native intercepts that navigation (main-frame nav, and window.open/target=_blank popups). It carries over scope, state, and a small allowlist of standard params the web app may have set (access_type, prompt, login_hint, include_granted_scopes, nonce, hd), and substitutes its own client_id/redirect_uri/response_type/ PKCE — the outbound URL is only a trigger.

  3. Native restarts the flow in a system browser using the native client id and a custom-scheme redirect, adding PKCE (code_challenge) for Google.

    • iOS: ASWebAuthenticationSession(url:callbackURLScheme:).

    • Android: AppAuth Custom Tab; the redirect is caught by AppAuth's RedirectUriReceiverActivity.

  4. On completion native builds <initialUrl>/_oauth_callback?… and loads it in the WebView.

  5. The web app's /_oauth_callback handler completes the exchange against the native client — server-side for any backend, or client-side for a public PKCE client (Google).

Token exchangeProviderSystem browserNativeWeb appToken exchangeProviderSystem browserNativeWeb appintercept, swap in native client_id, add PKCEno native token exchange, code stays unconsumedexchange code for tokens with the native clientnavigate to provider auth URLopen auth URL in system browserauthorization requestuser authenticates and consentsredirect back with code and stateload /_oauth_callback with code and code_verifierhand over callback paramssession established

The /_oauth_callback contract

Native loads this URL in the WebView on completion (identical shape on both platforms):

<initialUrl>/_oauth_callback ?provider=<google|facebook|…> &platform=<ios|android> &client_id=<native client id actually used> &redirect_uri=<custom-scheme redirect actually used> &code=<authorization code, UNCONSUMED> &state=<original state> &code_verifier=<PKCE verifier — present when the provider uses PKCE, e.g. Google>

On failure: …&error=<description> and no code.

Param

Notes

provider

google, facebook, …

platform

ios or android — lets the backend pick the right client config

client_id

The native client id native used (not the web client id the web app started with)

redirect_uri

The custom-scheme redirect native used; the backend must send the same value in the exchange

code

Authorization code — not exchanged natively, so still valid

state

Echo of the state the web app sent; verify it

code_verifier

PKCE verifier the exchange needs to complete (Google). Absent for non-PKCE providers

error

Present only on failure

Website changes required

The web app needs no change to trigger OAuth — it keeps navigating to the provider URL as it does on the web. Any kind of site works (server-rendered / MPA or client-rendered, not just a SPA). The changes are on the callback + token exchange:

  1. /_oauth_callback route — accept the params above (new: platform, client_id, redirect_uri, code_verifier) and complete the token exchange (below). A server-rendered / MPA site handles this route server-side; a client-rendered app via its router.

  2. Token exchange — server-side (any backend), or client-side only for public PKCE clients:

    • Use the native client_id from the callback (not the web client).

    • Send the PKCE code_verifier and the exact custom-scheme redirect_uri from the callback.

    • Send no client secret for Google (iOS/Android are public clients) — so a backend-less site can exchange Google client-side. Facebook is confidential — exchange with the Facebook app secret server-side (no PKCE); a backend-less site cannot support Facebook.

    • Map provider + platform → the correct client config (or trust the passed client_id/redirect_uri).

  3. Verify state and treat error as a failed sign-in.

  4. The callback arrives as a full page load to <initialUrl>/_oauth_callback (native does a full navigation, not a client-side route push), and always on the configured initialUrl host. A JS-driven app must persist any pre-login state in storage/cookies, not memory — it cannot assume the callback is an in-app route transition.

Provider / cloud configuration

  • Google

    • Create a dedicated iOS OAuth client and an Android OAuth client (package name + signing SHA-1) in Google Cloud.

    • iOS: put the real CLIENT_ID/REVERSED_CLIENT_ID in ios/App/App/GoogleService-Info.plist. The custom-scheme redirect is <REVERSED_CLIENT_ID>:/oauth2redirect.

    • Android: set the redirect scheme to the Android client's reversed client id (see build flags below).

  • Facebook — register iOS + Android platforms in the Facebook app; set the app id in the FACEBOOK_APP_ID (iOS) / provider console; the redirect is fb<appid>://authorize.

  • Register every custom-scheme redirect URI in the respective provider console.

Build flags

Native OAuth is off by default on both platforms; enable it once the real client ids + redirect scheme are configured.

Android (android/app/build.gradle)

Field

Default

Effect

OAUTH_ENABLED

false

Activates the AppAuth interception in MainActivity

OAUTH_REDIRECT_SCHEME

applicationId

Custom-scheme for the redirect. Must equal the appAuthRedirectScheme manifest placeholder (both are driven by the single oauthRedirectScheme variable). Set to the Google reversed client id when using Google

DEEPLINK_ENABLED

false

Unrelated to OAuth return (the Custom-Tab result returns via onActivityResult); only needed for host deep links

iOS (user-defined build settings in project.pbxproj, surfaced via Info.plist)

Build setting

Info.plist key

Default

Effect

OAUTH_ENABLED

OAuthEnabled ($(OAUTH_ENABLED))

NO

Activates OAuthCoordinator interception

FACEBOOK_APP_ID

FacebookAppID ($(FACEBOOK_APP_ID))

(empty)

Facebook app id; also forms the fb<appid> redirect scheme

Google config on iOS comes from GoogleService-Info.plist (CLIENT_ID/REVERSED_CLIENT_ID).

Platform notes

  • iOS — OAuthCoordinator.swift drives ASWebAuthenticationSession. The callback scheme is not registered in CFBundleURLTypes: ASWebAuthenticationSession catches its own callbackURLScheme, and registering the same scheme would risk the redirect routing to application(_:open:) instead of the session callback. PKCE uses CryptoKit (SHA-256). No new pod.

  • Android — MainActivity uses AppAuth (net.openid:appauth), authorization step only. PKCE is added by AppAuth automatically; the verifier is read back from AuthorizationResponse.request.codeVerifier and forwarded to the web app. No native token storage.

22 July 2026