AuthSession
API, refer to those docs for more information on the API.WebBrowser.maybeCompleteAuthSession()
to dismiss the web popup. If you forget to add this then the popup window will not close.AuthSession.makeRedirectUri()
this does a lot of the heavy lifting involved with universal platform support. Behind the scenes it uses expo-linking
.AuthSession.useAuthRequest()
, the hook allows for async setup which means mobile browsers won't block the authentication.request
is defined.promptAsync
in a user-interaction on web.IdentityServer 4
OAuth 2 | OpenID
Azure
OAuth 2 | OpenID
Apple
iOS Only
Coinbase
OAuth 2
Dropbox
OAuth 2
OAuth 2
Fitbit
OAuth 2
Firebase Phone
Recaptcha
Github
OAuth 2
OAuth 2 | OpenID
Imgur
OAuth 2
Okta
OAuth 2 | OpenID
OAuth 2
Slack
OAuth 2
Spotify
OAuth 2
Strava
OAuth 2
Twitch
OAuth 2
Uber
OAuth 2
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
More Info | OpenID | Required | Available |
offline_access
isn't included then no refresh token will be returned.import * as React from 'react'; import { Button, Text, View } from 'react-native'; import * as AuthSession from 'expo-auth-session'; import * as WebBrowser from 'expo-web-browser'; WebBrowser.maybeCompleteAuthSession(); const useProxy = true; const redirectUri = AuthSession.makeRedirectUri({ native: 'your.app://redirect', useProxy, }); export default function App() { const discovery = AuthSession.useAutoDiscovery('https://demo.identityserver.io'); // Create and load an auth request const [request, result, promptAsync] = AuthSession.useAuthRequest( { clientId: 'native.code', redirectUri, scopes: ['openid', 'profile', 'email', 'offline_access'], }, discovery ); return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Button title="Login!" disabled={!request} onPress={() => promptAsync({ useProxy })} /> {result && <Text>{JSON.stringify(result, null, 2)}</Text>} </View> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OpenID | Supported | Available |
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); export default function App() { // Endpoint const discovery = useAutoDiscovery('https://login.microsoftonline.com/<TENANT_ID>/v2.0'); // Request const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', scopes: ['openid', 'profile', 'email', 'offline_access'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OAuth 2.0 | Supported | Not Available |
@
in their redirect URIs.redirectUri
requires 2 slashes (://
).import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://www.coinbase.com/oauth/authorize', tokenEndpoint: 'https://api.coinbase.com/oauth/token', revocationEndpoint: 'https://api.coinbase.com/oauth/revoke', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', scopes: ['wallet:accounts:read'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OAuth 2.0 | Not Supported | Not Available |
usePKCE: false
) otherwise you'll get an error about code_challenge
being included in the query string.responseType: ResponseType.Code
is used (default behavior) the redirectUri
must be https
. This means that code exchange auth cannot be done on native without useProxy
enabled.ResponseType.Code
) will only work in native with useProxy: true
.import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button, Platform } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://www.dropbox.com/oauth2/authorize', tokenEndpoint: 'https://www.dropbox.com/oauth2/token', }; const useProxy = Platform.select({ web: false, default: true }); export default function App() { const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', // There are no scopes so just pass an empty array scopes: [], // Dropbox doesn't support PKCE usePKCE: false, // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', useProxy, }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync({ useProxy }); }} /> ); }
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://www.dropbox.com/oauth2/authorize', tokenEndpoint: 'https://www.dropbox.com/oauth2/token', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { responseType: ResponseType.Token, clientId: 'CLIENT_ID', // There are no scopes so just pass an empty array scopes: [], // Dropbox doesn't support PKCE usePKCE: false, // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { access_token } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
More Info | OAuth | Supported | Not Available |
fb
followed by the project ID (ex: fb145668956753819
):facebookScheme: 'fb<YOUR FBID>'
to your app.config.js
or app.json
expo build:ios
& expo build:android
.npx uri-scheme add fb<YOUR FBID>
yarn ios
& yarn android
native
redirect URI must be formatted like fbYOUR_NUMERIC_ID://authorize
No redirect URI in the params: No redirect present in URI
.://authorize
then you will get an error like: Can't Load URL: The domain of this URL isn't included in the app's domains. To be able to load this URL, add all domains and subdomains of your app to the App Domains field in your app settings.
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import * as Facebook from 'expo-auth-session/providers/facebook'; import { ResponseType } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); export default function App() { const [request, response, promptAsync] = Facebook.useAuthRequest({ clientId: '<YOUR FBID>', responseType: ResponseType.Code, }); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import * as Facebook from 'expo-auth-session/providers/facebook'; import { ResponseType } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); export default function App() { const [request, response, promptAsync] = useAuthRequest({ clientId: '<YOUR FBID>', }); React.useEffect(() => { if (response?.type === 'success') { const { authentication } = response; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import * as Facebook from 'expo-auth-session/providers/facebook'; import { ResponseType } from 'expo-auth-session'; import firebase from 'firebase'; import { Button } from 'react-native'; // Initialize Firebase if (!firebase.apps.length) { firebase.initializeApp({ /* Config */ }); } WebBrowser.maybeCompleteAuthSession(); export default function App() { const [request, response, promptAsync] = useAuthRequest({ responseType: ResponseType.Token, clientId: '<YOUR FBID>', }); React.useEffect(() => { if (response?.type === 'success') { const { access_token } = response.params; const credential = firebase.auth.FacebookAuthProvider.credential(access_token); // Sign in with the credential from the Facebook user. firebase.auth().signInWithCredential(credential); } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OAuth 2.0 | Supported | Not Available |
exp://localhost:19000/--/*
https://auth.expo.io/@you/your-app
com.your.app://*
https://yourwebsite.com/*
redirectUri
requires 2 slashes (://
).import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button, Platform } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://www.fitbit.com/oauth2/authorize', tokenEndpoint: 'https://api.fitbit.com/oauth2/token', revocationEndpoint: 'https://api.fitbit.com/oauth2/revoke', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', scopes: ['activity', 'sleep'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session'; import { Button, Platform } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); const useProxy = Platform.select({ web: false, default: true }); // Endpoint const discovery = { authorizationEndpoint: 'https://www.fitbit.com/oauth2/authorize', tokenEndpoint: 'https://api.fitbit.com/oauth2/token', revocationEndpoint: 'https://api.fitbit.com/oauth2/revoke', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { responseType: ResponseType.Token, clientId: 'CLIENT_ID', scopes: ['activity', 'sleep'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ useProxy, // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { access_token } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync({ useProxy }); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OAuth 2.0 | Supported | Not Available |
exp://localhost:19000/--/*
https://auth.expo.io/@you/your-app
com.your.app://*
https://yourwebsite.com/*
redirectUri
requires 2 slashes (://
).revocationEndpoint
is dynamic and requires your config.clientId
.import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://github.com/login/oauth/authorize', tokenEndpoint: 'https://github.com/login/oauth/access_token', revocationEndpoint: 'https://github.com/settings/connections/applications/<CLIENT_ID>', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', scopes: ['identity'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OpenID | Supported | Available |
Be sure to install the peerDependencyyarn add expo-application
expoClientId
: Proxy client ID for use in the Expo Go on iOS and Android.iosClientId
: iOS native client ID for use in standalone, bare-workflow, and custom clients.androidClientId
: Android native client ID for use in standalone, bare-workflow, and custom clients.webClientId
: Expo web client ID for use in the browser.expo login
. This will be part of the redirect URL.expoClientId
.iosClientId
.ios.bundleIdentifier
in your app.json
.npx uri-scheme add <your bundle id> --ios
expo eject
and run yarn ios
expo client:ios
expo build:ios
app.json
you'll need to rebuild the native app.androidClientId
.android.package
in your app.json
.android.package
(ex. com.myname.mycoolapp:/
).npx uri-scheme add <your android.package> --android
expo credentials:manager -p android
then select "Update upload Keystore" -> "Generate new keystore" -> "Go back to experience overview"A1:B2:C3
but longer.expo eject
and run yarn ios
expo build:android
app.json
you'll need to rebuild the native app.webClientId
.expo start:web --https
.import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import * as Google from 'expo-auth-session/providers/google'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); export default function App() { const [request, response, promptAsync] = Google.useAuthRequest({ expoClientId: 'GOOGLE_GUID.apps.googleusercontent.com', iosClientId: 'GOOGLE_GUID.apps.googleusercontent.com', androidClientId: 'GOOGLE_GUID.apps.googleusercontent.com', webClientId: 'GOOGLE_GUID.apps.googleusercontent.com', }); React.useEffect(() => { if (response?.type === 'success') { const { authentication } = response; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
YOUR_GUID
with your "Web client ID" and open this link:WebBrowser.maybeCompleteAuthSession();
from the root URL of your app.import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { ResponseType } from 'expo-auth-session'; import * as Google from 'expo-auth-session/providers/google'; import firebase from 'firebase'; import { Button } from 'react-native'; // Initialize Firebase if (!firebase.apps.length) { firebase.initializeApp({ /* Config */ }); } WebBrowser.maybeCompleteAuthSession(); export default function App() { const [request, response, promptAsync] = Google.useIdTokenAuthRequest( { clientId: 'Your-Web-Client-ID.apps.googleusercontent.com', }, ); React.useEffect(() => { if (response?.type === 'success') { const { id_token } = response.params; const credential = firebase.auth.GoogleAuthProvider.credential(id_token); firebase.auth().signInWithCredential(credential); } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OAuth 2.0 | Supported | Not Available |
clientId
).import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button, Platform } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); const discovery = { authorizationEndpoint: 'https://api.imgur.com/oauth2/authorize', tokenEndpoint: 'https://api.imgur.com/oauth2/token', }; export default function App() { // Request const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', clientSecret: 'CLIENT_SECRET', redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'com.myname.myapp://redirect', }), // imgur requires an empty array scopes: [], }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session'; import { Button, Platform } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); const discovery = { authorizationEndpoint: 'https://api.imgur.com/oauth2/authorize', tokenEndpoint: 'https://api.imgur.com/oauth2/token', }; export default function App() { // Request const [request, response, promptAsync] = useAuthRequest( { responseType: ResponseType.Token, clientId: 'CLIENT_ID', redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'com.myname.myapp://redirect', }), scopes: [], }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { access_token } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Sign-up > Applications | OpenID | Supported | Available |
redirectUri
, Okta will provide you with one.import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session'; import { Button, Platform } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); const useProxy = Platform.select({ web: false, default: true }); export default function App() { // Endpoint const discovery = useAutoDiscovery('https://<OKTA_DOMAIN>.com/oauth2/default'); // Request const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', scopes: ['openid', 'profile'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'com.okta.<OKTA_DOMAIN>:/callback', useProxy, }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync({ useProxy }); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OAuth 2.0 | Supported | Not Available |
exp://localhost:19000/--/*
https://auth.expo.io/@you/your-app
com.your.app://*
https://yourwebsite.com/*
redirectUri
requires 2 slashes (://
).import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://www.reddit.com/api/v1/authorize.compact', tokenEndpoint: 'https://www.reddit.com/api/v1/access_token', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', scopes: ['identity'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
installed
option for your app on Reddit to use implicit grant.import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://www.reddit.com/api/v1/authorize.compact', tokenEndpoint: 'https://www.reddit.com/api/v1/access_token', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { responseType: ResponseType.Token, clientId: 'CLIENT_ID', scopes: ['identity'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { access_token } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OAuth 2.0 | Supported | Not Available |
redirectUri
requires 2 slashes (://
).redirectUri
can be defined under the "OAuth & Permissions" section of the website.clientId
and clientSecret
can be found in the "App Credentials" section.revocationEndpoint
is not available.import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://slack.com/oauth/authorize', tokenEndpoint: 'https://slack.com/api/oauth.access', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', scopes: ['emoji:read'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OAuth 2.0 | Supported | Not Available |
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://accounts.spotify.com/authorize', tokenEndpoint: 'https://accounts.spotify.com/api/token', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', scopes: ['user-read-email', 'playlist-modify-public'], // In order to follow the "Authorization Code Flow" to fetch token after authorizationEndpoint // this must be set to false usePKCE: false, // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://accounts.spotify.com/authorize', tokenEndpoint: 'https://accounts.spotify.com/api/token', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { responseType: ResponseType.Token, clientId: 'CLIENT_ID', scopes: ['user-read-email', 'playlist-modify-public'], // In order to follow the "Authorization Code Flow" to fetch token after authorizationEndpoint // this must be set to false usePKCE: false, // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { access_token } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OAuth 2.0 | Supported | Not Available |
com.bacon.myapp://redirect
the domain would be redirect
.import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://www.strava.com/oauth/mobile/authorize', tokenEndpoint: 'https://www.strava.com/oauth/token', revocationEndpoint: 'https://www.strava.com/oauth/deauthorize', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', scopes: ['activity:read_all'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone // the "redirect" must match your "Authorization Callback Domain" in the Strava dev console. native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
const { accessToken } = await AuthSession.exchangeCodeAsync( { clientId: request?.clientId, redirectUri, code: result.params.code, extraParams: { // You must use the extraParams variation of clientSecret. // Never store your client secret on the client. client_secret: 'CLIENT_SECRET', }, }, { tokenEndpoint: 'https://www.strava.com/oauth/token' } );
Website | Provider | PKCE | Auto Discovery | Scopes |
---|---|---|---|---|
Get your Config | OAuth | Supported | Not Available | Info |
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://id.twitch.tv/oauth2/authorize', tokenEndpoint: 'https://id.twitch.tv/oauth2/token', revocationEndpoint: 'https://id.twitch.tv/oauth2/revoke', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), scopes: ['openid', 'user_read', 'analytics:read:games'], }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://id.twitch.tv/oauth2/authorize', tokenEndpoint: 'https://id.twitch.tv/oauth2/token', revocationEndpoint: 'https://id.twitch.tv/oauth2/revoke', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { responseType: ResponseType.Token, clientId: 'CLIENT_ID', // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), scopes: ['openid', 'user_read', 'analytics:read:games'], }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { access_token } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
Website | Provider | PKCE | Auto Discovery |
---|---|---|---|
Get Your Config | OAuth 2.0 | Supported | Not Available |
redirectUri
requires 2 slashes (://
).scopes
can be difficult to get approved.import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://login.uber.com/oauth/v2/authorize', tokenEndpoint: 'https://login.uber.com/oauth/v2/token', revocationEndpoint: 'https://login.uber.com/oauth/v2/revoke', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { clientId: 'CLIENT_ID', scopes: ['profile', 'delivery'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { code } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session'; import { Button } from 'react-native'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://login.uber.com/oauth/v2/authorize', tokenEndpoint: 'https://login.uber.com/oauth/v2/token', revocationEndpoint: 'https://login.uber.com/oauth/v2/revoke', }; export default function App() { const [request, response, promptAsync] = useAuthRequest( { responseType: ResponseType.Token, clientId: 'CLIENT_ID', scopes: ['profile', 'delivery'], // For usage in managed apps using the proxy redirectUri: makeRedirectUri({ // For usage in bare and standalone native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response?.type === 'success') { const { access_token } = response.params; } }, [response]); return ( <Button disabled={!request} title="Login" onPress={() => { promptAsync(); }} /> ); }
https://auth.expo.io/@yourname/your-app
AuthSession.makeRedirectUri({ useProxy: true })
to create this URI.promptAsync({ useProxy: true, redirectUri })
exp://exp.host/@yourname/your-app
expo publish
'd and opened in the Expo Go app.AuthSession.makeRedirectUri({ useProxy: false })
to create this URI.Linking.makeUrl()
from expo-linking
.promptAsync({ redirectUri })
exp://localhost:19000
expo start
.AuthSession.makeRedirectUri({ useProxy: false })
to create this URI.port
+ host
.Linking.makeUrl()
from expo-linking
.promptAsync({ redirectUri })
yourscheme://path
/
).npx create-react-native-app
or expo eject
expo build:ios
or expo build:android
Expo Go:ios
AuthSession.makeRedirectUri({ native: '<YOUR_URI>' })
to select native when running in the correct environment.scheme
from app.config.js
or app.json
. Often this will be used for providers like Google or Okta which require you to use a custom native URI redirect. You can add, list, and open URI schemes using npx uri-scheme
.expo.scheme
after ejecting then you'll need to use the expo apply
command to apply the changes to your native project, then rebuild them (yarn ios
, yarn android
).promptAsync({ redirectUri })
import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; function App() { React.useEffect(() => { WebBrowser.warmUpAsync(); return () => { WebBrowser.coolDownAsync(); }; }, []); // Do authentication ... }
expo-auth-session
requests an exchange code as this is the most widely applicable login method.import * as React from 'react'; import * as WebBrowser from 'expo-web-browser'; import { useAuthRequest, ResponseType } from 'expo-auth-session'; WebBrowser.maybeCompleteAuthSession(); // Endpoint const discovery = { authorizationEndpoint: 'https://accounts.spotify.com/authorize', }; function App() { const [request, response, promptAsync] = useAuthRequest( { responseType: ResponseType.Token, clientId: 'CLIENT_ID', scopes: ['user-read-email', 'playlist-modify-public'], redirectUri: makeRedirectUri({ native: 'your.app://redirect', }), }, discovery ); React.useEffect(() => { if (response && response.type === 'success') { const token = response.params.access_token; } }, [response]); return <Button disabled={!request} onPress={() => promptAsync()} title="Login" />; }
expo-secure-store
(This is different to AsyncStorage
which is not secure). This package provides native access to keychain services on iOS and encrypted SharedPreferences
on Android. There is no web equivalent to this functionality.import * as SecureStore from 'expo-secure-store'; const MY_SECURE_AUTH_STATE_KEY = 'MySecureAuthStateKey'; function App() { const [, response] = useAuthRequest({}); React.useEffect(() => { if (response && response.type === 'success') { const auth = response.params; const storageValue = JSON.stringify(auth); if (Platform.OS !== 'web') { // Securely store the auth on your device SecureStore.setItemAsync(MY_SECURE_AUTH_STATE_KEY, storageValue); } } }, [response]); // More login code... }