---
title: Authentication with OAuth or OpenID providers
---
import ImageSpotlight from '~/components/plugins/ImageSpotlight';
import { ASSETS, Grid, GridItem, Box } from '~/ui/components/Authentication';
import { Tab, Tabs } from '~/ui/components/Tabs';
Expo can be used to login to many popular providers on Android, iOS, and web. Most of these guides utilize the pure JS [`AuthSession` API](/versions/latest/sdk/auth-session), refer to those docs for more information on the API.
Here are some **important rules** that apply to all authentication providers:
- Use `WebBrowser.maybeCompleteAuthSession()` to dismiss the web popup. If you forget to add this then the popup window will not close.
- Create redirects with `AuthSession.makeRedirectUri()` this does a lot of the heavy lifting involved with universal platform support. Behind the scenes it uses `expo-linking`.
- Build requests using `AuthSession.useAuthRequest()`, the hook allows for async setup which means mobile browsers won't block the authentication.
- Be sure to disable the prompt until `request` is defined.
- You can only invoke `promptAsync` in a user-interaction on web.
## Guides
**AuthSession** can be used for any OAuth or OpenID Connect provider, we've assembled guides for using the most requested services!
If you'd like to see more, you can [open a PR](https://github.com/expo/expo/edit/main/docs/pages/guides/authentication.mdx) or [vote on canny](https://expo.canny.io/feature-requests).
| Website | Provider | PKCE | Auto Discovery |
| ------------------------ | -------- | -------- | -------------- |
| [More Info][c-identity4] | OpenID | Required | Available |
[c-identity4]: https://demo.identityserver.io/
- If `offline_access` isn't included then no refresh token will be returned.
{/* prettier-ignore */}
```tsx IdentityServer 4 Example
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
/* @info Using the Expo proxy will redirect the user through auth.expo.io enabling you to use web links when configuring your project with an OAuth provider. This is not available on web. */
const useProxy = true;
/* @end */
const redirectUri = AuthSession.makeRedirectUri({
useProxy,
});
export default function App() {
/* @info If the provider supports auto discovery then you can pass an issuer to the `useAutoDiscovery` hook to fetch the discovery document. */
const discovery = AuthSession.useAutoDiscovery('https://demo.identityserver.io');
/* @end */
// Create and load an auth request
const [request, result, promptAsync] = AuthSession.useAuthRequest(
{
clientId: 'native.code',
/* @info After a user finishes authenticating, the server will redirect them to this URI. Learn more about linking here. */
redirectUri,
/* @end */
scopes: ['openid', 'profile', 'email', 'offline_access'],
},
discovery
);
return (
);
}
```
{/* End IdentityServer 4 */}
| Website | Provider | PKCE | Auto Discovery |
| ----------------------------------------------- | -------- | --------- | -------------- |
| [Get Your Config](https://console.asgardeo.io/) | OpenID | Supported | Available |
- Make sure to check `Public Client` option in the console.
- Choose the intended grant in the Allowed grant types section.
{/* prettier-ignore */}
```tsx
import { useState, useEffect } from 'react';
import { StyleSheet, Text, View, Button, Alert } from 'react-native';
import * as AuthSession from "expo-auth-session";
import * as WebBrowser from "expo-web-browser";
import jwtDecode from "jwt-decode";
WebBrowser.maybeCompleteAuthSession();
const useProxy = true;
const redirectUri = AuthSession.makeRedirectUri({ useProxy });
/* @info Client ID: This can be found on the protocol tab of Asgardeo Console */
const CLIENT_ID = "YOUR_CLIENT_ID";
/* @end */
export default function App() {
/* @info Auto Discovery URL: This can be found on the info tab of Asgardeo Console. Copy the link under `Issuer` */
const discovery = AuthSession.useAutoDiscovery('https://api.asgardeo.io/t//oauth2/token');
/* @end */
const [tokenResponse, setTokenResponse] = useState({});
const [decodedIdToken, setDecodedIdToken] = useState({});
const [request, result, promptAsync] = AuthSession.useAuthRequest(
{
redirectUri,
clientId: CLIENT_ID,
responseType: "code",
scopes: ["openid", "profile", "email"]
},
discovery
);
const getAccessToken = () => {
if (result?.params?.code) {
/* @info Token Endpoint: This can be found on the info tab of Asgardeo Console. Copy the link under `Token` */
fetch(
/* @end */
"https://api.asgardeo.io/t/iamapptesting/oauth2/token",
{
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `grant_type=authorization_code&code=${result?.params?.code}&redirect_uri=${redirectUri}&client_id=${CLIENT_ID}&code_verifier=${request?.codeVerifier}`
}).then((response) => {
return response.json();
}).then((data) => {
setTokenResponse(data);
setDecodedIdToken(jwtDecode(data.id_token));
}).catch((err) => {
console.log(err);
});
}
}
useEffect(() => {
(async function setResult() {
if (result) {
if (result.error) {
Alert.alert(
"Authentication error",
result.params.error_description || "something went wrong"
);
return;
}
if (result.type === "success") {
getAccessToken();
}
}
})();
}, [result]);
return (
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
accessTokenBlock: {
width: 300,
height: 500,
overflow: "scroll"
}
});
```
{/* prettier-ignore */}
```tsx
import { useState, useEffect } from 'react';
import { StyleSheet, Text, View, Button, Alert } from 'react-native';
import * as AuthSession from "expo-auth-session";
import * as WebBrowser from "expo-web-browser";
import jwtDecode from "jwt-decode";
WebBrowser.maybeCompleteAuthSession();
const useProxy = true;
const redirectUri = AuthSession.makeRedirectUri({ useProxy });
/* @info Client ID: This can be found on the protocol tab of Asgardeo Console */
const CLIENT_ID = "YOUR_CLIENT_ID";
/* @end */
export default function App() {
/* @info Auto Discovery URL: This can be found on the info tab of Asgardeo Console. Copy the link under `Issuer` */
const discovery = AuthSession.useAutoDiscovery('https://api.asgardeo.io/t//oauth2/token');
/* @end */
const [decodedToken, setDecodedToken] = useState({});
const [request, result, promptAsync] = AuthSession.useAuthRequest(
{
redirectUri,
clientId: CLIENT_ID,
responseType: "token",
scopes: ["openid", "profile", "email"]
},
discovery
);
useEffect(() => {
(async function setResult() {
if (result) {
if (result.error) {
Alert.alert(
"Authentication error",
result.params.error_description || "something went wrong"
);
return;
}
if (result.type === "success") {
const jwtToken = result.params.access_token;
const decoded = jwtDecode(jwtToken);
setDecodedToken(decoded);
}
}
})();
}, [result]);
return (
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
accessTokenBlock: {
width: 300,
height: 500,
overflow: "scroll"
}
});
```
{/* End Asgardeo */}
| Website | Provider | PKCE | Auto Discovery |
| --------------------------- | -------- | --------- | -------------- |
| [Get Your Config][c-azure2] | OpenID | Supported | Available |
[c-azure2]: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-overview
{/* prettier-ignore */}
```tsx Azure Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import {
exchangeCodeAsync,
makeRedirectUri,
useAuthRequest,
useAutoDiscovery,
} from 'expo-auth-session';
import { Button, Text, SafeAreaView } from 'react-native';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
export default function App() {
// Endpoint
const discovery = useAutoDiscovery(
'https://login.microsoftonline.com//v2.0',
);
const redirectUri = makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: undefined,
/* @end */
/* @info Azure requires there to be a path in your redirect URI. */
path: 'auth',
/* @end */
});
const clientId = '';
// We store the JWT in here
const [token, setToken] = React.useState(null);
// Request
const [request, , promptAsync] = useAuthRequest(
{
clientId,
scopes: ['openid', 'profile', 'email', 'offline_access'],
redirectUri,
},
discovery,
);
return (
);
}
```
{/* End Azure */}
| Website | Provider | PKCE | Auto Discovery |
| ------------------------------------------------------------------- | -------- | --------- | -------------- |
| [Get your config](https://www.beyondidentity.com/developers/signup) | OpenID | Supported | Available |
- Beyond Identity allows developers to implement strong passwordless authentication based on public-private key pairs called Universal Passkeys. All keys are cryptographically linked to the user and can be centrally managed using the Beyond Identity APIs.
- You will need a Universal Passkey before you can authenticate. See [Beyond Identity documentation](https://developer.beyondidentity.com).
- Make sure to [create a development build](/develop/development-builds/create-a-build/) and follow instructions to [install required config plugins](https://github.com/gobeyondidentity/bi-sdk-react-native/tree/main#using-expo).
- For a complete example app, see [SDK's GitHub repository](https://github.com/gobeyondidentity/bi-sdk-react-native).
- Set your Beyond Identity [Authenticator Config's](https://developer.beyondidentity.com/docs/v1/platform-overview/authenticator-config) Invocation Type to **Automatic**.
- If **Automatic** is selected, Beyond Identity will automatically redirect to your application using the **Invoke URL** (the App Scheme or Univeral URL pointing to your application).
{/* prettier-ignore */}
```jsx Auth Code
import { useEffect } from 'react';
import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
import { Button } from 'react-native';
/* @info Import the Beyond Identity Embedded SDK.*/
import { Embedded } from '@beyondidentity/bi-sdk-react-native';
/* @end */
export default function App() {
// Endpoint
const discovery = useAutoDiscovery(
`https://auth-${region}.beyondidentity.com/v1/tenants/${tenant_id}/realms/${realm_id}/applications/${application_id}`
);
// Request
const [request, response, promptAsync] = useAuthRequest(
{
clientId: `${client_id}`,
scopes: ['openid'],
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property in your app.json or app.config.js is used instead. */
scheme: 'your.app',
/* @end */
}),
},
discovery
);
useEffect(() => {
const authenticate = async url => {
// Display UI for user to select a passwordless passkey
const passkeys = await Embedded.getPasskeys();
if (await Embedded.isAuthenticateUrl(url)) {
// Pass url and a selected passkey ID into the Beyond Identity Embedded SDK authenticate function
/* @info Parse query parameters from the 'redirectUrl' for a 'code' and then exchange that code for an access token */
const { redirectUrl } = await Embedded.authenticate(url, passkeys[0].id);
/* @end */
}
};
/* @info The response does not need to be of type 'success' if it has a url. The state value is stored in a JWT in the url 'request' parameter, and the url will be validated through the Beyond Identity Embedded SDK. */
if (response?.url) {
/* @end */
authenticate(url);
}
}, [response]);
return (
- Set your Beyond Identity [Authenticator Config's](https://developer.beyondidentity.com/docs/v1/platform-overview/authenticator-config) Invocation Type to **Manual**.
- If **Manual** is selected, an authentication URL is returned as part of a JSON response. No redirects are needed and do not require web service authentication. The result is a completely silent OAuth 2.0 authentication using Passkeys.
{/* prettier-ignore */}
```jsx Auth Code
import React from 'react';
import { Button } from 'react-native';
/* @info Import the Beyond Identity Embedded SDK.*/
import { Embedded } from '@beyondidentity/bi-sdk-react-native';
/* @end */
export default function App() {
async function authenticate() {
const BeyondIdentityAuthUrl = `https://auth-${region}.beyondidentity.com/v1/tenants/${tenant_od}/realms/${realm_id}/applications/${application_id}/authorize?response_type=code&client_id=${client_id}&redirect_uri=${uri_encoded_redirect_uri}&scope=openid&state=${state}&code_challenge_method=S256&code_challenge=${pkce_code_challenge}`;
let response = await fetch(BeyondIdentityAuthUrl, {
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json',
}),
});
const data = await response.json();
// Display UI for user to select a passwordless passkey
const passkeys = await Embedded.getPasskeys();
if (await Embedded.isAuthenticateUrl(data.authenticate_url)) {
// Pass url and selected Passkey ID into the Beyond Identity Embedded SDK authenticate function
/* @info Parse query parameters from the 'redirectUrl' for a 'code' and then exchange that code for an access token */
const { redirectUrl } = await Embedded.authenticate(data.authenticate_url, passkeys[0].id);
/* @end */
}
}
return (
);
}
```
{/* End Beyond Identity */}
| Website | Provider | PKCE | Auto Discovery |
| ---------------------------- | -------- | --------- | -------------- |
| [Get Your Config][c-cognito] | OpenID | Supported | Not Available |
[c-cognito]: https://console.aws.amazon.com/cognito/v2/idp/user-pools
[c-cognito-api-docs]: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-userpools-server-contract-reference.html
- Leverages the Hosted UI in Cognito ([API documentation][c-cognito-api-docs])
- Requests code after successfully authenticating, followed by exchanging code for the auth tokens (PKCE)
- The `/token` endpoint requires a `code_verifier` parameter which you can retrieve from the request before calling `exchangeCodeAsync()`:
```tsx
extraParams: {
code_verifier: request.codeVerifier,
}
```
{/* prettier-ignore */}
```tsx
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { useAuthRequest, exchangeCodeAsync, revokeAsync, ResponseType } from 'expo-auth-session';
import { Button, Alert } from 'react-native';
WebBrowser.maybeCompleteAuthSession();
const clientId = '';
const userPoolUrl =
'https://.auth..amazoncognito.com';
const redirectUri = 'your-redirect-uri';
export default function App() {
const [authTokens, setAuthTokens] = React.useState(null);
const discoveryDocument = React.useMemo(() => ({
authorizationEndpoint: userPoolUrl + '/oauth2/authorize',
tokenEndpoint: userPoolUrl + '/oauth2/token',
revocationEndpoint: userPoolUrl + '/oauth2/revoke',
}), []);
const [request, response, promptAsync] = useAuthRequest(
{
clientId,
responseType: ResponseType.Code,
redirectUri,
usePKCE: true,
},
discoveryDocument
);
React.useEffect(() => {
const exchangeFn = async (exchangeTokenReq) => {
try {
const exchangeTokenResponse = await exchangeCodeAsync(
exchangeTokenReq,
discoveryDocument
);
setAuthTokens(exchangeTokenResponse);
} catch (error) {
console.error(error);
}
};
if (response) {
if (response.error) {
Alert.alert(
'Authentication error',
response.params.error_description || 'something went wrong'
);
return;
}
if (response.type === 'success') {
exchangeFn({
clientId,
code: response.params.code,
redirectUri,
extraParams: {
code_verifier: request.codeVerifier,
},
});
}
}
}, [discoveryDocument, request, response]);
const logout = async () => {
const revokeResponse = await revokeAsync(
{
clientId: clientId,
token: authTokens.refreshToken,
},
discoveryDocument
);
if (revokeResponse) {
setAuthTokens(null);
}
};
console.log('authTokens: ' + JSON.stringify(authTokens));
return authTokens ? (
{/* End Cognito */}
| Website | Provider | PKCE | Auto Discovery |
| ----------------------------- | --------- | --------- | -------------- |
| [Get Your Config][c-coinbase] | OAuth 2.0 | Supported | Not Available |
[c-coinbase]: https://www.coinbase.com/oauth/applications/new
- The `redirectUri` requires 2 slashes (`://`).
- Scopes must be joined with ':' so just create one long string.
- Setup redirect URIs: Your Project > Permitted Redirect URIs: (be sure to save after making changes).
- _Expo Go_: `exp://localhost:8081/--/`. For SDK 48 and lower, the port number is `19000`.
- _Web dev_: `https://localhost:19006`
- Run `expo start --web --https` to run with **https**, auth won't work otherwise.
- Adding a slash to the end of the URL doesn't matter.
- _Standalone and Bare_: `your-scheme://`
- Scheme should be specified in app.json `expo.scheme: 'your-scheme'`, then added to the app code with `makeRedirectUri({ native: 'your-scheme://' })`)
- _Proxy_: **Not Supported**
- You cannot use the Expo proxy (`useProxy`) because they don't allow `@` in their redirect URIs.
- _Web production_: `https://yourwebsite.com`
- Set this to whatever your deployed website URL is.
{/* prettier-ignore */}
```tsx
import {
exchangeCodeAsync,
makeRedirectUri,
TokenResponse,
useAuthRequest,
} from "expo-auth-session";
import * as WebBrowser from "expo-web-browser";
import * as React from "react";
import { Button } from "react-native";
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// Endpoint
const discovery = {
authorizationEndpoint: "https://www.coinbase.com/oauth/authorize",
tokenEndpoint: "https://api.coinbase.com/oauth/token",
revocationEndpoint: "https://api.coinbase.com/oauth/revoke",
};
const redirectUri = makeRedirectUri({ /* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */ scheme: 'your.app'/* @end */});
const CLIENT_ID = "CLIENT_ID";
export default function App() {
const [request, response, promptAsync] = useAuthRequest(
{
clientId: CLIENT_ID,
scopes: ["wallet:accounts:read"],
redirectUri,
},
discovery
);
const {
// The token will be auto exchanged after auth completes.
token,
/* @info If the auto exchange fails, you can display the error. */
exchangeError,
/* @end */
} = useAutoExchange(
/* @info The auth code will be exchanged for an access token as soon as it's available. */
response?.type === "success" ? response.params.code : null
/* @end */
);
React.useEffect(() => {
if (token) {
/* @info The access token is now ready to be used to make authenticated requests. */
console.log("My Token:", token.accessToken);
/* @end */
}
}, [token]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
type State = {
token: TokenResponse | null;
exchangeError: Error | null;
};
// A hook to automatically exchange the auth token for an access token.
// this should be performed in a server and not here in the application.
// For educational purposes only:
function useAutoExchange(code?: string): State {
const [state, setState] = React.useReducer(
(state: State, action: Partial) => ({ ...state, ...action }),
{ token: null, exchangeError: null }
);
const isMounted = useMounted();
React.useEffect(() => {
if (!code) {
setState({ token: null, exchangeError: null });
return;
}
/* @info Swap this method out for a fetch request to a server that exchanges your auth code securely. */
exchangeCodeAsync(
/* @end */
{
clientId: CLIENT_ID,
/* @info Never store your client secret in the application code! */
clientSecret: "CLIENT_SECRET",
/* @end */
code,
redirectUri,
},
discovery
)
.then((token) => {
if (isMounted.current) {
setState({ token, exchangeError: null });
}
})
.catch((exchangeError) => {
if (isMounted.current) {
setState({ exchangeError, token: null });
}
});
}, [code]);
return state;
}
function useMounted() {
const isMounted = React.useRef(true);
React.useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
return isMounted;
}
```
- Coinbase does not support implicit grant.
{/* End Coinbase */}
| Website | Provider | PKCE | Auto Discovery |
| ---------------------------- | --------- | ------------- | -------------- |
| [Get Your Config][c-dropbox] | OAuth 2.0 | Not Supported | Not Available |
[c-dropbox]: https://www.dropbox.com/developers/apps/create
- Scopes must be an empty array.
- PKCE must be disabled (`usePKCE: false`) otherwise you'll get an error about `code_challenge` being included in the query string.
- Implicit auth is supported.
- When `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.
Auth code responses (`ResponseType.Code`) will only work in native with `useProxy: true`.
{/* prettier-ignore */}
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// Endpoint
const discovery = {
authorizationEndpoint: 'https://www.dropbox.com/oauth2/authorize',
tokenEndpoint: 'https://www.dropbox.com/oauth2/token',
};
/* @info Implicit auth is universal, .Code will only work in native with useProxy: true. */
const useProxy = Platform.select({ web: false, default: true });
/* @end */
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({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app',
/* @end */
useProxy,
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync({ useProxy });
/* @end */
}}
/>
);
}
```
{/* prettier-ignore */}
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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(
{
/* @info Request that the server returns an access_token, not all providers support this. */
responseType: ResponseType.Token,
/* @end */
clientId: 'CLIENT_ID',
// There are no scopes so just pass an empty array
scopes: [],
// Dropbox doesn't support PKCE
usePKCE: false,
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Use this access token to interact with user data on the provider's server. */
const { access_token } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
{/* End Dropbox */}
| Website | Provider | PKCE | Auto Discovery |
| --------------------------- | --------- | --------- | -------------- |
| [Get Your Config][c-fitbit] | OAuth 2.0 | Supported | Not Available |
[c-fitbit]: https://dev.fitbit.com/apps/new
- Provider only allows one redirect URI per app. You'll need an individual app for every method you want to use:
- Expo Go: `exp://localhost:8081/--/*`. For SDK 48 and lower, the port number is `19000`.
- Expo Go + Proxy: `https://auth.expo.io/@you/your-app`
- Standalone or Bare: `com.your.app://*`
- Web: `https://yourwebsite.com/*`
- The `redirectUri` requires 2 slashes (`://`).
{/* prettier-ignore */}
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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'],
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
{/* prettier-ignore */}
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
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(
{
/* @info Request that the server returns an access_token, not all providers support this. */
responseType: ResponseType.Token,
/* @end */
clientId: 'CLIENT_ID',
scopes: ['activity', 'sleep'],
// For usage in managed apps using the proxy
redirectUri: makeRedirectUri({
useProxy,
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Use this access token to interact with user data on the provider's server. */
const { access_token } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync({ useProxy });
/* @end */
}}
/>
);
}
```
{/* End FitBit */}
| Website | Provider | PKCE | Auto Discovery |
| --------------------------- | --------- | --------- | -------------- |
| [Get Your Config][c-github] | OAuth 2.0 | Supported | Not Available |
[c-github]: https://github.com/settings/developers
- Provider only allows one redirect URI per app. You'll need an individual app for every method you want to use:
- Expo Go: `exp://localhost:8081/--/*`. For SDK 48 and lower, the port number is `19000`.
- Expo Go + Proxy: `https://auth.expo.io/@you/your-app`
- Standalone or Bare: `com.your.app://*`
- Web: `https://yourwebsite.com/*`
- The `redirectUri` requires 2 slashes (`://`).
- `revocationEndpoint` is dynamic and requires your `config.clientId`.
{/* prettier-ignore */}
```tsx
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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/',
};
export default function App() {
const [request, response, promptAsync] = useAuthRequest(
{
clientId: 'CLIENT_ID',
scopes: ['identity'],
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
- Implicit grant is [not supported for GitHub](https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/).
| Website | Provider | PKCE | Auto Discovery |
| -------------------------- | --------- | --------- | -------------- |
| [Get Your Config][c-imgur] | OAuth 2.0 | Supported | Not Available |
[c-imgur]: https://api.imgur.com/oauth2/addclient
- You will need to create a different provider app for each platform (dynamically choosing your `clientId`).
- Learn more here: [imgur.com/oauth2](https://api.imgur.com/oauth2)
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
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({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app',
/* @end */
}),
// imgur requires an empty array
scopes: [],
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
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(
{
/* @info Request that the server returns an access_token, not all providers support this. */
responseType: ResponseType.Token,
/* @end */
clientId: 'CLIENT_ID',
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app',
/* @end */
}),
scopes: [],
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Use this access token to interact with user data on the provider's server. */
const { access_token } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
{/* End Imgur */}
| Website | Provider | PKCE | Auto Discovery |
| ------- | -------- | -------- | -------------- |
| - | OpenID | Required | Available |
{/* prettier-ignore */}
```tsx Keycloak Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
import { Button, Text, View } from 'react-native';
WebBrowser.maybeCompleteAuthSession();
export default function App() {
/* @info If the provider supports auto discovery then you can pass an issuer to the `useAutoDiscovery` hook to fetch the discovery document. */
const discovery = useAutoDiscovery('https://YOUR_KEYCLOAK/realms/YOUR_REALM');
/* @end */
// Create and load an auth request
const [request, result, promptAsync] = useAuthRequest(
{
clientId: 'YOUR_CLIENT_NAME',
/* @info After a user finishes authenticating, the server will redirect them to this URI. Learn more about linking here. */
redirectUri: makeRedirectUri({
scheme: 'YOUR_SCHEME'
}),
/* @end */
scopes: ['openid', 'profile'],
},
discovery
);
return (
promptAsync()} />
{result && {JSON.stringify(result, null, 2)}}
);
}
```
{/* End Keycloak */}
| Website | Provider | PKCE | Auto Discovery |
| -------------------------------- | -------- | --------- | -------------- |
| [Sign-up][c-okta] > Applications | OpenID | Supported | Available |
[c-okta]: https://developer.okta.com/signup/
- You cannot define a custom `redirectUri`, Okta will provide you with one.
- You can use the Expo proxy to test this without a native rebuild, just be sure to configure the project as a website.
{/* prettier-ignore */}
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
const useProxy = Platform.select({ web: false, default: true });
export default function App() {
// Endpoint
const discovery = useAutoDiscovery('https://.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.:/callback',
useProxy,
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync({ useProxy });
/* @end */
}}
/>
);
}
```
- This flow is not documented yet, learn more [from the Okta website](https://developer.okta.com/docs/guides/implement-implicit/use-flow/).
{/* End Okta */}
| Website | Provider | PKCE | Auto Discovery |
| --------------------------- | --------- | --------- | -------------- |
| [Get Your Config][c-reddit] | OAuth 2.0 | Supported | Not Available |
[c-reddit]: https://www.reddit.com/prefs/apps
- Provider only allows one redirect URI per app. You'll need an individual app for every method you want to use:
- Expo Go: `exp://localhost:8081/--/*`. For SDK 48 and lower, the port number is `19000`.
- Expo Go + Proxy: `https://auth.expo.io/@you/your-app`
- Standalone or Bare: `com.your.app://*`
- Web: `https://yourwebsite.com/*`
- The `redirectUri` requires 2 slashes (`://`).
{/* prettier-ignore */}
```tsx
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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'],
redirectUri: makeRedirectUri({
// For usage in bare and standalone
native: 'your.app://redirect',
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
- You must select the `installed` option for your app on Reddit to use implicit grant.
{/* prettier-ignore */}
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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(
{
/* @info Request that the server returns an access_token, not all providers support this. */
responseType: ResponseType.Token,
/* @end */
clientId: 'CLIENT_ID',
scopes: ['identity'],
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Use this access token to interact with user data on the provider's server. */
const { access_token } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
{/* End Reddit */}
| Website | Provider | PKCE | Auto Discovery |
| -------------------------- | --------- | --------- | -------------- |
| [Get Your Config][c-slack] | OAuth 2.0 | Supported | Not Available |
[c-slack]: https://api.slack.com/apps
- The `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.
- Scopes must be joined with ':' so just create one long string.
- Navigate to the **"Scopes"** section to enable scopes.
- `revocationEndpoint` is not available.
{/* prettier-ignore */}
```tsx
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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'],
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
- Slack does not support implicit grant.
{/* End Slack */}
| Website | Provider | PKCE | Auto Discovery |
| ---------------------------- | --------- | --------- | -------------- |
| [Get Your Config][c-spotify] | OAuth 2.0 | Supported | Not Available |
[c-spotify]: https://developer.spotify.com/dashboard/applications
- Setup your redirect URIs: Your project > Edit Settings > Redirect URIs (be sure to save after making changes).
- _Expo Go_: `exp://localhost:8081/--/`. For SDK 48 and lower, the port number is `19000`.
- _Web dev_: `https://localhost:19006`
- Important: Ensure there's no slash at the end of the URL unless manually changed in the app code with `makeRedirectUri({ path: '/' })`.
- Run `expo start --web --https` to run with **https**, auth won't work otherwise.
- _Custom app_: `your-scheme://`
- Scheme should be specified in app.json `expo.scheme: 'your-scheme'`, then added to the app code with `makeRedirectUri({ native: 'your-scheme://' })`)
- _Proxy_: `https://auth.expo.io/@username/slug`
- If `useProxy` is enabled (native only), change **username** for your Expo username and **slug** for the `slug` in your app.json. Try not to change the slug for your app after using it for auth as this can lead to versioning issues.
- _Web production_: `https://yourwebsite.com`
- Set this to whatever your deployed website URL is.
- For simpler authentication, use the **Implicit Flow** as it'll return an `access_token` without the need for a code exchange server request.
- Learn more about the [Spotify API](https://developer.spotify.com/documentation/web-api/).
{/* prettier-ignore */}
```tsx
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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'],
// To follow the "Authorization Code Flow" to fetch token after authorizationEndpoint
// this must be set to false
usePKCE: false,
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
{/* prettier-ignore */}
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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(
{
/* @info Request that the server returns an access_token, not all providers support this. */
responseType: ResponseType.Token,
/* @end */
clientId: 'CLIENT_ID',
scopes: ['user-read-email', 'playlist-modify-public'],
// To follow the "Authorization Code Flow" to fetch token after authorizationEndpoint
// this must be set to false
usePKCE: false,
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Use this access token to interact with user data on the provider's server. */
const { access_token } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
{/* End Spotify */}
| Website | Provider | PKCE | Auto Discovery |
| --------------------------- | --------- | --------- | -------------- |
| [Get Your Config][c-strava] | OAuth 2.0 | Supported | Not Available |
[c-strava]: https://www.strava.com/settings/api
- Learn more about the [Strava API](http://developers.strava.com/docs/reference/).
- The "Authorization Callback Domain" refers to the final path component of your redirect URI. Ex: In the URI `com.bacon.myapp://redirect` the domain would be `redirect`.
- No Implicit auth flow is provided by Strava.
{/* prettier-ignore */}
```tsx
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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'],
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') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
Strava doesn't provide an implicit auth flow, you should send the code to a server or serverless function to perform the access token exchange.
For **debugging** purposes, you can perform the exchange client-side using the following method:
{/* prettier-ignore */}
```tsx
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' }
);
```
{/* End Strava */}
| Website | Provider | PKCE | Auto Discovery | Scopes |
| --------------------------- | -------- | --------- | -------------- | ---------------- |
| [Get your Config][c-twitch] | OAuth | Supported | Not Available | [Info][s-twitch] |
[c-twitch]: https://dev.twitch.tv/console/apps/create
[s-twitch]: https://dev.twitch.tv/docs/authentication#scopes
- You will need to enable 2FA on your Twitch account to create an application.
{/* prettier-ignore */}
```tsx
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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',
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
scopes: ['user:read:email', 'analytics:read:games'],
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
{/* prettier-ignore */}
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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(
{
/* @info Request that the server returns an access_token, not all providers support this. */
responseType: ResponseType.Token,
/* @end */
clientId: 'CLIENT_ID',
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
scopes: ['user:read:email', 'analytics:read:games'],
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Use this access token to interact with user data on the provider's server. */
const { access_token } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
{/* End Twitch */}
| Website | Provider | PKCE | Auto Discovery | Scopes |
| ---------------------------- | -------- | --------- | -------------- | ----------------- |
| [Get your Config][c-twitter] | OAuth | Supported | Not Available | [Info][s-twitter] |
[c-twitter]: https://developer.twitter.com/en/portal/projects/new
[s-twitter]: https://developer.twitter.com/en/docs/authentication/oauth-2-0/authorization-code
- You will need to be approved by Twitter support before you can use the Twitter v2 API.
- Web does not appear to work, the Twitter authentication website appears to block the popup, causing the `response` of `useAuthRequest` to always be `{type: 'dismiss'}`.
- Example redirects:
- Expo Go + Proxy: `https://auth.expo.io/@you/your-app`
- Standalone or Bare: `com.your.app://`
- Web (dev `expo start --https`): `https://localhost:19006` (no ending slash)
- The `redirectUri` requires 2 slashes (`://`).
#### Expo Go
You must use the proxy service in the Expo Go app because `exp://localhost:8081` cannot be added to your Twitter app as a redirect.
{/* prettier-ignore */}
```tsx
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';
const useProxy = Platform.select({ web: false, default: true });
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// Endpoint
const discovery = {
authorizationEndpoint: "https://twitter.com/i/oauth2/authorize",
tokenEndpoint: "https://twitter.com/i/oauth2/token",
revocationEndpoint: "https://twitter.com/i/oauth2/revoke",
};
export default function App() {
const [request, response, promptAsync] = useAuthRequest(
{
clientId: 'CLIENT_ID',
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app',
/* @end */
useProxy
}),
usePKCE: true,
scopes: [
"tweet.read",
],
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync({ useProxy });
/* @end */
}}
/>
);
}
```
{/* End Twitter */}
| Website | Provider | PKCE | Auto Discovery |
| ------------------------- | --------- | --------- | -------------- |
| [Get Your Config][c-uber] | OAuth 2.0 | Supported | Not Available |
[c-uber]: https://developer.uber.com/docs/riders/guides/authentication/introduction
- The `redirectUri` requires 2 slashes (`://`).
- `scopes` can be difficult to get approved.
{/* prettier-ignore */}
```tsx
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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'],
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Exchange the code for an access token in a server. Alternatively you can use the Implicit auth method. */
const { code } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
{/* prettier-ignore */}
```tsx
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';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// 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(
{
/* @info Request that the server returns an access_token, not all providers support this. */
responseType: ResponseType.Token,
/* @end */
clientId: 'CLIENT_ID',
scopes: ['profile', 'delivery'],
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response?.type === 'success') {
/* @info Use this access token to interact with user data on the provider's server. */
const { access_token } = response.params;
/* @end */
}
}, [response]);
return (
{
/* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
promptAsync();
/* @end */
}}
/>
);
}
```
{/* End Uber */}
{/* End Guides */}
## Redirect URI patterns
Here are a few examples of some common redirect URI patterns you may end up using.
### Standalone and bare
> `yourscheme://path`
In some cases there will be anywhere between 1 to 3 slashes (`/`).
- **Environment:**
- Bare workflow
- `npx expo prebuild`
- Standalone builds in the App or Play Store or testing locally
- Android: `eas build` or `npx expo run:android`
- iOS: `eas build` or `npx expo run:ios`
- **Create:** Use `AuthSession.makeRedirectUri({ native: '' })` to select native when running in the correct environment.
- `your.app://redirect` -> `makeRedirectUri({ scheme: 'your.app', path: 'redirect' })`
- `your.app:///` -> `makeRedirectUri({ scheme: 'your.app', isTripleSlashed: true })`
- `your.app:/authorize` -> `makeRedirectUri({ native: 'your.app:/authorize' })`
- `your.app://auth?foo=bar` -> `makeRedirectUri({ scheme: 'your.app', path: 'auth', queryParams: { foo: 'bar' } })`
- `exp://u.expo.dev/[project-id]?channel-name=[channel-name]&runtime-version=[runtime-version]` -> `makeRedirectUri()`
- This link can often be created automatically but we recommend you define the `scheme` property at least. The entire URL can be overridden in custom apps by passing the `native` property. 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`.
- If you change the `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`).
- **Usage:** `promptAsync({ redirectUri })`
## Improving user experience
The "login flow" is an important thing to get right, in a lot of cases this is where the user will _commit_ to using your app again. A bad experience can cause users to give up on your app before they've really gotten to use it.
Here are a few tips you can use to make authentication quick, easy, and secure for your users!
### Warming the browser
On Android you can optionally warm up the web browser before it's used. This allows the browser app to pre-initialize itself in the background. Doing this can significantly speed up prompting the user for authentication.
{/* prettier-ignore */}
```tsx
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
function App() {
React.useEffect(() => {
/* @info Android only: Start loading the default browser app in the background to improve transition time. */
WebBrowser.warmUpAsync();
/* @end */
return () => {
/* @info Android only: Cool down the browser when the component unmounts to help improve memory on low-end Android devices. */
WebBrowser.coolDownAsync();
/* @end */
};
}, []);
// Do authentication ...
}
```
### Implicit login
You should never store your client secret locally in your bundle because there's no secure way to do this. Luckily a lot of providers have an "Implicit flow" which enables you to request an access token without the client secret. By default `expo-auth-session` requests an exchange code as this is the most widely applicable login method.
Here is an example of logging into Spotify without using a client secret.
{/* prettier-ignore */}
```tsx
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest, ResponseType } from 'expo-auth-session';
/* @info Web only: This method should be invoked on the page that the auth popup gets redirected to on web, it'll ensure that authentication is completed properly. On native this does nothing. */
WebBrowser.maybeCompleteAuthSession();
/* @end */
// Endpoint
const discovery = {
authorizationEndpoint: 'https://accounts.spotify.com/authorize',
};
function App() {
const [request, response, promptAsync] = useAuthRequest(
{
/* @info Request that the server returns an access_token, not all providers support this. */
responseType: ResponseType.Token,
/* @end */
clientId: 'CLIENT_ID',
scopes: ['user-read-email', 'playlist-modify-public'],
redirectUri: makeRedirectUri({
/* @info The URI [scheme]:// to be used in bare and standalone. If undefined, the scheme property of your app.json or app.config.js will be used instead. */
scheme: 'your.app'
/* @end */
}),
},
discovery
);
React.useEffect(() => {
if (response && response.type === 'success') {
/* @info You can use this access token to make calls into the Spotify API. */
const token = response.params.access_token;
/* @end */
}
}, [response]);
return promptAsync()} title="Login" />;
}
```
### Storing data
On native platforms like iOS, and Android you can secure things like access tokens locally using a package called [`expo-secure-store`](/versions/latest/sdk/securestore) (This is different to `AsyncStorage` which is not secure). This package provides native access to [keychain services](https://developer.apple.com/documentation/security/keychain_services) on iOS and encrypted [`SharedPreferences`](https://developer.android.com/training/basics/data-storage/shared-preferences.html) on Android. There is no web equivalent to this functionality.
You can store your authentication results and rehydrate them later to avoid having to prompt the user to login again.
{/* prettier-ignore */}
```tsx
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...
}
```
[userinfo]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
[provider-meta]: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
[oidc-dcr]: https://openid.net/specs/openid-connect-discovery-1_0.html#OpenID.Registration
[oidc-autherr]: https://openid.net/specs/openid-connect-core-1_0.html#AuthError
[oidc-authreq]: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationRequest