{"id":16721,"library":"expo-auth-session","title":"Expo Auth Session","description":"Expo Auth Session is a foundational module within the Expo ecosystem for implementing web browser-based authentication flows, such as OAuth 2.0 and OpenID Connect, across Android, iOS, and web platforms. It provides a unified API to manage the complexities of these authentication methods, leveraging `expo-web-browser` for browser interaction and `expo-crypto` for secure operations like Proof Key for Code Exchange (PKCE), which is now the recommended grant type over implicit flow due to enhanced security. The current stable version is 55.0.15, with releases typically synchronized with major Expo SDK updates, occurring approximately three times per year. Key differentiators include its seamless integration with Expo development builds and managed workflow capabilities, simplifying the setup of deep linking and redirect URIs across various environments, and providing hooks like `useAuthRequest` for easy React component integration.","status":"active","version":"55.0.15","language":"javascript","source_language":"en","source_url":"https://github.com/expo/expo","tags":["javascript","react-native","expo","expo-auth-session","auth","oauth","authentication","auth-session","typescript"],"install":[{"cmd":"npm install expo-auth-session","lang":"bash","label":"npm"},{"cmd":"yarn add expo-auth-session","lang":"bash","label":"yarn"},{"cmd":"pnpm add expo-auth-session","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Peer dependency required by all React Native and Expo projects.","package":"react","optional":false},{"reason":"Peer dependency for all React Native and Expo projects.","package":"react-native","optional":false},{"reason":"Required peer dependency for secure operations, notably PKCE.","package":"expo-crypto","optional":false},{"reason":"Core dependency for opening and managing web browser sessions for authentication.","package":"expo-web-browser","optional":false}],"imports":[{"note":"The primary hook for initiating OAuth authorization requests in functional components. Use named import.","wrong":"const { useAuthRequest } = require('expo-auth-session');","symbol":"useAuthRequest","correct":"import { useAuthRequest } from 'expo-auth-session';"},{"note":"Essential for dynamically generating platform-aware redirect URIs. Always use named import.","wrong":"import AuthSession from 'expo-auth-session'; const redirectUri = AuthSession.makeRedirectUri();","symbol":"makeRedirectUri","correct":"import { makeRedirectUri } from 'expo-auth-session';"},{"note":"Used for imperative authentication flows (less common with hooks). This is a named export, not a property of a default export.","wrong":"import AuthSession from 'expo-auth-session'; AuthSession.startAsync(...);","symbol":"startAsync","correct":"import { startAsync } from 'expo-auth-session';"},{"note":"This function is crucial for dismissing the web browser session after authentication on web platforms and is exported from 'expo-web-browser', not 'expo-auth-session'.","wrong":"import { maybeCompleteAuthSession } from 'expo-auth-session';","symbol":"maybeCompleteAuthSession","correct":"import * as WebBrowser from 'expo-web-browser'; WebBrowser.maybeCompleteAuthSession();"}],"quickstart":{"code":"import React, { useEffect } from 'react';\nimport { Button, View, Text, StyleSheet } from 'react-native';\nimport * as WebBrowser from 'expo-web-browser';\nimport { makeRedirectUri, useAuthRequest } from 'expo-auth-session';\n\n// Required for WebBrowser.maybeCompleteAuthSession() on web\nWebBrowser.maybeCompleteAuthSession();\n\nconst discovery = {\n  authorizationEndpoint: 'https://github.com/login/oauth/authorize',\n  tokenEndpoint: 'https://github.com/login/oauth/access_token',\n  revocationEndpoint: 'https://api.github.com/applications/YOUR_CLIENT_ID/token',\n};\n\nexport default function GitHubLogin() {\n  const redirectUri = makeRedirectUri({\n    scheme: 'your-app-scheme',\n    // useProxy: true, // Only for testing with Expo Go, deprecated since SDK 48\n  });\n\n  const [request, response, promptAsync] = useAuthRequest(\n    {\n      clientId: process.env.GITHUB_CLIENT_ID ?? '', // Replace with your GitHub OAuth App Client ID\n      scopes: ['user', 'repo', 'gist'],\n      redirectUri,\n    },\n    discovery\n  );\n\n  useEffect(() => {\n    if (response?.type === 'success') {\n      const { code } = response.params;\n      console.log('Authorization code:', code);\n      // In a real app, you would exchange the 'code' for an access token on your backend server.\n      // For local testing, you might fetch it directly if safe and for demonstration.\n    } else if (response?.type === 'cancel') {\n      console.log('Authentication flow canceled.');\n    } else if (response?.type === 'error') {\n      console.error('Authentication error:', response.error?.message);\n    }\n  }, [response]);\n\n  return (\n    <View style={styles.container}>\n      <Text style={styles.header}>GitHub OAuth Example</Text>\n      <Button\n        disabled={!request}\n        title=\"Login with GitHub\"\n        onPress={() => promptAsync()}\n      />\n      {response && response.type === 'success' && (\n        <Text style={styles.status}>Logged in successfully!</Text>\n      )}\n      {response && response.type === 'error' && (\n        <Text style={styles.error}>Login failed: {response.error?.message}</Text>\n      )}\n    </View>\n  );\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n    justifyContent: 'center',\n    alignItems: 'center',\n    padding: 20,\n  },\n  header: {\n    fontSize: 24,\n    marginBottom: 20,\n  },\n  status: {\n    marginTop: 20,\n    color: 'green',\n  },\n  error: {\n    marginTop: 20,\n    color: 'red',\n  },\n});","lang":"typescript","description":"This example demonstrates a basic GitHub OAuth login flow using the `useAuthRequest` hook and `makeRedirectUri`. It showcases how to initiate the authentication process, handle success and error responses, and securely retrieve an authorization code for backend exchange. It also includes the necessary `WebBrowser.maybeCompleteAuthSession()` call for web compatibility and deep linking setup for native platforms."},"warnings":[{"fix":"Migrate to using direct deep links and universal links. Configure your OAuth provider with your app's custom scheme and/or universal link domain. Use `makeRedirectUri({ useProxy: false, scheme: 'your-app-scheme' })` or omit `useProxy` entirely. [13]","message":"The `useProxy` option for `makeRedirectUri` was deprecated in SDK 48 and removed in subsequent versions. Relying on `auth.expo.io` as a proxy for redirects is no longer recommended due to security and reliability concerns, especially with evolving browser cookie policies. [13]","severity":"breaking","affected_versions":">=48.0.0"},{"fix":"For robust development and production, use an Expo Development Build (custom dev client) or a standalone app. These allow you to define and use your app's specific deep linking scheme or universal link domain. [4, 5]","message":"Testing OAuth flows reliably in Expo Go can be challenging or impossible for providers that strictly validate redirect URIs (e.g., Google). Expo Go uses a generic `exp://` URI which often differs from what can be registered directly with OAuth providers, leading to `redirect_uri_mismatch` errors. [4, 5, 21]","severity":"gotcha","affected_versions":">=44.0.0"},{"fix":"Always verify the exact redirect URI generated by `makeRedirectUri()` in your console logs during development. Add this precise URI to your OAuth provider's 'Authorized redirect URIs' list. For native apps, ensure your `app.json` scheme matches. [14]","message":"It is critical to properly configure your redirect URIs with your OAuth provider (e.g., GitHub, Google) to exactly match what `makeRedirectUri` generates for your specific environment (development, production, web, iOS, Android). Mismatches will result in `Error 400: redirect_uri_mismatch` or similar. [5, 14, 20]","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Implement a backend API endpoint to handle the authorization code exchange for an access token. Send the authorization code received from `expo-auth-session` to your backend, where the secret is securely stored and used. Your client app then receives the access token from your backend. [4]","message":"Client secrets should *never* be exposed in client-side code. For OAuth flows requiring a client secret (e.g., Authorization Code Grant with PKCE, exchanging code for token), this exchange must happen on a secure backend server. [4]","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Add `WebBrowser.maybeCompleteAuthSession();` at the top level of your application (e.g., `App.tsx` or `_layout.tsx` for Expo Router) to ensure it's called early enough to dismiss any lingering authentication sessions. [4]","message":"On web platforms, `WebBrowser.maybeCompleteAuthSession()` must be called to ensure the authentication popup window closes correctly after the OAuth flow. Forgetting this can leave the browser window open. [2, 4]","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Double-check the redirect URI generated by `AuthSession.makeRedirectUri()` in your app's logs and ensure it is precisely added to the 'Authorized redirect URIs' list in your OAuth provider's settings (e.g., Google Cloud Console, GitHub OAuth App settings). Ensure your app's scheme in `app.json` is correctly set and matches for native builds.","cause":"The redirect URI registered with the OAuth provider does not exactly match the URI generated by `expo-auth-session`.","error":"Error 400: redirect_uri_mismatch"},{"fix":"Remove `useProxy: true` from your `makeRedirectUri` options and configure direct deep linking/universal links with your OAuth provider. If forced to use an older SDK and proxy, ensure your project's full name (`@owner/slug`) is provided to `projectNameForProxy`. [23]","cause":"You are attempting to use the deprecated `useProxy: true` option without providing the `projectNameForProxy` or after the proxy service has been removed/disabled for your Expo SDK version. [23]","error":"Cannot use the AuthSession proxy because the project full name is not defined."},{"fix":"Ensure your app's scheme is correctly defined in `app.json` and in your native project configurations. Test with a custom Expo Development Build or standalone app, rather than Expo Go, as it provides more control over deep linking. Verify `WebBrowser.maybeCompleteAuthSession()` is called.","cause":"This generic error often indicates a problem with the deep linking mechanism preventing the browser from redirecting back to your application, especially after authentication on the proxy server. This can be due to incorrect scheme setup, issues with `useProxy`, or browser cookie policies. [19, 21]","error":"Something went wrong trying to finish signing in. Please close this screen to go back to the app."},{"fix":"While user cancellation is expected, if it happens unexpectedly, review redirect URI configurations, network connectivity, and ensure deep linking is correctly set up. For iOS, ensure `WebBrowser.maybeCompleteAuthSession()` is active and prompt for permissions is clear.","cause":"The user closed the browser or cancelled the authentication prompt, or the redirect URI was misconfigured leading to an inability to return to the app.","error":"OAuth flow canceled"}],"ecosystem":"npm"}