xref: /expo/docs/pages/guides/authentication.mdx (revision da751fd5)
1---
2title: Authentication with OAuth or OpenID providers
3---
4
5import ImageSpotlight from '~/components/plugins/ImageSpotlight';
6import { ASSETS, Grid, GridItem, Box } from '~/ui/components/Authentication';
7import { Tab, Tabs } from '~/ui/components/Tabs';
8
9Expo can be used to login to many popular providers on iOS, Android, 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.
10
11Here are some **important rules** that apply to all authentication providers:
12
13- Use `WebBrowser.maybeCompleteAuthSession()` to dismiss the web popup. If you forget to add this then the popup window will not close.
14- 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`.
15- Build requests using `AuthSession.useAuthRequest()`, the hook allows for async setup which means mobile browsers won't block the authentication.
16- Be sure to disable the prompt until `request` is defined.
17- You can only invoke `promptAsync` in a user-interaction on web.
18
19## Guides
20
21**AuthSession** can be used for any OAuth or OpenID Connect provider, we've assembled guides for using the most requested services!
22If 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).
23
24<Grid>
25  <GridItem title="IdentityServer 4" protocol={['OAuth 2', 'OpenID']} image={ASSETS.id4} />
26  <GridItem title="Azure" protocol={['OAuth 2', 'OpenID']} image={ASSETS.azure} />
27  <GridItem
28    title="Apple"
29    protocol={['iOS Only']}
30    href="/versions/latest/sdk/apple-authentication"
31    image={ASSETS.apple}
32  />
33  <GridItem
34    title="Beyond Identity"
35    protocol={['OAuth 2', 'OpenID']}
36    image={ASSETS.beyondidentity}
37  />
38  <GridItem title="Cognito" protocol={['OAuth 2', 'OpenID']} image={ASSETS.cognito} />
39  <GridItem title="Coinbase" protocol={['OAuth 2']} image={ASSETS.coinbase} />
40  <GridItem title="Dropbox" protocol={['OAuth 2']} image={ASSETS.dropbox} />
41  <GridItem
42    title="Facebook"
43    protocol={['OAuth 2']}
44    href="/guides/facebook-authentication/"
45    image={ASSETS.facebook}
46  />
47  <GridItem title="Fitbit" protocol={['OAuth 2']} image={ASSETS.fitbit} />
48  <GridItem
49    title="Firebase Phone"
50    protocol={['Recaptcha']}
51    href="/versions/latest/sdk/firebase-recaptcha"
52    image={ASSETS.firebase}
53  />
54  <GridItem title="GitHub" protocol={['OAuth 2']} image={ASSETS.github} />
55  <GridItem
56    title="Google"
57    protocol={['OAuth 2', 'OpenID']}
58    href="/guides/google-authentication/"
59    image={ASSETS.google}
60  />
61  <GridItem title="Imgur" protocol={['OAuth 2']} image={ASSETS.imgur} />
62  <GridItem title="Okta" protocol={['OAuth 2', 'OpenID']} image={ASSETS.okta} />
63  <GridItem title="Reddit" protocol={['OAuth 2']} image={ASSETS.reddit} />
64  <GridItem title="Slack" protocol={['OAuth 2']} image={ASSETS.slack} />
65  <GridItem title="Spotify" protocol={['OAuth 2']} image={ASSETS.spotify} />
66  <GridItem title="Strava" protocol={['OAuth 2']} image={ASSETS.strava} />
67  <GridItem title="Twitch" protocol={['OAuth 2']} image={ASSETS.twitch} />
68  <GridItem title="Twitter" protocol={['OAuth 2']} image={ASSETS.twitter} />
69  <GridItem title="Uber" protocol={['OAuth 2']} image={ASSETS.uber} />
70</Grid>
71
72<Box
73  name="IdentityServer 4"
74  image={ASSETS.id4}
75>
76
77| Website                  | Provider | PKCE     | Auto Discovery |
78| ------------------------ | -------- | -------- | -------------- |
79| [More Info][c-identity4] | OpenID   | Required | Available      |
80
81[c-identity4]: https://demo.identityserver.io/
82
83- If `offline_access` isn't included then no refresh token will be returned.
84
85{/* prettier-ignore */}
86```tsx IdentityServer 4 Example
87import * as React from 'react';
88import { Button, Text, View } from 'react-native';
89import * as AuthSession from 'expo-auth-session';
90import * as WebBrowser from 'expo-web-browser';
91
92/* @info <strong>Web only:</strong> 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. */
93WebBrowser.maybeCompleteAuthSession();
94/* @end */
95/* @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. */
96const useProxy = true;
97/* @end */
98const redirectUri = AuthSession.makeRedirectUri({
99  useProxy,
100});
101
102export default function App() {
103  /* @info If the provider supports auto discovery then you can pass an issuer to the `useAutoDiscovery` hook to fetch the discovery document. */
104  const discovery = AuthSession.useAutoDiscovery('https://demo.identityserver.io');
105  /* @end */
106  // Create and load an auth request
107  const [request, result, promptAsync] = AuthSession.useAuthRequest(
108    {
109      clientId: 'native.code',
110      /* @info After a user finishes authenticating, the server will redirect them to this URI. Learn more about <a href="../../guides/linking/">linking here</a>. */
111      redirectUri,
112      /* @end */
113      scopes: ['openid', 'profile', 'email', 'offline_access'],
114    },
115    discovery
116  );
117
118  return (
119    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
120      <Button title="Login!" disabled={!request} onPress={() => promptAsync({ useProxy })} />
121      {result && <Text>{JSON.stringify(result, null, 2)}</Text>}
122    </View>
123  );
124}
125```
126
127</Box>
128{/* End IdentityServer 4 */}
129
130<Box
131  name="Azure"
132  createUrl="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-overview"
133  image={ASSETS.azure}
134>
135
136| Website                     | Provider | PKCE      | Auto Discovery |
137| --------------------------- | -------- | --------- | -------------- |
138| [Get Your Config][c-azure2] | OpenID   | Supported | Available      |
139
140[c-azure2]: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-overview
141
142{/* prettier-ignore */}
143```tsx Azure Example
144import * as React from 'react';
145import * as WebBrowser from 'expo-web-browser';
146import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
147import { Button } from 'react-native';
148
149/* @info <strong>Web only:</strong> 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. */
150WebBrowser.maybeCompleteAuthSession();
151/* @end */
152
153export default function App() {
154  // Endpoint
155  const discovery = useAutoDiscovery('https://login.microsoftonline.com/<TENANT_ID>/v2.0');
156  // Request
157  const [request, response, promptAsync] = useAuthRequest(
158    {
159      clientId: 'CLIENT_ID',
160      scopes: ['openid', 'profile', 'email', 'offline_access'],
161      redirectUri: makeRedirectUri({
162        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
163        scheme: 'your.app'
164        /* @end */
165      }),
166    },
167    discovery
168  );
169
170  return (
171    <Button
172      /* @info Disable the button until the request is loaded asynchronously. */
173      disabled={!request}
174      /* @end */
175      title="Login"
176      onPress={() => {
177        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
178        promptAsync();
179        /* @end */
180      }}
181    />
182  );
183}
184```
185
186</Box>
187{/* End Azure */}
188
189<Box
190  name="Beyond Identity"
191  createUrl="https://www.beyondidentity.com/developers/signup"
192  image={ASSETS.beyondidentity}
193>
194
195| Website                                                             | Provider | PKCE      | Auto Discovery |
196| ------------------------------------------------------------------- | -------- | --------- | -------------- |
197| [Get your config](https://www.beyondidentity.com/developers/signup) | OpenID   | Supported | Available      |
198
199- 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.
200- You will need a Universal Passkey before you can authenticate. See [Beyond Identity documentation](https://developer.beyondidentity.com).
201- 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).
202- For a complete example app, see [SDK's GitHub repository](https://github.com/gobeyondidentity/bi-sdk-react-native).
203
204<Tabs tabs={["Auth Code with automatic invocation", "Auth Code with manual invocation"]}>
205<Tab>
206- Set your Beyond Identity [Authenticator Config's](https://developer.beyondidentity.com/docs/v1/platform-overview/authenticator-config) Invocation Type to **Automatic**.
207
208- 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).
209  {/* prettier-ignore */}
210
211```jsx Auth Code
212import { useEffect } from 'react';
213import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
214import { Button } from 'react-native';
215/* @info Import the Beyond Identity Embedded SDK.*/
216import { Embedded } from '@beyondidentity/bi-sdk-react-native';
217/* @end */
218
219export default function App() {
220  // Endpoint
221  const discovery = useAutoDiscovery(
222    `https://auth-${region}.beyondidentity.com/v1/tenants/${tenant_id}/realms/${realm_id}/applications/${application_id}`
223  );
224  // Request
225  const [request, response, promptAsync] = useAuthRequest(
226    {
227      clientId: `${client_id}`,
228      scopes: ['openid'],
229      redirectUri: makeRedirectUri({
230        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property in your app.json or app.config.js is used instead. */
231        scheme: 'your.app',
232        /* @end */
233      }),
234    },
235    discovery
236  );
237
238  useEffect(() => {
239    const authenticate = async url => {
240      // Display UI for user to select a passwordless passkey
241      const passkeys = await Embedded.getPasskeys();
242
243      if (await Embedded.isAuthenticateUrl(url)) {
244        // Pass url and a selected passkey ID into the Beyond Identity Embedded SDK authenticate function
245        /* @info Parse query parameters from the 'redirectUrl' for a 'code' and then exchange that code for an access token */
246        const { redirectUrl } = await Embedded.authenticate(url, passkeys[0].id);
247        /* @end */
248      }
249    };
250
251    /* @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. */
252    if (response?.url) {
253      /* @end */
254      authenticate(url);
255    }
256  }, [response]);
257
258  return (
259    <Button
260      /* @info Disable the button until the request is loaded asynchronously. */
261      disabled={!request}
262      /* @end */
263      title="Passwordless Login"
264      onPress={() => {
265        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
266        promptAsync();
267        /* @end */
268      }}
269    />
270  );
271}
272```
273
274</Tab>
275
276<Tab>
277- Set your Beyond Identity [Authenticator Config's](https://developer.beyondidentity.com/docs/v1/platform-overview/authenticator-config) Invocation Type to **Manual**.
278- 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.
279
280{/* prettier-ignore */}
281```jsx Auth Code
282import React from 'react';
283import { Button } from 'react-native';
284/* @info Import the Beyond Identity Embedded SDK.*/
285import { Embedded } from '@beyondidentity/bi-sdk-react-native';
286/* @end */
287
288export default function App() {
289  async function authenticate() {
290    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}`;
291
292    let response = await fetch(BeyondIdentityAuthUrl, {
293      method: 'GET',
294      headers: new Headers({
295        'Content-Type': 'application/json',
296      }),
297    });
298    const data = await response.json();
299
300    // Display UI for user to select a passwordless passkey
301    const passkeys = await Embedded.getPasskeys();
302
303    if (await Embedded.isAuthenticateUrl(data.authenticate_url)) {
304      // Pass url and selected Passkey ID into the Beyond Identity Embedded SDK authenticate function
305      /* @info Parse query parameters from the 'redirectUrl' for a 'code' and then exchange that code for an access token */
306      const { redirectUrl } = await Embedded.authenticate(data.authenticate_url, passkeys[0].id);
307      /* @end */
308    }
309  }
310
311  return (
312    <Button
313      title="Passwordless Login"
314      onPress={authenticate}
315    />
316  );
317}
318```
319
320</Tab>
321
322</Tabs>
323
324</Box>
325{/* End Beyond Identity */}
326
327<Box
328  name="Cognito"
329  createUrl="https://console.aws.amazon.com/cognito/v2/idp/user-pools"
330  image={ASSETS.cognito}
331>
332
333| Website                      | Provider | PKCE      | Auto Discovery |
334| ---------------------------- | -------- | --------- | -------------- |
335| [Get Your Config][c-cognito] | OpenID   | Supported | Not Available  |
336
337[c-cognito]: https://console.aws.amazon.com/cognito/v2/idp/user-pools
338[c-cognito-api-docs]: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-userpools-server-contract-reference.html
339
340- Leverages the Hosted UI in Cognito ([API documentation][c-cognito-api-docs])
341- Requests code after successfully authenticating, followed by exchanging code for the auth tokens (PKCE)
342- The `/token` endpoint requires a `code_verifier` parameter which you can retrieve from the request before calling `exchangeCodeAsync()`:
343
344```tsx
345extraParams: {
346  code_verifier: request.codeVerifier,
347}
348```
349
350<Tabs tabs={["Auth Code"]}>
351
352<Tab>
353
354{/* prettier-ignore */}
355```tsx
356import * as React from 'react';
357import * as WebBrowser from 'expo-web-browser';
358import { useAuthRequest, exchangeCodeAsync, revokeAsync, ResponseType } from 'expo-auth-session';
359import { Button, Alert } from 'react-native';
360
361WebBrowser.maybeCompleteAuthSession();
362
363const clientId = '<your-client-id-here>';
364const userPoolUrl =
365  'https://<your-user-pool-domain>.auth.<your-region>.amazoncognito.com';
366const redirectUri = 'your-redirect-uri';
367
368export default function App() {
369  const [authTokens, setAuthTokens] = React.useState(null);
370  const discoveryDocument = React.useMemo(() => ({
371    authorizationEndpoint: userPoolUrl + '/oauth2/authorize',
372    tokenEndpoint: userPoolUrl + '/oauth2/token',
373    revocationEndpoint: userPoolUrl + '/oauth2/revoke',
374  }), []);
375
376  const [request, response, promptAsync] = useAuthRequest(
377    {
378      clientId,
379      responseType: ResponseType.Code,
380      redirectUri,
381      usePKCE: true,
382    },
383    discoveryDocument
384  );
385
386  React.useEffect(() => {
387    const exchangeFn = async (exchangeTokenReq) => {
388      try {
389        const exchangeTokenResponse = await exchangeCodeAsync(
390          exchangeTokenReq,
391          discoveryDocument
392        );
393        setAuthTokens(exchangeTokenResponse);
394      } catch (error) {
395        console.error(error);
396      }
397    };
398    if (response) {
399      if (response.error) {
400        Alert.alert(
401          'Authentication error',
402          response.params.error_description || 'something went wrong'
403        );
404        return;
405      }
406      if (response.type === 'success') {
407        exchangeFn({
408          clientId,
409          code: response.params.code,
410          redirectUri,
411          extraParams: {
412            code_verifier: request.codeVerifier,
413          },
414        });
415      }
416    }
417  }, [discoveryDocument, request, response]);
418
419  const logout = async () => {
420    const revokeResponse = await revokeAsync(
421      {
422        clientId: clientId,
423        token: authTokens.refreshToken,
424      },
425      discoveryDocument
426    );
427    if (revokeResponse) {
428      setAuthTokens(null);
429    }
430  };
431  console.log('authTokens: ' + JSON.stringify(authTokens));
432  return authTokens ? (
433    <Button title="Logout" onPress={() => logout()} />
434  ) : (
435    <Button disabled={!request} title="Login" onPress={() => promptAsync()} />
436  );
437}
438```
439
440</Tab>
441
442</Tabs>
443
444</Box>
445
446{/* End Cognito */}
447
448<Box
449  name="Coinbase"
450  createUrl="https://www.coinbase.com/oauth/applications/new"
451  image={ASSETS.coinbase}
452>
453
454| Website                       | Provider  | PKCE      | Auto Discovery |
455| ----------------------------- | --------- | --------- | -------------- |
456| [Get Your Config][c-coinbase] | OAuth 2.0 | Supported | Not Available  |
457
458[c-coinbase]: https://www.coinbase.com/oauth/applications/new
459
460- The `redirectUri` requires 2 slashes (`://`).
461- Scopes must be joined with ':' so just create one long string.
462- Setup redirect URIs: Your Project > Permitted Redirect URIs: (be sure to save after making changes).
463  - _Expo Go_: `exp://localhost:8081/--/`. For SDK 48 and lower, the port number is `19000`.
464  - _Web dev_: `https://localhost:19006`
465    - Run `expo start --web --https` to run with **https**, auth won't work otherwise.
466    - Adding a slash to the end of the URL doesn't matter.
467  - _Standalone and Bare_: `your-scheme://`
468    - Scheme should be specified in app.json `expo.scheme: 'your-scheme'`, then added to the app code with `makeRedirectUri({ native: 'your-scheme://' })`)
469  - _Proxy_: **Not Supported**
470    - You cannot use the Expo proxy (`useProxy`) because they don't allow `@` in their redirect URIs.
471  - _Web production_: `https://yourwebsite.com`
472    - Set this to whatever your deployed website URL is.
473
474<Tabs tabs={["Auth Code", "Implicit Flow"]}>
475<Tab>
476
477{/* prettier-ignore */}
478```tsx
479import {
480  exchangeCodeAsync,
481  makeRedirectUri,
482  TokenResponse,
483  useAuthRequest,
484} from "expo-auth-session";
485import * as WebBrowser from "expo-web-browser";
486import * as React from "react";
487import { Button } from "react-native";
488
489/* @info <strong>Web only:</strong> 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. */
490WebBrowser.maybeCompleteAuthSession();
491/* @end */
492
493// Endpoint
494const discovery = {
495  authorizationEndpoint: "https://www.coinbase.com/oauth/authorize",
496  tokenEndpoint: "https://api.coinbase.com/oauth/token",
497  revocationEndpoint: "https://api.coinbase.com/oauth/revoke",
498};
499
500const redirectUri = makeRedirectUri({ /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */ scheme: 'your.app'/* @end */});
501const CLIENT_ID = "CLIENT_ID";
502
503export default function App() {
504  const [request, response, promptAsync] = useAuthRequest(
505    {
506      clientId: CLIENT_ID,
507      scopes: ["wallet:accounts:read"],
508      redirectUri,
509    },
510    discovery
511  );
512  const {
513    // The token will be auto exchanged after auth completes.
514    token,
515    /* @info If the auto exchange fails, you can display the error. */
516    exchangeError,
517    /* @end */
518  } = useAutoExchange(
519    /* @info The auth code will be exchanged for an access token as soon as it's available. */
520    response?.type === "success" ? response.params.code : null
521    /* @end */
522  );
523
524  React.useEffect(() => {
525    if (token) {
526      /* @info The access token is now ready to be used to make authenticated requests. */
527      console.log("My Token:", token.accessToken);
528      /* @end */
529    }
530  }, [token]);
531
532  return (
533    <Button
534      /* @info Disable the button until the request is loaded asynchronously. */
535      disabled={!request}
536      /* @end */
537      title="Login"
538      onPress={() => {
539        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
540        promptAsync();
541        /* @end */
542      }}
543    />
544  );
545}
546
547type State = {
548  token: TokenResponse | null;
549  exchangeError: Error | null;
550};
551
552// A hook to automatically exchange the auth token for an access token.
553// this should be performed in a server and not here in the application.
554// For educational purposes only:
555function useAutoExchange(code?: string): State {
556  const [state, setState] = React.useReducer(
557    (state: State, action: Partial<State>) => ({ ...state, ...action }),
558    { token: null, exchangeError: null }
559  );
560  const isMounted = useMounted();
561
562  React.useEffect(() => {
563    if (!code) {
564      setState({ token: null, exchangeError: null });
565      return;
566    }
567
568    /* @info Swap this method out for a fetch request to a server that exchanges your auth code securely. */
569    exchangeCodeAsync(
570      /* @end */
571      {
572        clientId: CLIENT_ID,
573        /* @info <b>Never</b> store your client secret in the application code! */
574        clientSecret: "CLIENT_SECRET",
575        /* @end */
576        code,
577        redirectUri,
578      },
579      discovery
580    )
581      .then((token) => {
582        if (isMounted.current) {
583          setState({ token, exchangeError: null });
584        }
585      })
586      .catch((exchangeError) => {
587        if (isMounted.current) {
588          setState({ exchangeError, token: null });
589        }
590      });
591  }, [code]);
592
593  return state;
594}
595
596function useMounted() {
597  const isMounted = React.useRef(true);
598  React.useEffect(() => {
599    return () => {
600      isMounted.current = false;
601    };
602  }, []);
603  return isMounted;
604}
605```
606
607</Tab>
608
609<Tab>
610
611- Coinbase does not support implicit grant.
612
613</Tab>
614</Tabs>
615</Box>
616{/* End Coinbase */}
617
618<Box
619  name="Dropbox"
620  createUrl="https://www.dropbox.com/developers/apps/create"
621  image={ASSETS.dropbox}
622>
623
624| Website                      | Provider  | PKCE          | Auto Discovery |
625| ---------------------------- | --------- | ------------- | -------------- |
626| [Get Your Config][c-dropbox] | OAuth 2.0 | Not Supported | Not Available  |
627
628[c-dropbox]: https://www.dropbox.com/developers/apps/create
629
630- Scopes must be an empty array.
631- PKCE must be disabled (`usePKCE: false`) otherwise you'll get an error about `code_challenge` being included in the query string.
632- Implicit auth is supported.
633- 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.
634
635<Tabs tabs={["Auth Code", "Implicit Flow"]}>
636
637<Tab>
638
639Auth code responses (`ResponseType.Code`) will only work in native with `useProxy: true`.
640
641{/* prettier-ignore */}
642```tsx
643import * as React from 'react';
644import * as WebBrowser from 'expo-web-browser';
645import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
646import { Button, Platform } from 'react-native';
647
648/* @info <strong>Web only:</strong> 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. */
649WebBrowser.maybeCompleteAuthSession();
650/* @end */
651
652// Endpoint
653const discovery = {
654  authorizationEndpoint: 'https://www.dropbox.com/oauth2/authorize',
655  tokenEndpoint: 'https://www.dropbox.com/oauth2/token',
656};
657
658/* @info Implicit auth is universal, <code>.Code</code> will only work in native with <code>useProxy: true</code>. */
659const useProxy = Platform.select({ web: false, default: true });
660/* @end */
661
662export default function App() {
663  const [request, response, promptAsync] = useAuthRequest(
664    {
665      clientId: 'CLIENT_ID',
666      // There are no scopes so just pass an empty array
667      scopes: [],
668      // Dropbox doesn't support PKCE
669      usePKCE: false,
670      // For usage in managed apps using the proxy
671      redirectUri: makeRedirectUri({
672        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
673        scheme: 'your.app',
674        /* @end */
675        useProxy,
676      }),
677    },
678    discovery
679  );
680
681  React.useEffect(() => {
682    if (response?.type === 'success') {
683      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
684      const { code } = response.params;
685      /* @end */
686    }
687  }, [response]);
688
689  return (
690    <Button
691      /* @info Disable the button until the request is loaded asynchronously. */
692      disabled={!request}
693      /* @end */
694      title="Login"
695      onPress={() => {
696        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
697        promptAsync({ useProxy });
698        /* @end */
699      }}
700    />
701  );
702}
703```
704
705</Tab>
706
707<Tab>
708
709{/* prettier-ignore */}
710```tsx
711import * as React from 'react';
712import * as WebBrowser from 'expo-web-browser';
713import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
714import { Button } from 'react-native';
715
716/* @info <strong>Web only:</strong> 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. */
717WebBrowser.maybeCompleteAuthSession();
718/* @end */
719
720// Endpoint
721const discovery = {
722  authorizationEndpoint: 'https://www.dropbox.com/oauth2/authorize',
723  tokenEndpoint: 'https://www.dropbox.com/oauth2/token',
724};
725
726export default function App() {
727  const [request, response, promptAsync] = useAuthRequest(
728    {
729      /* @info Request that the server returns an <code>access_token</code>, not all providers support this. */
730      responseType: ResponseType.Token,
731      /* @end */
732      clientId: 'CLIENT_ID',
733      // There are no scopes so just pass an empty array
734      scopes: [],
735      // Dropbox doesn't support PKCE
736      usePKCE: false,
737      redirectUri: makeRedirectUri({
738        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
739        scheme: 'your.app'
740        /* @end */
741      }),
742    },
743    discovery
744  );
745
746  React.useEffect(() => {
747    if (response?.type === 'success') {
748      /* @info Use this access token to interact with user data on the provider's server. */
749      const { access_token } = response.params;
750      /* @end */
751    }
752  }, [response]);
753
754  return (
755    <Button
756      /* @info Disable the button until the request is loaded asynchronously. */
757      disabled={!request}
758      /* @end */
759      title="Login"
760      onPress={() => {
761        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
762        promptAsync();
763        /* @end */
764      }}
765    />
766  );
767}
768```
769
770</Tab>
771</Tabs>
772</Box>
773{/* End Dropbox */}
774
775<Box
776  name="Fitbit"
777  createUrl="https://dev.fitbit.com/apps/new"
778  image={ASSETS.fitbit}
779>
780
781| Website                     | Provider  | PKCE      | Auto Discovery |
782| --------------------------- | --------- | --------- | -------------- |
783| [Get Your Config][c-fitbit] | OAuth 2.0 | Supported | Not Available  |
784
785[c-fitbit]: https://dev.fitbit.com/apps/new
786
787- Provider only allows one redirect URI per app. You'll need an individual app for every method you want to use:
788  - Expo Go: `exp://localhost:8081/--/*`. For SDK 48 and lower, the port number is `19000`.
789  - Expo Go + Proxy: `https://auth.expo.io/@you/your-app`
790  - Standalone or Bare: `com.your.app://*`
791  - Web: `https://yourwebsite.com/*`
792- The `redirectUri` requires 2 slashes (`://`).
793
794<Tabs tabs={["Auth Code", "Implicit Flow"]}>
795<Tab>
796
797{/* prettier-ignore */}
798```tsx
799import * as React from 'react';
800import * as WebBrowser from 'expo-web-browser';
801import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
802import { Button, Platform } from 'react-native';
803
804/* @info <strong>Web only:</strong> 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. */
805WebBrowser.maybeCompleteAuthSession();
806/* @end */
807
808// Endpoint
809const discovery = {
810  authorizationEndpoint: 'https://www.fitbit.com/oauth2/authorize',
811  tokenEndpoint: 'https://api.fitbit.com/oauth2/token',
812  revocationEndpoint: 'https://api.fitbit.com/oauth2/revoke',
813};
814
815export default function App() {
816  const [request, response, promptAsync] = useAuthRequest(
817    {
818      clientId: 'CLIENT_ID',
819      scopes: ['activity', 'sleep'],
820      redirectUri: makeRedirectUri({
821        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
822        scheme: 'your.app'
823        /* @end */
824      }),
825    },
826    discovery
827  );
828
829  React.useEffect(() => {
830    if (response?.type === 'success') {
831      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
832      const { code } = response.params;
833      /* @end */
834    }
835  }, [response]);
836
837  return (
838    <Button
839      /* @info Disable the button until the request is loaded asynchronously. */
840      disabled={!request}
841      /* @end */
842      title="Login"
843      onPress={() => {
844        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
845        promptAsync();
846        /* @end */
847      }}
848    />
849  );
850}
851```
852
853</Tab>
854
855<Tab>
856
857{/* prettier-ignore */}
858```tsx
859import * as React from 'react';
860import * as WebBrowser from 'expo-web-browser';
861import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
862import { Button, Platform } from 'react-native';
863
864/* @info <strong>Web only:</strong> 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. */
865WebBrowser.maybeCompleteAuthSession();
866/* @end */
867
868const useProxy = Platform.select({ web: false, default: true });
869
870// Endpoint
871const discovery = {
872  authorizationEndpoint: 'https://www.fitbit.com/oauth2/authorize',
873  tokenEndpoint: 'https://api.fitbit.com/oauth2/token',
874  revocationEndpoint: 'https://api.fitbit.com/oauth2/revoke',
875};
876
877export default function App() {
878  const [request, response, promptAsync] = useAuthRequest(
879    {
880      /* @info Request that the server returns an <code>access_token</code>, not all providers support this. */
881      responseType: ResponseType.Token,
882      /* @end */
883      clientId: 'CLIENT_ID',
884      scopes: ['activity', 'sleep'],
885      // For usage in managed apps using the proxy
886      redirectUri: makeRedirectUri({
887        useProxy,
888        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
889        scheme: 'your.app'
890        /* @end */
891      }),
892    },
893    discovery
894  );
895
896  React.useEffect(() => {
897    if (response?.type === 'success') {
898      /* @info Use this access token to interact with user data on the provider's server. */
899      const { access_token } = response.params;
900      /* @end */
901    }
902  }, [response]);
903
904  return (
905    <Button
906      /* @info Disable the button until the request is loaded asynchronously. */
907      disabled={!request}
908      /* @end */
909      title="Login"
910      onPress={() => {
911        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
912        promptAsync({ useProxy });
913        /* @end */
914      }}
915    />
916  );
917}
918```
919
920</Tab>
921</Tabs>
922</Box>
923{/* End FitBit */}
924
925<Box
926  name="GitHub"
927  createUrl="https://github.com/settings/developers"
928  image={ASSETS.github}
929>
930
931| Website                     | Provider  | PKCE      | Auto Discovery |
932| --------------------------- | --------- | --------- | -------------- |
933| [Get Your Config][c-github] | OAuth 2.0 | Supported | Not Available  |
934
935[c-github]: https://github.com/settings/developers
936
937- Provider only allows one redirect URI per app. You'll need an individual app for every method you want to use:
938  - Expo Go: `exp://localhost:8081/--/*`. For SDK 48 and lower, the port number is `19000`.
939  - Expo Go + Proxy: `https://auth.expo.io/@you/your-app`
940  - Standalone or Bare: `com.your.app://*`
941  - Web: `https://yourwebsite.com/*`
942- The `redirectUri` requires 2 slashes (`://`).
943- `revocationEndpoint` is dynamic and requires your `config.clientId`.
944
945<Tabs tabs={["Auth Code", "Implicit Flow"]}>
946<Tab>
947
948{/* prettier-ignore */}
949```tsx
950import * as React from 'react';
951import * as WebBrowser from 'expo-web-browser';
952import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
953import { Button } from 'react-native';
954
955/* @info <strong>Web only:</strong> 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. */
956WebBrowser.maybeCompleteAuthSession();
957/* @end */
958
959// Endpoint
960const discovery = {
961  authorizationEndpoint: 'https://github.com/login/oauth/authorize',
962  tokenEndpoint: 'https://github.com/login/oauth/access_token',
963  revocationEndpoint: 'https://github.com/settings/connections/applications/<CLIENT_ID>',
964};
965
966export default function App() {
967  const [request, response, promptAsync] = useAuthRequest(
968    {
969      clientId: 'CLIENT_ID',
970      scopes: ['identity'],
971      redirectUri: makeRedirectUri({
972        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
973        scheme: 'your.app'
974        /* @end */
975      }),
976    },
977    discovery
978  );
979
980  React.useEffect(() => {
981    if (response?.type === 'success') {
982      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
983      const { code } = response.params;
984      /* @end */
985    }
986  }, [response]);
987
988  return (
989    <Button
990      /* @info Disable the button until the request is loaded asynchronously. */
991      disabled={!request}
992      /* @end */
993      title="Login"
994      onPress={() => {
995        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
996        promptAsync();
997        /* @end */
998      }}
999    />
1000  );
1001}
1002```
1003
1004</Tab>
1005
1006<Tab>
1007
1008- Implicit grant is [not supported for GitHub](https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/).
1009
1010</Tab>
1011</Tabs>
1012</Box>
1013
1014<Box
1015  name="Imgur"
1016  createUrl="https://api.imgur.com/oauth2/addclient"
1017  image={ASSETS.imgur}
1018>
1019
1020| Website                    | Provider  | PKCE      | Auto Discovery |
1021| -------------------------- | --------- | --------- | -------------- |
1022| [Get Your Config][c-imgur] | OAuth 2.0 | Supported | Not Available  |
1023
1024[c-imgur]: https://api.imgur.com/oauth2/addclient
1025
1026- You will need to create a different provider app for each platform (dynamically choosing your `clientId`).
1027- Learn more here: [imgur.com/oauth2](https://api.imgur.com/oauth2)
1028
1029<Tabs tabs={["Auth Code", "Implicit Flow"]}>
1030<Tab>
1031
1032```tsx
1033import * as React from 'react';
1034import * as WebBrowser from 'expo-web-browser';
1035import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
1036import { Button, Platform } from 'react-native';
1037
1038/* @info <strong>Web only:</strong> 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. */
1039WebBrowser.maybeCompleteAuthSession();
1040/* @end */
1041
1042const discovery = {
1043  authorizationEndpoint: 'https://api.imgur.com/oauth2/authorize',
1044  tokenEndpoint: 'https://api.imgur.com/oauth2/token',
1045};
1046
1047export default function App() {
1048  // Request
1049  const [request, response, promptAsync] = useAuthRequest(
1050    {
1051      clientId: 'CLIENT_ID',
1052      clientSecret: 'CLIENT_SECRET',
1053      redirectUri: makeRedirectUri({
1054        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
1055        scheme: 'your.app',
1056        /* @end */
1057      }),
1058      // imgur requires an empty array
1059      scopes: [],
1060    },
1061    discovery
1062  );
1063
1064  React.useEffect(() => {
1065    if (response?.type === 'success') {
1066      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
1067      const { code } = response.params;
1068      /* @end */
1069    }
1070  }, [response]);
1071
1072  return (
1073    <Button
1074      /* @info Disable the button until the request is loaded asynchronously. */
1075      disabled={!request}
1076      /* @end */
1077      title="Login"
1078      onPress={() => {
1079        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1080        promptAsync();
1081        /* @end */
1082      }}
1083    />
1084  );
1085}
1086```
1087
1088</Tab>
1089
1090<Tab>
1091
1092```tsx
1093import * as React from 'react';
1094import * as WebBrowser from 'expo-web-browser';
1095import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
1096import { Button, Platform } from 'react-native';
1097
1098/* @info <strong>Web only:</strong> 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. */
1099WebBrowser.maybeCompleteAuthSession();
1100/* @end */
1101
1102const discovery = {
1103  authorizationEndpoint: 'https://api.imgur.com/oauth2/authorize',
1104  tokenEndpoint: 'https://api.imgur.com/oauth2/token',
1105};
1106
1107export default function App() {
1108  // Request
1109  const [request, response, promptAsync] = useAuthRequest(
1110    {
1111      /* @info Request that the server returns an <code>access_token</code>, not all providers support this. */
1112      responseType: ResponseType.Token,
1113      /* @end */
1114      clientId: 'CLIENT_ID',
1115      redirectUri: makeRedirectUri({
1116        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
1117        scheme: 'your.app',
1118        /* @end */
1119      }),
1120      scopes: [],
1121    },
1122    discovery
1123  );
1124
1125  React.useEffect(() => {
1126    if (response?.type === 'success') {
1127      /* @info Use this access token to interact with user data on the provider's server. */
1128      const { access_token } = response.params;
1129      /* @end */
1130    }
1131  }, [response]);
1132
1133  return (
1134    <Button
1135      /* @info Disable the button until the request is loaded asynchronously. */
1136      disabled={!request}
1137      /* @end */
1138      title="Login"
1139      onPress={() => {
1140        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1141        promptAsync();
1142        /* @end */
1143      }}
1144    />
1145  );
1146}
1147```
1148
1149</Tab>
1150</Tabs>
1151</Box>
1152{/* End Imgur */}
1153
1154<Box
1155  name="Okta"
1156  createUrl="https://developer.okta.com/signup"
1157  image={ASSETS.okta}
1158>
1159
1160| Website                          | Provider | PKCE      | Auto Discovery |
1161| -------------------------------- | -------- | --------- | -------------- |
1162| [Sign-up][c-okta] > Applications | OpenID   | Supported | Available      |
1163
1164[c-okta]: https://developer.okta.com/signup/
1165
1166- You cannot define a custom `redirectUri`, Okta will provide you with one.
1167- You can use the Expo proxy to test this without a native rebuild, just be sure to configure the project as a website.
1168
1169<Tabs tabs={["Auth Code", "Implicit Flow"]}>
1170<Tab>
1171
1172{/* prettier-ignore */}
1173```tsx
1174import * as React from 'react';
1175import * as WebBrowser from 'expo-web-browser';
1176import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
1177import { Button, Platform } from 'react-native';
1178
1179/* @info <strong>Web only:</strong> 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. */
1180WebBrowser.maybeCompleteAuthSession();
1181/* @end */
1182
1183const useProxy = Platform.select({ web: false, default: true });
1184
1185export default function App() {
1186  // Endpoint
1187  const discovery = useAutoDiscovery('https://<OKTA_DOMAIN>.com/oauth2/default');
1188  // Request
1189  const [request, response, promptAsync] = useAuthRequest(
1190    {
1191      clientId: 'CLIENT_ID',
1192      scopes: ['openid', 'profile'],
1193      // For usage in managed apps using the proxy
1194      redirectUri: makeRedirectUri({
1195        // For usage in bare and standalone
1196        native: 'com.okta.<OKTA_DOMAIN>:/callback',
1197        useProxy,
1198      }),
1199    },
1200    discovery
1201  );
1202
1203  React.useEffect(() => {
1204    if (response?.type === 'success') {
1205      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
1206      const { code } = response.params;
1207      /* @end */
1208    }
1209  }, [response]);
1210
1211  return (
1212    <Button
1213      /* @info Disable the button until the request is loaded asynchronously. */
1214      disabled={!request}
1215      /* @end */
1216      title="Login"
1217      onPress={() => {
1218        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1219        promptAsync({ useProxy });
1220        /* @end */
1221      }}
1222    />
1223  );
1224}
1225```
1226
1227</Tab>
1228
1229<Tab>
1230
1231- This flow is not documented yet, learn more [from the Okta website](https://developer.okta.com/docs/guides/implement-implicit/use-flow/).
1232
1233</Tab>
1234</Tabs>
1235</Box>
1236{/* End Okta */}
1237
1238<Box
1239  name="Reddit"
1240  createUrl="https://www.reddit.com/prefs/apps"
1241  image={ASSETS.reddit}
1242>
1243
1244| Website                     | Provider  | PKCE      | Auto Discovery |
1245| --------------------------- | --------- | --------- | -------------- |
1246| [Get Your Config][c-reddit] | OAuth 2.0 | Supported | Not Available  |
1247
1248[c-reddit]: https://www.reddit.com/prefs/apps
1249
1250- Provider only allows one redirect URI per app. You'll need an individual app for every method you want to use:
1251  - Expo Go: `exp://localhost:8081/--/*`. For SDK 48 and lower, the port number is `19000`.
1252  - Expo Go + Proxy: `https://auth.expo.io/@you/your-app`
1253  - Standalone or Bare: `com.your.app://*`
1254  - Web: `https://yourwebsite.com/*`
1255- The `redirectUri` requires 2 slashes (`://`).
1256
1257<Tabs tabs={["Auth Code", "Implicit Flow"]}>
1258
1259<Tab>
1260
1261{/* prettier-ignore */}
1262```tsx
1263import * as React from 'react';
1264import * as WebBrowser from 'expo-web-browser';
1265import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
1266import { Button } from 'react-native';
1267
1268/* @info <strong>Web only:</strong> 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. */
1269WebBrowser.maybeCompleteAuthSession();
1270/* @end */
1271
1272// Endpoint
1273const discovery = {
1274  authorizationEndpoint: 'https://www.reddit.com/api/v1/authorize.compact',
1275  tokenEndpoint: 'https://www.reddit.com/api/v1/access_token',
1276};
1277
1278export default function App() {
1279  const [request, response, promptAsync] = useAuthRequest(
1280    {
1281      clientId: 'CLIENT_ID',
1282      scopes: ['identity'],
1283      redirectUri: makeRedirectUri({
1284        // For usage in bare and standalone
1285        native: 'your.app://redirect',
1286      }),
1287    },
1288    discovery
1289  );
1290
1291  React.useEffect(() => {
1292    if (response?.type === 'success') {
1293      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
1294      const { code } = response.params;
1295      /* @end */
1296    }
1297  }, [response]);
1298
1299  return (
1300    <Button
1301      /* @info Disable the button until the request is loaded asynchronously. */
1302      disabled={!request}
1303      /* @end */
1304      title="Login"
1305      onPress={() => {
1306        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1307        promptAsync();
1308        /* @end */
1309      }}
1310    />
1311  );
1312}
1313```
1314
1315</Tab>
1316
1317<Tab>
1318
1319- You must select the `installed` option for your app on Reddit to use implicit grant.
1320
1321{/* prettier-ignore */}
1322```tsx
1323import * as React from 'react';
1324import * as WebBrowser from 'expo-web-browser';
1325import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
1326import { Button } from 'react-native';
1327
1328/* @info <strong>Web only:</strong> 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. */
1329WebBrowser.maybeCompleteAuthSession();
1330/* @end */
1331
1332// Endpoint
1333const discovery = {
1334  authorizationEndpoint: 'https://www.reddit.com/api/v1/authorize.compact',
1335  tokenEndpoint: 'https://www.reddit.com/api/v1/access_token',
1336};
1337
1338export default function App() {
1339  const [request, response, promptAsync] = useAuthRequest(
1340    {
1341      /* @info Request that the server returns an <code>access_token</code>, not all providers support this. */
1342      responseType: ResponseType.Token,
1343      /* @end */
1344      clientId: 'CLIENT_ID',
1345      scopes: ['identity'],
1346      redirectUri: makeRedirectUri({
1347        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
1348        scheme: 'your.app'
1349        /* @end */
1350      }),
1351    },
1352    discovery
1353  );
1354
1355  React.useEffect(() => {
1356    if (response?.type === 'success') {
1357      /* @info Use this access token to interact with user data on the provider's server. */
1358      const { access_token } = response.params;
1359      /* @end */
1360    }
1361  }, [response]);
1362
1363  return (
1364    <Button
1365      /* @info Disable the button until the request is loaded asynchronously. */
1366      disabled={!request}
1367      /* @end */
1368      title="Login"
1369      onPress={() => {
1370        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1371        promptAsync();
1372        /* @end */
1373      }}
1374    />
1375  );
1376}
1377```
1378
1379</Tab>
1380</Tabs>
1381</Box>
1382{/* End Reddit */}
1383
1384<Box
1385  name="Slack"
1386  createUrl="https://api.slack.com/apps"
1387  image={ASSETS.slack}
1388>
1389
1390| Website                    | Provider  | PKCE      | Auto Discovery |
1391| -------------------------- | --------- | --------- | -------------- |
1392| [Get Your Config][c-slack] | OAuth 2.0 | Supported | Not Available  |
1393
1394[c-slack]: https://api.slack.com/apps
1395
1396- The `redirectUri` requires 2 slashes (`://`).
1397- `redirectUri` can be defined under the "OAuth & Permissions" section of the website.
1398- `clientId` and `clientSecret` can be found in the **"App Credentials"** section.
1399- Scopes must be joined with ':' so just create one long string.
1400- Navigate to the **"Scopes"** section to enable scopes.
1401- `revocationEndpoint` is not available.
1402
1403<Tabs tabs={["Auth Code", "Implicit Flow"]}>
1404
1405<Tab>
1406
1407{/* prettier-ignore */}
1408```tsx
1409import * as React from 'react';
1410import * as WebBrowser from 'expo-web-browser';
1411import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
1412import { Button } from 'react-native';
1413
1414/* @info <strong>Web only:</strong> 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. */
1415WebBrowser.maybeCompleteAuthSession();
1416/* @end */
1417
1418// Endpoint
1419const discovery = {
1420  authorizationEndpoint: 'https://slack.com/oauth/authorize',
1421  tokenEndpoint: 'https://slack.com/api/oauth.access',
1422};
1423
1424export default function App() {
1425  const [request, response, promptAsync] = useAuthRequest(
1426    {
1427      clientId: 'CLIENT_ID',
1428      scopes: ['emoji:read'],
1429      redirectUri: makeRedirectUri({
1430        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
1431        scheme: 'your.app'
1432        /* @end */
1433      }),
1434    },
1435    discovery
1436  );
1437
1438  React.useEffect(() => {
1439    if (response?.type === 'success') {
1440      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
1441      const { code } = response.params;
1442      /* @end */
1443    }
1444  }, [response]);
1445
1446  return (
1447    <Button
1448      /* @info Disable the button until the request is loaded asynchronously. */
1449      disabled={!request}
1450      /* @end */
1451      title="Login"
1452      onPress={() => {
1453        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1454        promptAsync();
1455        /* @end */
1456      }}
1457    />
1458  );
1459}
1460```
1461
1462</Tab>
1463
1464<Tab>
1465
1466- Slack does not support implicit grant.
1467
1468</Tab>
1469</Tabs>
1470</Box>
1471{/* End Slack */}
1472
1473<Box
1474  name="Spotify"
1475  createUrl="https://developer.spotify.com/dashboard/applications"
1476  image={ASSETS.spotify}
1477>
1478
1479| Website                      | Provider  | PKCE      | Auto Discovery |
1480| ---------------------------- | --------- | --------- | -------------- |
1481| [Get Your Config][c-spotify] | OAuth 2.0 | Supported | Not Available  |
1482
1483[c-spotify]: https://developer.spotify.com/dashboard/applications
1484
1485- Setup your redirect URIs: Your project > Edit Settings > Redirect URIs (be sure to save after making changes).
1486  - _Expo Go_: `exp://localhost:8081/--/`. For SDK 48 and lower, the port number is `19000`.
1487  - _Web dev_: `https://localhost:19006`
1488    - Important: Ensure there's no slash at the end of the URL unless manually changed in the app code with `makeRedirectUri({ path: '/' })`.
1489    - Run `expo start --web --https` to run with **https**, auth won't work otherwise.
1490  - _Custom app_: `your-scheme://`
1491    - Scheme should be specified in app.json `expo.scheme: 'your-scheme'`, then added to the app code with `makeRedirectUri({ native: 'your-scheme://' })`)
1492  - _Proxy_: `https://auth.expo.io/@username/slug`
1493    - 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.
1494  - _Web production_: `https://yourwebsite.com`
1495    - Set this to whatever your deployed website URL is.
1496- For simpler authentication, use the **Implicit Flow** as it'll return an `access_token` without the need for a code exchange server request.
1497- Learn more about the [Spotify API](https://developer.spotify.com/documentation/web-api/).
1498
1499<Tabs tabs={["Auth Code", "Implicit Flow"]}>
1500<Tab>
1501
1502{/* prettier-ignore */}
1503```tsx
1504import * as React from 'react';
1505import * as WebBrowser from 'expo-web-browser';
1506import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
1507import { Button } from 'react-native';
1508
1509/* @info <strong>Web only:</strong> 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. */
1510WebBrowser.maybeCompleteAuthSession();
1511/* @end */
1512
1513// Endpoint
1514const discovery = {
1515  authorizationEndpoint: 'https://accounts.spotify.com/authorize',
1516  tokenEndpoint: 'https://accounts.spotify.com/api/token',
1517};
1518
1519export default function App() {
1520  const [request, response, promptAsync] = useAuthRequest(
1521    {
1522      clientId: 'CLIENT_ID',
1523      scopes: ['user-read-email', 'playlist-modify-public'],
1524      // In order to follow the "Authorization Code Flow" to fetch token after authorizationEndpoint
1525      // this must be set to false
1526      usePKCE: false,
1527      redirectUri: makeRedirectUri({
1528        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
1529        scheme: 'your.app'
1530        /* @end */
1531      }),
1532    },
1533    discovery
1534  );
1535
1536  React.useEffect(() => {
1537    if (response?.type === 'success') {
1538      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
1539      const { code } = response.params;
1540      /* @end */
1541    }
1542  }, [response]);
1543
1544  return (
1545    <Button
1546      /* @info Disable the button until the request is loaded asynchronously. */
1547      disabled={!request}
1548      /* @end */
1549      title="Login"
1550      onPress={() => {
1551        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1552        promptAsync();
1553        /* @end */
1554      }}
1555    />
1556  );
1557}
1558```
1559
1560</Tab>
1561
1562<Tab>
1563
1564{/* prettier-ignore */}
1565```tsx
1566import * as React from 'react';
1567import * as WebBrowser from 'expo-web-browser';
1568import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
1569import { Button } from 'react-native';
1570
1571/* @info <strong>Web only:</strong> 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. */
1572WebBrowser.maybeCompleteAuthSession();
1573/* @end */
1574
1575// Endpoint
1576const discovery = {
1577  authorizationEndpoint: 'https://accounts.spotify.com/authorize',
1578  tokenEndpoint: 'https://accounts.spotify.com/api/token',
1579};
1580
1581export default function App() {
1582  const [request, response, promptAsync] = useAuthRequest(
1583    {
1584      /* @info Request that the server returns an <code>access_token</code>, not all providers support this. */
1585      responseType: ResponseType.Token,
1586      /* @end */
1587      clientId: 'CLIENT_ID',
1588      scopes: ['user-read-email', 'playlist-modify-public'],
1589      // In order to follow the "Authorization Code Flow" to fetch token after authorizationEndpoint
1590      // this must be set to false
1591      usePKCE: false,
1592      redirectUri: makeRedirectUri({
1593        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
1594        scheme: 'your.app'
1595        /* @end */
1596      }),
1597    },
1598    discovery
1599  );
1600
1601  React.useEffect(() => {
1602    if (response?.type === 'success') {
1603      /* @info Use this access token to interact with user data on the provider's server. */
1604      const { access_token } = response.params;
1605      /* @end */
1606    }
1607  }, [response]);
1608
1609  return (
1610    <Button
1611      /* @info Disable the button until the request is loaded asynchronously. */
1612      disabled={!request}
1613      /* @end */
1614      title="Login"
1615      onPress={() => {
1616        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1617        promptAsync();
1618        /* @end */
1619      }}
1620    />
1621  );
1622}
1623```
1624
1625</Tab>
1626</Tabs>
1627</Box>
1628{/* End Spotify */}
1629
1630<Box
1631  name="Strava"
1632  createUrl="https://www.strava.com/settings/api"
1633  image={ASSETS.strava}
1634>
1635
1636| Website                     | Provider  | PKCE      | Auto Discovery |
1637| --------------------------- | --------- | --------- | -------------- |
1638| [Get Your Config][c-strava] | OAuth 2.0 | Supported | Not Available  |
1639
1640[c-strava]: https://www.strava.com/settings/api
1641
1642- Learn more about the [Strava API](http://developers.strava.com/docs/reference/).
1643- 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`.
1644- No Implicit auth flow is provided by Strava.
1645
1646<Tabs tabs={["Auth Code"]}>
1647<Tab>
1648
1649{/* prettier-ignore */}
1650```tsx
1651import * as React from 'react';
1652import * as WebBrowser from 'expo-web-browser';
1653import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
1654import { Button } from 'react-native';
1655
1656/* @info <strong>Web only:</strong> 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. */
1657WebBrowser.maybeCompleteAuthSession();
1658/* @end */
1659
1660// Endpoint
1661const discovery = {
1662  authorizationEndpoint: 'https://www.strava.com/oauth/mobile/authorize',
1663  tokenEndpoint: 'https://www.strava.com/oauth/token',
1664  revocationEndpoint: 'https://www.strava.com/oauth/deauthorize',
1665};
1666
1667export default function App() {
1668  const [request, response, promptAsync] = useAuthRequest(
1669    {
1670      clientId: 'CLIENT_ID',
1671      scopes: ['activity:read_all'],
1672      redirectUri: makeRedirectUri({
1673        // For usage in bare and standalone
1674        // the "redirect" must match your "Authorization Callback Domain" in the Strava dev console.
1675        native: 'your.app://redirect',
1676      }),
1677    },
1678    discovery
1679  );
1680
1681  React.useEffect(() => {
1682    if (response?.type === 'success') {
1683      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
1684      const { code } = response.params;
1685      /* @end */
1686    }
1687  }, [response]);
1688
1689  return (
1690    <Button
1691      /* @info Disable the button until the request is loaded asynchronously. */
1692      disabled={!request}
1693      /* @end */
1694      title="Login"
1695      onPress={() => {
1696        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1697        promptAsync();
1698        /* @end */
1699      }}
1700    />
1701  );
1702}
1703```
1704
1705Strava doesn't provide an implicit auth flow, you should send the code to a server or serverless function to perform the access token exchange.
1706For **debugging** purposes, you can perform the exchange client-side using the following method:
1707
1708{/* prettier-ignore */}
1709```tsx
1710const { accessToken } = await AuthSession.exchangeCodeAsync(
1711  {
1712    clientId: request?.clientId,
1713    redirectUri,
1714    code: result.params.code,
1715    extraParams: {
1716      // You must use the extraParams variation of clientSecret.
1717      // Never store your client secret on the client.
1718      client_secret: 'CLIENT_SECRET',
1719    },
1720  },
1721  { tokenEndpoint: 'https://www.strava.com/oauth/token' }
1722);
1723```
1724
1725</Tab>
1726</Tabs>
1727</Box>
1728{/* End Strava */}
1729
1730<Box
1731  name="Twitch"
1732  createUrl="https://dev.twitch.tv/console/apps/create"
1733  image={ASSETS.twitch}
1734>
1735
1736| Website                     | Provider | PKCE      | Auto Discovery | Scopes           |
1737| --------------------------- | -------- | --------- | -------------- | ---------------- |
1738| [Get your Config][c-twitch] | OAuth    | Supported | Not Available  | [Info][s-twitch] |
1739
1740[c-twitch]: https://dev.twitch.tv/console/apps/create
1741[s-twitch]: https://dev.twitch.tv/docs/authentication#scopes
1742
1743- You will need to enable 2FA on your Twitch account to create an application.
1744
1745<Tabs tabs={["Auth Code", "Implicit Flow"]}>
1746
1747<Tab>
1748
1749{/* prettier-ignore */}
1750```tsx
1751import * as React from 'react';
1752import * as WebBrowser from 'expo-web-browser';
1753import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
1754import { Button } from 'react-native';
1755
1756/* @info <strong>Web only:</strong> 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. */
1757WebBrowser.maybeCompleteAuthSession();
1758/* @end */
1759
1760// Endpoint
1761const discovery = {
1762  authorizationEndpoint: 'https://id.twitch.tv/oauth2/authorize',
1763  tokenEndpoint: 'https://id.twitch.tv/oauth2/token',
1764  revocationEndpoint: 'https://id.twitch.tv/oauth2/revoke',
1765};
1766
1767export default function App() {
1768  const [request, response, promptAsync] = useAuthRequest(
1769    {
1770      clientId: 'CLIENT_ID',
1771      redirectUri: makeRedirectUri({
1772        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
1773        scheme: 'your.app'
1774        /* @end */
1775      }),
1776      scopes: ['user:read:email', 'analytics:read:games'],
1777    },
1778    discovery
1779  );
1780
1781  React.useEffect(() => {
1782    if (response?.type === 'success') {
1783      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
1784      const { code } = response.params;
1785      /* @end */
1786    }
1787  }, [response]);
1788
1789  return (
1790    <Button
1791      /* @info Disable the button until the request is loaded asynchronously. */
1792      disabled={!request}
1793      /* @end */
1794      title="Login"
1795      onPress={() => {
1796        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1797        promptAsync();
1798        /* @end */
1799      }}
1800    />
1801  );
1802}
1803```
1804
1805</Tab>
1806
1807<Tab>
1808
1809{/* prettier-ignore */}
1810```tsx
1811import * as React from 'react';
1812import * as WebBrowser from 'expo-web-browser';
1813import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
1814import { Button } from 'react-native';
1815
1816/* @info <strong>Web only:</strong> 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. */
1817WebBrowser.maybeCompleteAuthSession();
1818/* @end */
1819
1820// Endpoint
1821const discovery = {
1822  authorizationEndpoint: 'https://id.twitch.tv/oauth2/authorize',
1823  tokenEndpoint: 'https://id.twitch.tv/oauth2/token',
1824  revocationEndpoint: 'https://id.twitch.tv/oauth2/revoke',
1825};
1826
1827export default function App() {
1828  const [request, response, promptAsync] = useAuthRequest(
1829    {
1830      /* @info Request that the server returns an <code>access_token</code>, not all providers support this. */
1831      responseType: ResponseType.Token,
1832      /* @end */
1833      clientId: 'CLIENT_ID',
1834      redirectUri: makeRedirectUri({
1835        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
1836        scheme: 'your.app'
1837        /* @end */
1838      }),
1839      scopes: ['user:read:email', 'analytics:read:games'],
1840    },
1841    discovery
1842  );
1843
1844  React.useEffect(() => {
1845    if (response?.type === 'success') {
1846      /* @info Use this access token to interact with user data on the provider's server. */
1847      const { access_token } = response.params;
1848      /* @end */
1849    }
1850  }, [response]);
1851
1852  return (
1853    <Button
1854      /* @info Disable the button until the request is loaded asynchronously. */
1855      disabled={!request}
1856      /* @end */
1857      title="Login"
1858      onPress={() => {
1859        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1860        promptAsync();
1861        /* @end */
1862      }}
1863    />
1864  );
1865}
1866```
1867
1868</Tab>
1869</Tabs>
1870</Box>
1871{/* End Twitch */}
1872
1873<Box
1874  name="Twitter"
1875  createUrl="https://developer.twitter.com/en/portal/projects/new"
1876  image={ASSETS.twitter}
1877>
1878
1879| Website                      | Provider | PKCE      | Auto Discovery | Scopes            |
1880| ---------------------------- | -------- | --------- | -------------- | ----------------- |
1881| [Get your Config][c-twitter] | OAuth    | Supported | Not Available  | [Info][s-twitter] |
1882
1883[c-twitter]: https://developer.twitter.com/en/portal/projects/new
1884[s-twitter]: https://developer.twitter.com/en/docs/authentication/oauth-2-0/authorization-code
1885
1886- You will need to be approved by Twitter support before you can use the Twitter v2 API.
1887- 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'}`.
1888- Example redirects:
1889  - Expo Go + Proxy: `https://auth.expo.io/@you/your-app`
1890  - Standalone or Bare: `com.your.app://`
1891  - Web (dev `expo start --https`): `https://localhost:19006` (no ending slash)
1892- The `redirectUri` requires 2 slashes (`://`).
1893
1894#### Expo Go
1895
1896You must use the proxy service in the Expo Go app because `exp://localhost:8081` cannot be added to your Twitter app as a redirect.
1897
1898<Tabs tabs={["Auth Code"]}>
1899
1900<Tab>
1901
1902{/* prettier-ignore */}
1903```tsx
1904import * as React from 'react';
1905import * as WebBrowser from 'expo-web-browser';
1906import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
1907import { Button, Platform } from 'react-native';
1908
1909const useProxy = Platform.select({ web: false, default: true });
1910
1911/* @info <strong>Web only:</strong> 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. */
1912WebBrowser.maybeCompleteAuthSession();
1913/* @end */
1914
1915// Endpoint
1916const discovery = {
1917  authorizationEndpoint: "https://twitter.com/i/oauth2/authorize",
1918  tokenEndpoint: "https://twitter.com/i/oauth2/token",
1919  revocationEndpoint: "https://twitter.com/i/oauth2/revoke",
1920};
1921
1922export default function App() {
1923  const [request, response, promptAsync] = useAuthRequest(
1924    {
1925      clientId: 'CLIENT_ID',
1926      redirectUri: makeRedirectUri({
1927        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
1928        scheme: 'your.app',
1929        /* @end */
1930        useProxy
1931      }),
1932      usePKCE: true,
1933      scopes: [
1934        "tweet.read",
1935      ],
1936    },
1937    discovery
1938  );
1939
1940  React.useEffect(() => {
1941    if (response?.type === 'success') {
1942      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
1943      const { code } = response.params;
1944      /* @end */
1945    }
1946  }, [response]);
1947
1948  return (
1949    <Button
1950      /* @info Disable the button until the request is loaded asynchronously. */
1951      disabled={!request}
1952      /* @end */
1953      title="Login"
1954      onPress={() => {
1955        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
1956        promptAsync({ useProxy });
1957        /* @end */
1958      }}
1959    />
1960  );
1961}
1962```
1963
1964</Tab>
1965</Tabs>
1966</Box>
1967{/* End Twitter */}
1968
1969<Box
1970  name="Uber"
1971  createUrl="https://developer.uber.com/docs/riders/guides/authentication/introduction"
1972  image={ASSETS.uber}
1973>
1974
1975| Website                   | Provider  | PKCE      | Auto Discovery |
1976| ------------------------- | --------- | --------- | -------------- |
1977| [Get Your Config][c-uber] | OAuth 2.0 | Supported | Not Available  |
1978
1979[c-uber]: https://developer.uber.com/docs/riders/guides/authentication/introduction
1980
1981- The `redirectUri` requires 2 slashes (`://`).
1982- `scopes` can be difficult to get approved.
1983
1984<Tabs tabs={["Auth Code", "Implicit Flow"]}>
1985
1986<Tab>
1987
1988{/* prettier-ignore */}
1989```tsx
1990import * as React from 'react';
1991import * as WebBrowser from 'expo-web-browser';
1992import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
1993import { Button } from 'react-native';
1994
1995/* @info <strong>Web only:</strong> 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. */
1996WebBrowser.maybeCompleteAuthSession();
1997/* @end */
1998
1999// Endpoint
2000const discovery = {
2001  authorizationEndpoint: 'https://login.uber.com/oauth/v2/authorize',
2002  tokenEndpoint: 'https://login.uber.com/oauth/v2/token',
2003  revocationEndpoint: 'https://login.uber.com/oauth/v2/revoke',
2004};
2005
2006export default function App() {
2007  const [request, response, promptAsync] = useAuthRequest(
2008    {
2009      clientId: 'CLIENT_ID',
2010      scopes: ['profile', 'delivery'],
2011      redirectUri: makeRedirectUri({
2012        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
2013        scheme: 'your.app'
2014        /* @end */
2015      }),
2016    },
2017    discovery
2018  );
2019
2020  React.useEffect(() => {
2021    if (response?.type === 'success') {
2022      /* @info Exchange the code for an access token in a server. Alternatively you can use the <b>Implicit</b> auth method. */
2023      const { code } = response.params;
2024      /* @end */
2025    }
2026  }, [response]);
2027
2028  return (
2029    <Button
2030      /* @info Disable the button until the request is loaded asynchronously. */
2031      disabled={!request}
2032      /* @end */
2033      title="Login"
2034      onPress={() => {
2035        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
2036        promptAsync();
2037        /* @end */
2038      }}
2039    />
2040  );
2041}
2042```
2043
2044</Tab>
2045
2046<Tab>
2047
2048{/* prettier-ignore */}
2049```tsx
2050import * as React from 'react';
2051import * as WebBrowser from 'expo-web-browser';
2052import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
2053import { Button } from 'react-native';
2054
2055/* @info <strong>Web only:</strong> 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. */
2056WebBrowser.maybeCompleteAuthSession();
2057/* @end */
2058
2059// Endpoint
2060const discovery = {
2061  authorizationEndpoint: 'https://login.uber.com/oauth/v2/authorize',
2062  tokenEndpoint: 'https://login.uber.com/oauth/v2/token',
2063  revocationEndpoint: 'https://login.uber.com/oauth/v2/revoke',
2064};
2065
2066export default function App() {
2067  const [request, response, promptAsync] = useAuthRequest(
2068    {
2069      /* @info Request that the server returns an <code>access_token</code>, not all providers support this. */
2070      responseType: ResponseType.Token,
2071      /* @end */
2072      clientId: 'CLIENT_ID',
2073      scopes: ['profile', 'delivery'],
2074      redirectUri: makeRedirectUri({
2075        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
2076        scheme: 'your.app'
2077        /* @end */
2078      }),
2079    },
2080    discovery
2081  );
2082
2083  React.useEffect(() => {
2084    if (response?.type === 'success') {
2085      /* @info Use this access token to interact with user data on the provider's server. */
2086      const { access_token } = response.params;
2087      /* @end */
2088    }
2089  }, [response]);
2090
2091  return (
2092    <Button
2093      /* @info Disable the button until the request is loaded asynchronously. */
2094      disabled={!request}
2095      /* @end */
2096      title="Login"
2097      onPress={() => {
2098        /* @info Prompt the user to authenticate in a user interaction or web browsers will block it. */
2099        promptAsync();
2100        /* @end */
2101      }}
2102    />
2103  );
2104}
2105```
2106
2107</Tab>
2108</Tabs>
2109</Box>
2110{/* End Uber */}
2111
2112{/* End Guides */}
2113
2114## Redirect URI patterns
2115
2116Here are a few examples of some common redirect URI patterns you may end up using.
2117
2118### Standalone and Bare
2119
2120> `yourscheme://path`
2121
2122In some cases there will be anywhere between 1 to 3 slashes (`/`).
2123
2124- **Environment:**
2125
2126  - Bare workflow
2127    - `npx expo prebuild`
2128  - Standalone builds in the App or Play Store or testing locally
2129    - Android: `eas build` or `npx expo run:android`
2130    - iOS: `eas build` or `npx expo run:ios`
2131
2132- **Create:** Use `AuthSession.makeRedirectUri({ native: '<YOUR_URI>' })` to select native when running in the correct environment.
2133  - `your.app://redirect` -> `makeRedirectUri({ scheme: 'your.app', path: 'redirect' })`
2134  - `your.app:///` -> `makeRedirectUri({ scheme: 'your.app', isTripleSlashed: true })`
2135  - `your.app:/authorize` -> `makeRedirectUri({ native: 'your.app:/authorize' })`
2136  - `your.app://auth?foo=bar` -> `makeRedirectUri({ scheme: 'your.app', path: 'auth', queryParams: { foo: 'bar' } })`
2137  - `exp://u.expo.dev/[project-id]?channel-name=[channel-name]&runtime-version=[runtime-version]` -> `makeRedirectUri()`
2138  - 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`.
2139  - 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`).
2140- **Usage:** `promptAsync({ redirectUri })`
2141
2142## Improving User Experience
2143
2144The "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.
2145
2146Here are a few tips you can use to make authentication quick, easy, and secure for your users!
2147
2148### Warming the browser
2149
2150On 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.
2151
2152{/* prettier-ignore */}
2153```tsx
2154import * as React from 'react';
2155import * as WebBrowser from 'expo-web-browser';
2156
2157function App() {
2158  React.useEffect(() => {
2159    /* @info <strong>Android only:</strong> Start loading the default browser app in the background to improve transition time. */
2160    WebBrowser.warmUpAsync();
2161    /* @end */
2162
2163    return () => {
2164      /* @info <strong>Android only:</strong> Cool down the browser when the component unmounts to help improve memory on low-end Android devices. */
2165      WebBrowser.coolDownAsync();
2166      /* @end */
2167    };
2168  }, []);
2169
2170  // Do authentication ...
2171}
2172```
2173
2174### Implicit login
2175
2176You 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.
2177
2178Here is an example of logging into Spotify without using a client secret.
2179
2180{/* prettier-ignore */}
2181```tsx
2182import * as React from 'react';
2183import * as WebBrowser from 'expo-web-browser';
2184import { makeRedirectUri, useAuthRequest, ResponseType } from 'expo-auth-session';
2185
2186/* @info <strong>Web only:</strong> 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. */
2187WebBrowser.maybeCompleteAuthSession();
2188/* @end */
2189
2190// Endpoint
2191const discovery = {
2192  authorizationEndpoint: 'https://accounts.spotify.com/authorize',
2193};
2194
2195function App() {
2196  const [request, response, promptAsync] = useAuthRequest(
2197    {
2198      /* @info Request that the server returns an <code>access_token</code>, not all providers support this. */
2199      responseType: ResponseType.Token,
2200      /* @end */
2201      clientId: 'CLIENT_ID',
2202      scopes: ['user-read-email', 'playlist-modify-public'],
2203      redirectUri: makeRedirectUri({
2204        /* @info The URI <code>[scheme]://</code> to be used in bare and standalone. If undefined, the <code>scheme</code> property of your app.json or app.config.js will be used instead. */
2205        scheme: 'your.app'
2206        /* @end */
2207      }),
2208    },
2209    discovery
2210  );
2211
2212  React.useEffect(() => {
2213    if (response && response.type === 'success') {
2214      /* @info You can use this access token to make calls into the Spotify API. */
2215      const token = response.params.access_token;
2216      /* @end */
2217    }
2218  }, [response]);
2219
2220  return <Button disabled={!request} onPress={() => promptAsync()} title="Login" />;
2221}
2222```
2223
2224### Storing data
2225
2226On 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.
2227
2228You can store your authentication results and rehydrate them later to avoid having to prompt the user to login again.
2229
2230{/* prettier-ignore */}
2231```tsx
2232import * as SecureStore from 'expo-secure-store';
2233
2234const MY_SECURE_AUTH_STATE_KEY = 'MySecureAuthStateKey';
2235
2236function App() {
2237  const [, response] = useAuthRequest({});
2238
2239  React.useEffect(() => {
2240    if (response && response.type === 'success') {
2241      const auth = response.params;
2242      const storageValue = JSON.stringify(auth);
2243
2244      if (Platform.OS !== 'web') {
2245        // Securely store the auth on your device
2246        SecureStore.setItemAsync(MY_SECURE_AUTH_STATE_KEY, storageValue);
2247      }
2248    }
2249  }, [response]);
2250
2251  // More login code...
2252}
2253```
2254
2255[userinfo]: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
2256[provider-meta]: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
2257[oidc-dcr]: https://openid.net/specs/openid-connect-discovery-1_0.html#OpenID.Registration
2258[oidc-autherr]: https://openid.net/specs/openid-connect-core-1_0.html#AuthError
2259[oidc-authreq]: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationRequest
2260