首页指南参考教程

使用 OAuth 或 OpenID 提供商进行身份验证

了解如何利用 expo-auth-session 库通过 OAuth 或 OpenID 提供商实现身份验证。


Expo 可用于登录 Android、iOS 和 Web 上的许多流行提供商。这些指南中的大多数都使用纯 JS AuthSession API,请参阅这些文档以获取有关 API 的更多信息。

¥Expo can be used to login to many popular providers on Android, iOS, and web. Most of these guides utilize the pure JS AuthSession API, refer to those docs for more information on the API.

以下是适用于所有身份验证提供商的一些重要规则:

¥Here are some important rules that apply to all authentication providers:

  • 使用 WebBrowser.maybeCompleteAuthSession() 关闭 Web 弹出窗口。如果你忘记添加此项,则弹出窗口将不会关闭。

    ¥Use WebBrowser.maybeCompleteAuthSession() to dismiss the web popup. If you forget to add this then the popup window will not close.

  • 使用 AuthSession.makeRedirectUri() 创建重定向,这完成了与通用平台支持相关的大量繁重工作。在幕后,它使用 expo-linking

    ¥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.

  • 使用 AuthSession.useAuthRequest() 构建请求,该钩子允许异步设置,这意味着移动浏览器不会阻止身份验证。

    ¥Build requests using AuthSession.useAuthRequest(), the hook allows for async setup which means mobile browsers won't block the authentication.

  • 确保在定义 request 之前禁用提示。

    ¥Be sure to disable the prompt until request is defined.

  • 你只能在 Web 上的用户交互中调用 promptAsync

    ¥You can only invoke promptAsync in user interaction on the web.

  • 由于无法自定义你的应用方案,Expo Go 无法用于本地开发和测试支持 OAuth 或 OpenID Connect 的应用。你可以改为使用 开发构建,它可以实现类似于 Expo Go 的开发体验,并支持登录后 OAuth 重定向回你的应用,其工作方式与生产中的工作方式相同。

    ¥Expo Go cannot be used for local development and testing of OAuth or OpenID Connect-enabled apps due to the inability to customize your app scheme. You can instead use a Development Build, which enables an Expo Go-like development experience and supports OAuth redirects back to your app after login in a manner that works just like it would in production.

获取访问令牌

¥Obtaining access tokens

本指南中的大多数提供都使用 OAuth 2 标准进行安全身份验证和授权。在授权代码授予中,身份提供者返回一次性代码。然后将此代码交换为用户的访问令牌。

¥Most provides in this guide use the OAuth 2 standard for secure authentication and authorization. In the authorization code grant, the identity provider returns a one-time code. This code is then exchanged for the user's access token.

由于 你的应用代码不是存储密钥的安全场所,需要在上下文(例如,你的服务器)中交换授权代码。这将允许你安全地存储和使用客户端密钥来访问提供商的令牌端点。

¥Since your application code is not a secure place to store secrets, it is necessary to exchange the authorization code in a context (for example, your server). This will allow you to securely store and use a client secret to access the provider's token endpoint.

指南

¥Guides

AuthSession 可用于任何 OAuth 或 OpenID Connect 提供商,我们已经整理了使用最需要的服务的指南!如果你想查看更多内容,可以选择 开启 PR投票给精明的人

¥AuthSession can be used for any OAuth or OpenID Connect provider, we've assembled guides for using the most requested services! If you'd like to see more, you can open a PR or vote on canny.

IdentityServer 4

IdentityServer 4

OAuth 2 | OpenID

Asgardeo

Asgardeo

OAuth 2 | OpenID

Azure

Azure

OAuth 2 | OpenID

Apple

Apple

iOS Only

Beyond Identity

Beyond Identity

OAuth 2 | OpenID

Calendly

Calendly

OAuth 2

Cognito

Cognito

OAuth 2 | OpenID

Coinbase

Coinbase

OAuth 2

Descope

Descope

OAuth 2 | OpenID

Dropbox

Dropbox

OAuth 2

Facebook

Facebook

OAuth 2

Fitbit

Fitbit

OAuth 2

Firebase Phone

Firebase Phone

Recaptcha

GitHub

GitHub

OAuth 2

Google

Google

OAuth 2 | OpenID

Imgur

Imgur

OAuth 2

Keycloak

Keycloak

OAuth 2 | OpenID

Logto

Logto

OAuth 2 | OpenID

Okta

Okta

OAuth 2 | OpenID

Reddit

Reddit

OAuth 2

Slack

Slack

OAuth 2

Spotify

Spotify

OAuth 2

Strava

Strava

OAuth 2

Strivacity

Strivacity

OAuth 2

Twitch

Twitch

OAuth 2

Twitter

Twitter

OAuth 2

Uber

Uber

OAuth 2

IdentityServer 4

IdentityServer 4

网站提供者PKCE自动发现
更多信息OpenID必需的可用的
  • 如果不包含 offline_access,则不会返回刷新令牌。

    ¥If offline_access isn't included then no refresh token will be returned.

IdentityServer 4 Example
import * as React from 'react';
import { Button, Text, View } from 'react-native';
import * as AuthSession from 'expo-auth-session';
import * as WebBrowser from 'expo-web-browser';

WebBrowser.maybeCompleteAuthSession();
const redirectUri = AuthSession.makeRedirectUri();

export default function App() {
  const discovery = AuthSession.useAutoDiscovery('https://demo.identityserver.io');
  // Create and load an auth request
  const [request, result, promptAsync] = AuthSession.useAuthRequest(
    {
      clientId: 'native.code',
      redirectUri,
      scopes: ['openid', 'profile', 'email', 'offline_access'],
    },
    discovery
  );

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button title="Login!" disabled={!request} onPress={() => promptAsync()} />
      {result && <Text>{JSON.stringify(result, null, 2)}</Text>}
    </View>
  );
}
Asgardeo

Asgardeo

Create Asgardeo App
网站提供者PKCE自动发现
获取你的配置OpenID支持的可用的
  • 确保选中控制台中的 Public Client 选项。

    ¥Make sure to check Public Client option in the console.

  • 在允许的授权类型部分中选择预期的授权。

    ¥Choose the intended grant in the Allowed grant types section.

Asgardeo Auth Example
import { useState, useEffect } from 'react';
import { StyleSheet, Text, View, Button, Alert } from 'react-native';
import * as AuthSession from "expo-auth-session";
import * as WebBrowser from "expo-web-browser";
import jwtDecode from "jwt-decode";

WebBrowser.maybeCompleteAuthSession();

const redirectUri = AuthSession.makeRedirectUri();

const CLIENT_ID = "YOUR_CLIENT_ID";

export default function App() {

    const discovery = AuthSession.useAutoDiscovery('https://api.asgardeo.io/t/<YOUR_ORG_NAME>/oauth2/token');
    const [tokenResponse, setTokenResponse] = useState({});
    const [decodedIdToken, setDecodedIdToken] = useState({});

    const [request, result, promptAsync] = AuthSession.useAuthRequest(
        {
            redirectUri,
            clientId: CLIENT_ID,
            responseType: "code",
            scopes: ["openid", "profile", "email"]
        },
        discovery
    );

    const getAccessToken = () => {
      if (result?.params?.code) {
        fetch(
        "https://api.asgardeo.io/t/iamapptesting/oauth2/token",
          {
            method: "POST",
            headers: {
              "Content-Type": "application/x-www-form-urlencoded"
            },
            body: `grant_type=authorization_code&code=${result?.params?.code}&redirect_uri=${redirectUri}&client_id=${CLIENT_ID}&code_verifier=${request?.codeVerifier}`
          }).then((response) => {
              return response.json();
            }).then((data) => {
              setTokenResponse(data);
              setDecodedIdToken(jwtDecode(data.id_token));
            }).catch((err) => {
              console.log(err);
            });
        }
    }

    useEffect(() => {
      (async function setResult() {
        if (result) {
          if (result.error) {
            Alert.alert(
              "Authentication error",
              result.params.error_description || "something went wrong"
            );
            return;
          }
          if (result.type === "success") {
            getAccessToken();
          }
        }
      })();
    }, [result]);


    return (
      <View style={styles.container}>
        <Button title="Login" disabled={!request} onPress={() => promptAsync()} />
        {decodedIdToken && <Text>Welcome {decodedIdToken.given_name || ""}!</Text>}
        {decodedIdToken && <Text>{decodedIdToken.email}</Text>}
        <View style={styles.accessTokenBlock}>
          decodedToken && <Text>Access Token: {tokenResponse.access_token}</Text>
        </View>
      </View>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
    },
    accessTokenBlock: {
        width: 300,
        height: 500,
        overflow: "scroll"
    }
});
Azure

Azure

Create Azure App
网站提供者PKCE自动发现
获取你的配置OpenID支持的可用的
Azure Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import {
  exchangeCodeAsync,
  makeRedirectUri,
  useAuthRequest,
  useAutoDiscovery,
} from 'expo-auth-session';
import { Button, Text, SafeAreaView } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

export default function App() {
  // Endpoint
  const discovery = useAutoDiscovery(
    'https://login.microsoftonline.com/<TENANT_ID>/v2.0',
  );
  const redirectUri = makeRedirectUri({
    scheme: undefined,
    path: 'auth',
  });
  const clientId = '<CLIENT_ID>';

  // We store the JWT in here
  const [token, setToken] = React.useState<string | null>(null);

  // Request
  const [request, , promptAsync] = useAuthRequest(
    {
      clientId,
      scopes: ['openid', 'profile', 'email', 'offline_access'],
      redirectUri,
    },
    discovery,
  );

  return (
    <SafeAreaView>
      <Button
        disabled=

{!request}


        title="Login"
        onPress={() => {
          promptAsync().then((codeResponse) => {
            if (request && codeResponse?.type === 'success' && discovery) {
              exchangeCodeAsync(
                {
                  clientId,
                  code: codeResponse.params.code,
                  extraParams: request.codeVerifier
                    ? { code_verifier: request.codeVerifier }
                    : undefined,
                  redirectUri,
                },
                discovery,
              ).then((res) => {
                setToken(res.accessToken);
              });
            }
          });
        }}
      />
      <Text>{token}</Text>
    </SafeAreaView>
  );
}
Beyond Identity

Beyond Identity

Create Beyond Identity App
网站提供者PKCE自动发现
获取你的配置OpenID支持的可用的
  • Beyond Identity 允许开发者基于称为通用密钥的公私密钥对实现强大的无密码身份验证。所有密钥均以加密方式链接到用户,并且可以使用 Beyond Identity API 进行集中管理。

    ¥Beyond Identity allows developers to implement strong passwordless authentication based on public-private key pairs called Universal Passkeys. All keys are cryptographically linked to the user and can be centrally managed using the Beyond Identity APIs.

  • 在进行身份验证之前,你将需要通用密钥。参见 超越身份证明文件

    ¥You will need a Universal Passkey before you can authenticate. See Beyond Identity documentation.

  • 确保 创建开发版本 并遵循 安装所需的配置插件 的说明。

    ¥Make sure to create a development build and follow instructions to install required config plugins.

  • 有关完整的示例应用,请参阅 SDK 的 GitHub 存储库

    ¥For a complete example app, see SDK's GitHub repository.

  • 将 Beyond Identity 验证器配置 调用类型设置为自动。

    ¥Set your Beyond Identity Authenticator Config's Invocation Type to Automatic.

  • 如果选择“自动”,Beyond Identity 将使用调用 URL(指向你的应用的 App Scheme 或通用 URL)自动重定向到你的应用。

    ¥If Automatic is selected, Beyond Identity will automatically redirect to your application using the Invoke URL (the App Scheme or Universal URL pointing to your application).

Auth Code
import { useEffect } from 'react';
import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
import { Button } from 'react-native';
import { Embedded } from '@beyondidentity/bi-sdk-react-native';

export default function App() {
  // Endpoint
  const discovery = useAutoDiscovery(
    `https://auth-${region}.beyondidentity.com/v1/tenants/${tenant_id}/realms/${realm_id}/applications/${application_id}`
  );
  // Request
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: `${client_id}`,
      scopes: ['openid'],
      redirectUri: makeRedirectUri({
        scheme: 'your.app',
      }),
    },
    discovery
  );

  useEffect(() => {
    const authenticate = async url => {
      // Display UI for the user to select a passwordless passkey
      const passkeys = await Embedded.getPasskeys();

      if (await Embedded.isAuthenticateUrl(url)) {
        // Pass url and a selected passkey ID into the Beyond Identity Embedded SDK authenticate function
        const { redirectUrl } = await Embedded.authenticate(url, passkeys[0].id);
      }
    };

    if (response?.url) {
      authenticate(url);
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Passwordless Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
  • 将 Beyond Identity 验证器配置 调用类型设置为手动。

    ¥Set your Beyond Identity Authenticator Config's Invocation Type to Manual.

  • 如果选择“手动”,身份验证 URL 将作为 JSON 响应的一部分返回。不需要重定向,也不需要 Web 服务身份验证。结果是使用密钥进行完全静默的 OAuth 2.0 身份验证。

    ¥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.

Auth Code
import React from 'react';
import { Button } from 'react-native';
import { Embedded } from '@beyondidentity/bi-sdk-react-native';

export default function App() {
  async function authenticate() {
    const BeyondIdentityAuthUrl = `https://auth-${region}.beyondidentity.com/v1/tenants/${tenant_od}/realms/${realm_id}/applications/${application_id}/authorize?response_type=code&client_id=${client_id}&redirect_uri=${uri_encoded_redirect_uri}&scope=openid&state=${state}&code_challenge_method=S256&code_challenge=${pkce_code_challenge}`;

    let response = await fetch(BeyondIdentityAuthUrl, {
      method: 'GET',
      headers: new Headers({
        'Content-Type': 'application/json',
      }),
    });
    const data = await response.json();

    // Display UI for the user to select a passwordless passkey
    const passkeys = await Embedded.getPasskeys();

    if (await Embedded.isAuthenticateUrl(data.authenticate_url)) {
      // Pass url and selected Passkey ID into the Beyond Identity Embedded SDK authenticate function
      const { redirectUrl } = await Embedded.authenticate(data.authenticate_url, passkeys[0].id);
    }
  }

  return (
    <Button
      title="Passwordless Login"
      onPress={authenticate}
    />
  );
}
Calendly

Calendly

Create Calendly App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0支持的无法使用
  • redirectUri 需要两个斜线(://)。

    ¥redirectUri requires two slashes (://).

  • 示例重定向 Uri:

    ¥Example redirectUri:

    • 独立/开发构建:myapp://*

      ¥Standalone / development build: myapp://*

    • 网址:https://yourwebsite.com/*

      ¥Web: https://yourwebsite.com/*

Calendly Auth Example
import * as WebBrowser from 'expo-web-browser';
import {
  makeRedirectUri,
  useAuthRequest,
  exchangeCodeAsync,
} from "expo-auth-session";
import React, { useEffect, useState } from "react";

WebBrowser.maybeCompleteAuthSession();

const discovery = {
  authorizationEndpoint: "https://auth.calendly.com/oauth/authorize",
  tokenEndpoint: "https://auth.calendly.com/oauth/token",
};

export default function App() {
  const [authTokens, setAuthTokens] = useState({access_token: "", refresh_token: ""});
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: process.env.EXPO_PUBLIC_Client_ID,
      usePKCE: true,
      redirectUri: makeRedirectUri({
        native: "myapp://",
      }),
    },
    discovery
  );

  useEffect(() => {
    const exchange = async (exchangeTokenReq) => {
      try {
        const exchangeTokenResponse = await exchangeCodeAsync(
          {
            clientId: process.env.EXPO_PUBLIC_Client_ID,
            code: exchangeTokenReq,
            redirectUri: makeRedirectUri({
              native: "myapp://",
            }),
            extraParams: {
              code_verifier: request.codeVerifier,
            },
          },
          discovery
        );
        setAuthTokens(exchangeTokenResponse);
      } catch (error) {
        console.error("error", error);
      }
    };

    if (response) {
      if (response.error) {
        console.error(
          "Authentication error",
          response.params.error_description || "something went wrong"
        );
      }
      if (response.type === "success") {
        exchange( response.params.code);
      }
    }
  }, [discovery, request, response]);

  return (
  <SafeAreaView>
      <View>
        <Text>0Auth2</Text>
        <Button
          title="Connect to Calendly"
          onPress={() => {
            promptAsync();
          }}
        />
        <Text>AuthTokens: {JSON.stringify(authTokens)}</Text>
      </View>
  </SafeAreaView>
  )
}

Cognito

Cognito

Create Cognito App
网站提供者PKCE自动发现
获取你的配置OpenID支持的无法使用
  • 利用 Cognito 中的托管 UI (API 文档)

    ¥Leverages the Hosted UI in Cognito (API documentation)

  • 成功验证后请求代码,然后用代码交换身份验证令牌 (PKCE)

    ¥Requests code after successfully authenticating, followed by exchanging code for the auth tokens (PKCE)

  • /token 端点需要一个 code_verifier 参数,你可以在调用 exchangeCodeAsync() 之前从请求中检索该参数:

    ¥The /token endpoint requires a code_verifier parameter which you can retrieve from the request before calling exchangeCodeAsync():

extraParams: {
  code_verifier: request.codeVerifier,
}
Cognito Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { useAuthRequest, exchangeCodeAsync, revokeAsync, ResponseType } from 'expo-auth-session';
import { Button, Alert } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

const clientId = '<your-client-id-here>';
const userPoolUrl =
  'https://<your-user-pool-domain>.auth.<your-region>.amazoncognito.com';
const redirectUri = 'your-redirect-uri';

export default function App() {
  const [authTokens, setAuthTokens] = React.useState(null);
  const discoveryDocument = React.useMemo(() => ({
    authorizationEndpoint: userPoolUrl + '/oauth2/authorize',
    tokenEndpoint: userPoolUrl + '/oauth2/token',
    revocationEndpoint: userPoolUrl + '/oauth2/revoke',
  }), []);

  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId,
      responseType: ResponseType.Code,
      redirectUri,
      usePKCE: true,
    },
    discoveryDocument
  );

  React.useEffect(() => {
    const exchangeFn = async (exchangeTokenReq) => {
      try {
        const exchangeTokenResponse = await exchangeCodeAsync(
          exchangeTokenReq,
          discoveryDocument
        );
        setAuthTokens(exchangeTokenResponse);
      } catch (error) {
        console.error(error);
      }
    };
    if (response) {
      if (response.error) {
        Alert.alert(
          'Authentication error',
          response.params.error_description || 'something went wrong'
        );
        return;
      }
      if (response.type === 'success') {
        exchangeFn({
          clientId,
          code: response.params.code,
          redirectUri,
          extraParams: {
            code_verifier: request.codeVerifier,
          },
        });
      }
    }
  }, [discoveryDocument, request, response]);

  const logout = async () => {
    const revokeResponse = await revokeAsync(
      {
        clientId: clientId,
        token: authTokens.refreshToken,
      },
      discoveryDocument
    );
    if (revokeResponse) {
      setAuthTokens(null);
    }
  };
  console.log('authTokens: ' + JSON.stringify(authTokens));
  return authTokens ? (
    <Button title="Logout" onPress={() => logout()} />
  ) : (
    <Button disabled={!request} title="Login" onPress={() => promptAsync()} />
  );
}
Coinbase

Coinbase

Create Coinbase App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0支持的无法使用
  • redirectUri 需要两个斜杠(://)。

    ¥The redirectUri requires two slashes (://).

  • 范围必须与 : 连接,因此只需创建一个长字符串。

    ¥Scopes must be joined with : so just create one long string.

  • 设置重定向 URI:你的项目 > 允许的重定向 URI:(修改后一定要保存)。

    ¥Setup redirect URIs: Your Project > Permitted Redirect URIs: (be sure to save after making changes).

    网络开发:https://localhost:19006

    ¥Web dev: https://localhost:19006

    • 运行 expo start --web --https 以使用 https 运行,否则 auth 将无法工作。

      ¥Run expo start --web --https to run with https, auth won't work otherwise.

    • 在 URL 末尾添加斜杠并不重要。

      ¥Adding a slash to the end of the URL doesn't matter.

    独立/开发构建:your-scheme://

    ¥Standalone / development build: your-scheme://

    • 方案应在 app.json expo.scheme: 'your-scheme' 中指定,然后使用 makeRedirectUri({ native: 'your-scheme://' }) 添加到应用代码中)

      ¥Scheme should be specified in app.json expo.scheme: 'your-scheme', then added to the app code with makeRedirectUri({ native: 'your-scheme://' }))

    网页制作:https://yourwebsite.com

    ¥Web production: https://yourwebsite.com

    • 将其设置为你部署的网站 URL 的值。

      ¥Set this to whatever your deployed website URL is.

Coinbase Auth Example
import {
  exchangeCodeAsync,
  makeRedirectUri,
  TokenResponse,
  useAuthRequest,
} from "expo-auth-session";
import * as WebBrowser from "expo-web-browser";
import * as React from "react";
import { Button } from "react-native";

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: "https://www.coinbase.com/oauth/authorize",
  tokenEndpoint: "https://api.coinbase.com/oauth/token",
  revocationEndpoint: "https://api.coinbase.com/oauth/revoke",
};

const redirectUri = makeRedirectUri({ scheme: 'your.app'});
const CLIENT_ID = "CLIENT_ID";

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: CLIENT_ID,
      scopes: ["wallet:accounts:read"],
      redirectUri,
    },
    discovery
  );
  const {
    // The token will be auto exchanged after auth completes.
    token,
    exchangeError,
  } = useAutoExchange(
    response?.type === "success" ? response.params.code : null
  );

  React.useEffect(() => {
    if (token) {
      console.log("My Token:", token.accessToken);
    }
  }, [token]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}

type State = {
  token: TokenResponse | null;
  exchangeError: Error | null;
};

// A hook to automatically exchange the auth token for an access token.
// this should be performed in a server and not here in the application.
// For educational purposes only:
function useAutoExchange(code?: string): State {
  const [state, setState] = React.useReducer(
    (state: State, action: Partial<State>) => ({ ...state, ...action }),
    { token: null, exchangeError: null }
  );
  const isMounted = useMounted();

  React.useEffect(() => {
    if (!code) {
      setState({ token: null, exchangeError: null });
      return;
    }

    exchangeCodeAsync(
      {
        clientId: CLIENT_ID,
        clientSecret: "CLIENT_SECRET",
        code,
        redirectUri,
      },
      discovery
    )
      .then((token) => {
        if (isMounted.current) {
          setState({ token, exchangeError: null });
        }
      })
      .catch((exchangeError) => {
        if (isMounted.current) {
          setState({ exchangeError, token: null });
        }
      });
  }, [code]);

  return state;
}

function useMounted() {
  const isMounted = React.useRef(true);
  React.useEffect(() => {
    return () => {
      isMounted.current = false;
    };
  }, []);
  return isMounted;
}
Descope

Descope

Create Descope App
网站提供者PKCE自动发现
获取你的配置OpenID支持的可用的
  • 利用托管 Descope Flow 应用 身份验证托管应用

    ¥Leverages the Hosted Descope Flow app Auth-Hosting App.

  • 成功验证后请求代码,然后用代码交换身份验证令牌 (PKCE)。

    ¥Requests code after successfully authenticating, followed by exchanging code for the auth tokens (PKCE).

Descope Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import * as AuthSession from 'expo-auth-session';
import { Button, View } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

const descopeProjectId = '<Descope Project ID>'; // Replace this with your Descope Project ID
const descopeUrl = `https://api.descope.com/${descopeProjectId}`;
const redirectUri = AuthSession.makeRedirectUri();

export default function App() {
  const [authTokens, setAuthTokens] = React.useState(null);
  const discovery = AuthSession.useAutoDiscovery(descopeUrl);

  const [request, response, promptAsync] = AuthSession.useAuthRequest(
    {
      clientId: descopeProjectId,
      responseType: AuthSession.ResponseType.Code,
      redirectUri,
      usePKCE: true,
      scopes: ['openid', 'profile', 'email'],
    },
    discovery
  );

  React.useEffect(() => {
    if (response) {
      if (response.error) {
        console.error(
          'Authentication error',
          response.params.error_description || 'something went wrong'
        );
        return;
      }
      if (response.type === 'success') {
        const exchangeFn = async (exchangeTokenReq) => {
          try {
            const exchangeTokenResponse = await AuthSession.exchangeCodeAsync(
              exchangeTokenReq,
              discovery
            );
            setAuthTokens(exchangeTokenResponse);
          } catch (error) {
            console.error(error);
          }
        };

        exchangeFn({
          clientId: descopeProjectId,
          code: response.params.code,
          redirectUri,
          extraParams: {
            code_verifier: request.codeVerifier,
          },
        });
      }
    }
  }, [discovery, request, response]);

  const logout = async () => {
    const revokeResponse = await AuthSession.revokeAsync(
      {
        clientId: descopeProjectId,
        token: authTokens.refreshToken,
      },
      discovery
    );
    if (revokeResponse) {
      setAuthTokens(null);
    }
  };

  return (
    <View>
      {authTokens ? (
        <Button title="Logout" onPress={logout} />
      ) : (
        <Button
          disabled={!request}
          title="Login"
          onPress={promptAsync}
        />
      )}
    </View>
  );
}
Dropbox

Dropbox

Create Dropbox App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0不支持无法使用
  • 范围必须是空数组。

    ¥Scopes must be an empty array.

Dropbox Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button, Platform } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://www.dropbox.com/oauth2/authorize',
  tokenEndpoint: 'https://www.dropbox.com/oauth2/token',
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      // There are no scopes so just pass an empty array
      scopes: [],
      redirectUri: makeRedirectUri({
        scheme: 'your.app',
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
Fitbit

Fitbit

Create Fitbit App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0支持的无法使用
  • 提供商仅允许每个应用使用一个重定向 URI。对于要使用的每种方法,你都需要一个单独的应用:

    ¥Provider only allows one redirect URI per app. You'll need an individual app for every method you want to use:

    • 独立/开发构建:com.your.app://*

      ¥Standalone / development build: com.your.app://*

    • 网址:https://yourwebsite.com/*

      ¥Web: https://yourwebsite.com/*

  • redirectUri 需要两个斜杠(://)。

    ¥The redirectUri requires two slashes (://).

Fitbit Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button, Platform } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://www.fitbit.com/oauth2/authorize',
  tokenEndpoint: 'https://api.fitbit.com/oauth2/token',
  revocationEndpoint: 'https://api.fitbit.com/oauth2/revoke',
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      scopes: ['activity', 'sleep'],
      redirectUri: makeRedirectUri({
        scheme: 'your.app'
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
GitHub

GitHub

Create GitHub App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0支持的无法使用
  • 提供商仅允许每个应用使用一个重定向 URI。对于要使用的每种方法,你都需要一个单独的应用:

    ¥Provider only allows one redirect URI per app. You'll need an individual app for every method you want to use:

    • 独立/开发构建:com.your.app://*

      ¥Standalone / development build: com.your.app://*

    • 网址:https://yourwebsite.com/*

      ¥Web: https://yourwebsite.com/*

  • redirectUri 需要两个斜杠(://)。

    ¥The redirectUri requires two slashes (://).

  • revocationEndpoint 是动态的,需要你的 config.clientId

    ¥revocationEndpoint is dynamic and requires your config.clientId.

GitHub Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://github.com/login/oauth/authorize',
  tokenEndpoint: 'https://github.com/login/oauth/access_token',
  revocationEndpoint: 'https://github.com/settings/connections/applications/<CLIENT_ID>',
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      scopes: ['identity'],
      redirectUri: makeRedirectUri({
        scheme: 'your.app'
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
Imgur

Imgur

Create Imgur App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0支持的无法使用
  • 你将需要为每个平台创建不同的提供商应用(动态选择你的 clientId)。

    ¥You will need to create a different provider app for each platform (dynamically choosing your clientId).

  • 在这里了解更多:imgur.com/oauth2

    ¥Learn more here: imgur.com/oauth2

Imgur Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button, Platform } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

const discovery = {
  authorizationEndpoint: 'https://api.imgur.com/oauth2/authorize',
  tokenEndpoint: 'https://api.imgur.com/oauth2/token',
};

export default function App() {
  // Request
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      clientSecret: 'CLIENT_SECRET',
      redirectUri: makeRedirectUri({
        scheme: 'your.app',
      }),
      // imgur requires an empty array
      scopes: [],
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
Keycloak

Keycloak

网站提供者PKCE自动发现
*OpenID必需的可用的
Keycloak Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
import { Button, Text, View } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

export default function App() {
  const discovery = useAutoDiscovery('https://YOUR_KEYCLOAK/realms/YOUR_REALM');

// Create and load an auth request
  const [request, result, promptAsync] = useAuthRequest(
    {
      clientId: 'YOUR_CLIENT_NAME',
      redirectUri: makeRedirectUri({
        scheme: 'YOUR_SCHEME'
      }),
      scopes: ['openid', 'profile'],
    },
    discovery
  );

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Button title="Login!" disabled={!request} onPress={() => promptAsync()} />
      {result && <Text>{JSON.stringify(result, null, 2)}</Text>}
    </View>
  );
}
Logto

Logto

Create Logto App
网站提供者PKCE自动发现
获取你的配置OpenID支持的可用的
  • 对于原生平台,redirectUri 需要私有使用的原生 URI 方案。详细信息请参见 OAuth2 规范

    ¥For native platforms, a Private-Use native URI scheme is required for redirectUri. See OAuth2 spec for more details.

  • 对于 Web 平台,redirectUri 需要 http(s):// 方案。

    ¥For web platform, a http(s):// scheme is required for redirectUri.

Logto Auth Example
import { LogtoProvider, useLogto } from "@logto/rn";

// Use `useLogto()` hook to sign in and sign out
const Content = () => {
  const { signIn, signOut, isAuthenticated } = useLogto();

  return isAuthenticated ? (
    <Button title="Sign Out" onPress={signOut} />
  ) : (
    <Button title="Sign In" onPress={async () => signIn(redirectUri)} />
  );
};

// Wrap your page component with `LogtoProvider`
const App = () => {
  const logtoConfig = {
    appId: "YOUR_APP",
    endpoint: "YOUR_LOGTO_ENDPOINT",
  };

  return (
    <LogtoProvider config={logtoConfig}>
      <Content />
    </LogtoProvider>
  );
};
Okta

Okta

Create Okta App
网站提供者PKCE自动发现
报名 > 应用OpenID支持的可用的
  • 你无法定义自定义 redirectUri,Okta 将为你提供一个。

    ¥You cannot define a custom redirectUri, Okta will provide you with one.

Okta Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest, useAutoDiscovery } from 'expo-auth-session';
import { Button, Platform } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

export default function App() {
  // Endpoint
  const discovery = useAutoDiscovery('https://<OKTA_DOMAIN>.com/oauth2/default');
  // Request
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      scopes: ['openid', 'profile'],
      redirectUri: makeRedirectUri({
        native: 'com.okta.<OKTA_DOMAIN>:/callback',
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
Reddit

Reddit

Create Reddit App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0支持的无法使用
  • 提供商仅允许每个应用使用一个重定向 URI。对于要使用的每种方法,你都需要一个单独的应用:

    ¥Provider only allows one redirect URI per app. You'll need an individual app for every method you want to use:

    • 独立/开发构建:com.your.app://*

      ¥Standalone / development build: com.your.app://*

    • 网址:https://yourwebsite.com/*

      ¥Web: https://yourwebsite.com/*

  • redirectUri 需要两个斜杠(://)。

    ¥The redirectUri requires two slashes (://).

Reddit Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://www.reddit.com/api/v1/authorize.compact',
  tokenEndpoint: 'https://www.reddit.com/api/v1/access_token',
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      scopes: ['identity'],
      redirectUri: makeRedirectUri({
        native: 'your.app://redirect',
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
Slack

Slack

Create Slack App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0支持的无法使用
  • redirectUri 需要两个斜杠(://)。

    ¥The redirectUri requires two slashes (://).

  • redirectUri 可以在网站的 "OAuth 和权限" 部分下定义。

    ¥redirectUri can be defined under the "OAuth & Permissions" section of the website.

  • clientIdclientSecret 可以在 "应用凭证" 部分找到。

    ¥clientId and clientSecret can be found in the "App Credentials" section.

  • 范围必须与 ':' 连接,因此只需创建一个长字符串。

    ¥Scopes must be joined with ':' so just create one long string.

  • 导航至 "范围" 部分以启用范围。

    ¥Navigate to the "Scopes" section to enable scopes.

  • revocationEndpoint 不可用。

    ¥revocationEndpoint is not available.

Slack Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://slack.com/oauth/authorize',
  tokenEndpoint: 'https://slack.com/api/oauth.access',
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      scopes: ['emoji:read'],
      redirectUri: makeRedirectUri({
        scheme: 'your.app'
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
Spotify

Spotify

Create Spotify App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0支持的无法使用
  • 设置你的重定向 URI:你的项目 > 编辑设置 > 重定向 URI,请确保在进行更改后保存。

    ¥Setup your redirect URIs: Your project > Edit Settings > Redirect URIs, be sure to save after making changes.

    网络开发:https://localhost:19006

    ¥Web dev: https://localhost:19006

    • 重要的:确保 URL 末尾没有斜杠,除非在应用代码中使用 makeRedirectUri({ path: '/' }) 手动更改。

      ¥Important: Ensure there's no slash at the end of the URL unless manually changed in the app code with makeRedirectUri({ path: '/' }).

    • 运行 expo start --web --https 以使用 https 运行,否则 auth 将无法工作。

      ¥Run expo start --web --https to run with https, auth won't work otherwise.

    独立/开发构建:your-scheme://

    ¥Standalone / development build: your-scheme://

    • 方案应在 app.json expo.scheme: 'your-scheme' 中指定,然后使用 makeRedirectUri({ native: 'your-scheme://' }) 添加到应用代码中)

      ¥Scheme should be specified in app.json expo.scheme: 'your-scheme', then added to the app code with makeRedirectUri({ native: 'your-scheme://' }))

    网页制作:https://yourwebsite.com

    ¥Web production: https://yourwebsite.com

    • 将其设置为你部署的网站 URL 的值。

      ¥Set this to whatever your deployed website URL is.

  • 了解有关 Spotify API 的更多信息。

    ¥Learn more about the Spotify API.

Spotify Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://accounts.spotify.com/authorize',
  tokenEndpoint: 'https://accounts.spotify.com/api/token',
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      scopes: ['user-read-email', 'playlist-modify-public'],
      // To follow the "Authorization Code Flow" to fetch token after authorizationEndpoint
      // this must be set to false
      usePKCE: false,
      redirectUri: makeRedirectUri({
        scheme: 'your.app'
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
Strava

Strava

Create Strava App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0支持的无法使用
  • 了解有关 Strava API 的更多信息。

    ¥Learn more about the Strava API.

  • "授权回调域" 指的是重定向 URI 的最终路径组成部分。前任:在 URI com.bacon.myapp://redirect 中,域将为 redirect

    ¥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.

  • Strava 不提供隐式身份验证流程。

    ¥No Implicit auth flow is provided by Strava.

Strava Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://www.strava.com/oauth/mobile/authorize',
  tokenEndpoint: 'https://www.strava.com/oauth/token',
  revocationEndpoint: 'https://www.strava.com/oauth/deauthorize',
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      scopes: ['activity:read_all'],
      redirectUri: makeRedirectUri({
        // the "redirect" must match your "Authorization Callback Domain" in the Strava dev console.
        native: 'your.app://redirect',
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}

Strava 不提供隐式身份验证流程,你应该将代码发送到服务器或无服务器函数来执行访问令牌交换。出于调试目的,你可以使用以下方法在客户端执行交换:

¥Strava doesn't provide an implicit auth flow, you should send the code to a server or serverless function to perform the access token exchange. For debugging purposes, you can perform the exchange client-side using the following method:

const { accessToken } = await AuthSession.exchangeCodeAsync(
  {
    clientId: request?.clientId,
    redirectUri,
    code: result.params.code,
    extraParams: {
      // You must use the extraParams variation of clientSecret.
      // Never store your client secret on the client.
      client_secret: 'CLIENT_SECRET',
    },
  },
  { tokenEndpoint: 'https://www.strava.com/oauth/token' }
);
Strivacity

Strivacity

Create Strivacity App
网站提供者PKCE自动发现
获取你的配置OpenID支持的可用的
Strivacity Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://<instance>.strivacity.com/oauth2/auth',
  tokenEndpoint: 'https://<instance>.strivacity.com/oauth2/token',
  revocationEndpoint: 'https://<instance>.strivacity.com/oauth2/revoke',
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      scopes: ['openid', 'profile'],
      redirectUri: makeRedirectUri();
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
Twitch

Twitch

Create Twitch App
网站提供者PKCE自动发现范围
获取你的配置OAuth支持的无法使用信息
  • 你需要在 Twitch 账户上启用 2FA 才能创建应用。

    ¥You will need to enable 2FA on your Twitch account to create an application.

Twitch Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://id.twitch.tv/oauth2/authorize',
  tokenEndpoint: 'https://id.twitch.tv/oauth2/token',
  revocationEndpoint: 'https://id.twitch.tv/oauth2/revoke',
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      redirectUri: makeRedirectUri({
        scheme: 'your.app'
      }),
      scopes: ['user:read:email', 'analytics:read:games'],
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
Twitter

Twitter

Create Twitter App
网站提供者PKCE自动发现范围
获取你的配置OAuth支持的无法使用信息
  • 你需要获得 Twitter 支持人员的批准才能使用 Twitter v2 API。

    ¥You will need to be approved by Twitter support before you can use the Twitter v2 API.

  • Web 似乎无法正常工作,Twitter 认证网站似乎阻止了弹出窗口,导致 useAuthRequestresponse 始终为 {type: 'dismiss'}

    ¥Web does not appear to work, the Twitter authentication website appears to block the popup, causing the response of useAuthRequest to always be {type: 'dismiss'}.

  • 重定向示例:

    ¥Example redirects:

    • 独立/开发构建:com.your.app://

      ¥Standalone / development build: com.your.app://

    • 网页(开发 expo start --https):https://localhost:19006(无结尾斜杠)

      ¥Web (dev expo start --https): https://localhost:19006 (no ending slash)

  • redirectUri 需要两个斜杠(://)。

    ¥The redirectUri requires two slashes (://).

Twitter Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button, Platform } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: "https://twitter.com/i/oauth2/authorize",
  tokenEndpoint: "https://twitter.com/i/oauth2/token",
  revocationEndpoint: "https://twitter.com/i/oauth2/revoke",
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      redirectUri: makeRedirectUri({
        scheme: 'your.app',
      }),
      usePKCE: true,
      scopes: [
        "tweet.read",
      ],
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}
Uber

Uber

Create Uber App
网站提供者PKCE自动发现
获取你的配置OAuth 2.0支持的无法使用
  • redirectUri 需要两个斜杠(://)。

    ¥The redirectUri requires two slashes (://).

  • scopes 可能很难获得批准。

    ¥scopes can be difficult to get approved.

Uber Auth Example
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://login.uber.com/oauth/v2/authorize',
  tokenEndpoint: 'https://login.uber.com/oauth/v2/token',
  revocationEndpoint: 'https://login.uber.com/oauth/v2/revoke',
};

export default function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      clientId: 'CLIENT_ID',
      scopes: ['profile', 'delivery'],
      redirectUri: makeRedirectUri({
        scheme: 'your.app'
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response?.type === 'success') {
      const { code } = response.params;
    }
  }, [response]);

  return (
    <Button
      disabled={!request}
      title="Login"
      onPress={() => {
        promptAsync();
      }}
    />
  );
}

重定向 URI 模式

¥Redirect URI patterns

以下是你可能最终使用的一些常见重定向 URI 模式的示例。

¥Here are a few examples of some common redirect URI patterns you may end up using.

独立/开发构建

¥Standalone / development build

yourscheme://path

在某些情况下,会有 1 到 3 个斜杠 (/)。

¥In some cases there will be anywhere between 1 to 3 slashes (/).

  • 环境:

    ¥Environment:

    • 裸工作流程

      ¥Bare workflow

      • npx expo prebuild
    • 在 App 或 Play Store 中独立构建或在本地测试

      ¥Standalone builds in the App or Play Store or testing locally

      • 安卓:eas buildnpx expo run:android

        ¥Android: eas build or npx expo run:android

      • iOS:eas buildnpx expo run:ios

        ¥iOS: eas build or npx expo run:ios

  • 创造:在正确的环境中运行时使用 AuthSession.makeRedirectUri({ native: '<YOUR_URI>' }) 选择原生。

    ¥Create: Use AuthSession.makeRedirectUri({ native: '<YOUR_URI>' }) to select native when running in the correct environment.

    • your.app://redirect -> makeRedirectUri({ scheme: 'your.app', path: 'redirect' })

    • your.app:/// -> makeRedirectUri({ scheme: 'your.app', isTripleSlashed: true })

    • your.app:/authorize -> makeRedirectUri({ native: 'your.app:/authorize' })

    • your.app://auth?foo=bar -> makeRedirectUri({ scheme: 'your.app', path: 'auth', queryParams: { foo: 'bar' } })

    • exp://u.expo.dev/[project-id]?channel-name=[channel-name]&runtime-version=[runtime-version] -> makeRedirectUri()

    • 此链接通常可以自动创建,但我们建议你至少定义 scheme 属性。可以通过传递 native 属性在应用中覆盖整个 URL。通常,这将用于 Google 或 Okta 等要求你使用自定义原生 URI 重定向的提供商。你可以使用 npx uri-scheme 添加、列出和打开 URI 方案。

      ¥This link can often be created automatically but we recommend you define the scheme property at least. The entire URL can be overridden in 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.

    • 如果在弹出后更改 expo.scheme,则需要使用 expo apply 命令将更改应用到原生项目,然后重建它们(yarn iosyarn android)。

      ¥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).

  • 用法:promptAsync({ redirectUri })

    ¥Usage: promptAsync({ redirectUri })

改善用户体验

¥Improving user experience

"登录流程" 是一件需要做好的重要事情,在很多情况下,这是用户 promise 再次使用你的应用的地方。糟糕的体验可能会导致用户在真正开始使用你的应用之前就放弃它。

¥The "login flow" is an important thing to get right, in a lot of cases this is where the user will commit to using your app again. A bad experience can cause users to give up on your app before they've really gotten to use it.

你可以使用以下一些技巧来让你的用户快速、轻松且安全地进行身份验证!

¥Here are a few tips you can use to make authentication quick, easy, and secure for your users!

预热浏览器

¥Warming the browser

在 Android 上,你可以选择在使用网络浏览器之前对其进行预热。这允许浏览器应用在后台预先初始化自身。这样做可以显着加快提示用户进行身份验证的速度。

¥On Android you can optionally warm up the web browser before it's used. This allows the browser app to pre-initialize itself in the background. Doing this can significantly speed up prompting the user for authentication.

import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';

function App() {
  React.useEffect(() => {
    WebBrowser.warmUpAsync();

    return () => {
      WebBrowser.coolDownAsync();
    };
  }, []);

  // Do authentication ...
}

隐式登录

¥Implicit login

由于没有安全的方法来将客户端密钥存储在应用包中,因此历史上许多提供商都提供了 "隐式流",它使你能够在没有客户端密钥的情况下请求访问令牌。由于固有的安全风险(包括访问令牌注入的风险),不再建议这样做。相反,大多数提供商现在支持带有 PKCE(代码交换证明密钥)扩展的授权代码,以便在客户端应用代码中安全地交换授权代码以获取访问令牌。了解有关 从隐式流程过渡到使用 PKCE 的授权代码 的更多信息。

¥Because there was no secure way to do this to store client secrets in your app bundle, historically, many providers have offered an "Implicit flow" which enables you to request an access token without the client secret. This is no longer recommended due to inherent security risks, including the risk of access token injection. Instead, most providers now support the authorization code with PKCE (Proof Key for Code Exchange) extension to securely exchange an authorization code for an access token within your client app code. Learn more about transitioning from Implicit flow to authorization code with PKCE.

expo-auth-session 仍然支持用于旧版代码目的的隐式流。下面是隐式流程的示例实现。

¥expo-auth-session still supports Implicit flow for legacy code purposes. Below is an example implementation of the Implicit flow.

import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, useAuthRequest, ResponseType } from 'expo-auth-session';

WebBrowser.maybeCompleteAuthSession();

// Endpoint
const discovery = {
  authorizationEndpoint: 'https://accounts.spotify.com/authorize',
};

function App() {
  const [request, response, promptAsync] = useAuthRequest(
    {
      responseType: ResponseType.Token,
      clientId: 'CLIENT_ID',
      scopes: ['user-read-email', 'playlist-modify-public'],
      redirectUri: makeRedirectUri({
        scheme: 'your.app'
      }),
    },
    discovery
  );

  React.useEffect(() => {
    if (response && response.type === 'success') {
      const token = response.params.access_token;
    }
  }, [response]);

  return <Button disabled={!request} onPress={() => promptAsync()} title="Login" />;
}

存储数据

¥Storing data

在 iOS 和 Android 等原生平台上,你可以使用名为 expo-secure-store 的包(这与不安全的 AsyncStorage 不同)来保护本地访问令牌等内容。此软件包提供对 iOS 上的 密钥扣服务 和 Android 上的加密 SharedPreferences 的原生访问。没有与此功能相当的网络。

¥On native platforms like iOS, and Android you can secure things like access tokens locally using a package called expo-secure-store (This is different to AsyncStorage which is not secure). This package provides native access to keychain services on iOS and encrypted SharedPreferences on Android. There is no web equivalent to this functionality.

你可以存储身份验证结果并在以后重新水化它们,以避免提示用户再次登录。

¥You can store your authentication results and rehydrate them later to avoid having to prompt the user to login again.

import * as SecureStore from 'expo-secure-store';

const MY_SECURE_AUTH_STATE_KEY = 'MySecureAuthStateKey';

function App() {
  const [, response] = useAuthRequest({});

  React.useEffect(() => {
    if (response && response.type === 'success') {
      const auth = response.params;
      const storageValue = JSON.stringify(auth);

      if (Platform.OS !== 'web') {
        // Securely store the auth on your device
        SecureStore.setItemAsync(MY_SECURE_AUTH_STATE_KEY, storageValue);
      }
    }
  }, [response]);

  // More login code...
}
Expo 中文网 - 粤ICP备13048890号