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