# OAuth uses SFSafariViewController or Custom Tabs, not embedded WebView

- **Pattern:** `ab-000509` (`app-store-review-blockers.technical-requirements.webview-oauth-secure`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000509
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000509)

## Why it matters

An embedded `WebView` used for OAuth credential entry violates Apple's guideline 5.1.1 and the OAuth 2.0 security model: the app can read the WebView's DOM content, giving it access to the user's credentials on the OAuth provider's login page. CWE-522 (insufficiently protected credentials) applies directly. Google, Apple, and GitHub explicitly prohibit embedded WebView OAuth because it enables phishing — an app can silently capture the username and password typed into what appears to be Google's login form. The correct implementations (`SFSafariViewController`, `AuthenticationSession`, Chrome Custom Tabs) operate in a separate process the app cannot read.

## Severity rationale

Low because the pattern requires an OAuth flow to be present and is not an immediate binary rejection in all cases, but Apple has started rejecting it more consistently under guideline 5.1.1.

## Remediation

Replace any WebView OAuth flow with `expo-auth-session`, which uses `SFSafariViewController` on iOS and Chrome Custom Tabs on Android.

```tsx
// src/screens/LoginScreen.tsx
import * as Google from 'expo-auth-session/providers/google';

export default function LoginScreen() {
  const [request, response, promptAsync] = Google.useAuthRequest({
    clientId: process.env.EXPO_PUBLIC_GOOGLE_CLIENT_ID,
  });

  useEffect(() => {
    if (response?.type === 'success') {
      const { authentication } = response;
      // exchange authentication.accessToken for your session
    }
  }, [response]);

  return <Button title="Sign in with Google" onPress={() => promptAsync()} />;
}
```

For Apple Sign In (required when offering any third-party sign-in on iOS), use `expo-apple-authentication`. Never use a `<WebView source={{ uri: 'https://accounts.google.com/...' }} />` for credential entry.

## Detection

- **ID:** `webview-oauth-secure`
- **Severity:** `low`
- **What to look for:** Count all relevant instances and enumerate each. Search for OAuth flows in the codebase. Look for `WebView` components used for authentication (check props like `source={{ uri: 'https://accounts.google.com/...' }}` or similar OAuth provider URLs in WebView). The correct pattern is: `expo-auth-session` (Expo), `react-native-app-auth` (React Native), `SFSafariViewController` (iOS native), `Chrome Custom Tabs` (Android). Look for `WebView` imports in authentication-related files: `src/screens/auth/`, `src/components/auth/`, login flows. Check if the WebView has access to the user's session cookies (which would allow the app to read OAuth tokens from the page content).
- **Pass criteria:** OAuth authentication flows use system browser components (`SFSafariViewController` / `AuthenticationSession` on iOS, `Chrome Custom Tabs` on Android) or well-established auth libraries (`expo-auth-session`, `react-native-app-auth`). At least 1 implementation must be verified. No embedded WebView is used for OAuth credential entry.
- **Fail criteria:** OAuth flow embeds a `WebView` component that loads an OAuth provider's login page (Google, Apple, Facebook, GitHub, etc.) — this allows the app to read the WebView's content, which is a security violation.
- **Skip (N/A) when:** App has no web-based OAuth authentication (uses only email/password, phone number, or anonymous auth without web OAuth providers).
- **Detail on fail:** `"Google OAuth is implemented with an embedded WebView loading accounts.google.com — use expo-auth-session or react-native-app-auth instead"`
- **Remediation:** Apple guideline 5.1.1 prohibits capturing user credentials from OAuth provider sites via WebView.
  1. Replace embedded WebView OAuth with `expo-auth-session`:
     ```tsx
     import * as Google from 'expo-auth-session/providers/google';
     const [request, response, promptAsync] = Google.useAuthRequest({ ... });
     ```
  2. Or use `react-native-app-auth` for non-Expo projects
  3. For Apple Sign In, use `expo-apple-authentication` or the `AuthenticationServices` framework directly — this is required for any app offering third-party sign-in on iOS

## External references

- cwe CWE-522
- external apple-guideline-5.1.1-oauth-webview — https://developer.apple.com/app-store/review/guidelines/#data-collection-and-storage

Taxons: regulatory-conformance, access-control

HTML version: https://auditbuffet.com/patterns/ab-000509
