@descope/react-native-sdk

中文文档
0.13.1发布于 2026/09/08MITTypeScriptESM + CJS构建来源已验证近 30 天下载 10.1万

The Descope SDK for React-Native

原始使用文档
文档目录26 节
  1. Compatibility
  2. Requirements
  3. Installing the SDK
  4. Usage
  5. Wrap your app with Auth Provider
  6. Logging
  7. Session Management
  8. Accessing the Session
  9. Refreshing the Session
  10. Running Flows
  11. Setup #1: Define and host your flow
  12. (OPTIONAL) Setup #2.1: Enable App Links for Magic Link and …
  13. (OPTIONAL) Setup #2.2: Support Magic Link Redirects on iOS
  14. Run a Flow
  15. Flow Output
  16. Use the useDescope and useSession hooks in your components …
  17. Passkeys
  18. Setup #1: Configure the project
  19. Setup #2.1: Associated domains on iOS
  20. Setup #2.2: Digital Asset Links on Android
  21. Usage
  22. Troubleshooting
  23. Fixing XCode 26+ Compatibility Issues
  24. Learn More
  25. Contact Us
  26. License

Descope SDK for React Native

The Descope SDK for React Native provides convenient access to Descope for an application written on top of React Native. You can read more on the Descope Website.

Compatibility

The SDK is developed and tested against the versions listed below. Older versions may work but are not officially supported.

RequirementVersion
React Native0.78 (current release) - works on >= 0.74 via the React Native interop layer
React19.0 (shipped with React Native 0.78)
Node.js>= 18.18.0
iOS deployment target>= 13.0
Xcode>= 15.0 (tested through 26.4)
Android minSdkVersion24
Android compileSdkVersion35
Java / Kotlin JVM target17

Architecture: The library is written against the legacy bridge architecture. On apps using React Native's New Architecture (default since 0.76), the library is transparently wrapped by the interop layer - no host configuration required. Migration to the newer architecture is roadmapped.

Expo: Expo is supported, however because the library ships native code, it requires a custom development build or the bare workflow - Expo Go is a pre-built app and thus will not work.

XCode 26+: New versions of XCode may cause an fmt compatibility issue with Xcode 26's clang and affects any React Native app - not this SDK specifically. It's fixed upstream in React Native 0.84+ which ships a newer fmt. If you're on an earlier version, you'll need to patch your iOS build. See the troubleshooting section for detailed patching steps.

Requirements

Installing the SDK

Install the package with:

yarn add @descope/react-native-sdk
# or
npm i --save @descope/react-native-sdk

When targeting iOS, make sure to navigate into the ios folder and install the dependencies:

pod install

Usage

Wrap your app with Auth Provider

import { AuthProvider } from '@descope/react-native-sdk'

const AppRoot = () => {
  return (
    <AuthProvider
      projectId="my-project-id"
      // If the Descope project manages the token response in cookies, a custom domain
      // must be configured (e.g., https://auth.app.example.com)
      // and should be set as the baseUrl property.
      // baseUrl = "https://auth.app.example.com"
    >
      <App />
    </AuthProvider>
  )
}

Logging

You can configure the SDK to emit log messages by providing a logger to AuthProvider:

const logger = {
  log: (message) => MyMonitoringService.info(message),
  debug: (message) => MyMonitoringService.debug(message),
  warn: (message) => MyMonitoringService.warn(message),
  error: (message) => MyMonitoringService.error(message),
}

<AuthProvider projectId="my-project-id" logger={logger}>
  <App />
</AuthProvider>

The logger prop accepts any object that implements log, debug, warn, and error methods, making it easy to hook up to any monitoring or observability service. During development you can pass console directly for quick debugging.

The logger receives messages from both the JavaScript layer and the native SDK layer (iOS/Android), so all SDK activity - including flow execution and network calls - will be captured.

Session Management

The useSession hook is used to manage an authenticated user session for an application.

The session manager takes care of loading, saving, and automatically refreshing the session. Auto-refresh is background-aware and on by default. Pass autoRefresh={false} to <AuthProvider> if you need to opt out.

When the user completes a sign in flow successfully you should set the DescopeSession as the active session of the session manager.

import { useDescope, useSession } from '@descope/react-native-sdk'

const descope = useDescope()
const { manageSession } = useSession()

const resp = await descope.otp.email.verify('[email protected]', '123456')
manageSession(resp.data)

The session manager can then be used at any time to ensure the session is valid and to authenticate outgoing requests to your backend with a bearer token authorization header.

const { session } = useSession()

const res = await fetch('/path/to/server/api', {
  headers: {
    Authorization: `Bearer ${session.sessionJwt}`,
  },
})

When the application is relaunched the AuthProvider component will automatically load any existing session. Once the isSessionLoading flag is false, you can check if there's a session available (i.e. an authenticated user).

When the user wants to sign out of the application we revoke the active session and clear it from the session manager:

import { useDescope, useSession } from '@descope/react-native-sdk'

const descope = useDescope()
const { session, clearSession } = useSession()

await descope.logout(session.refreshJwt)
await clearSession(resp.data)

Accessing the Session

The session information can be accessed via the useSession hook, but also it might be convenient to use the getCurrentSessionToken(), getCurrentRefreshToken() and getCurrentUser() helper functions. These functions are available outside of the component render-lifecycle. This might be useful, for example, to add an authorization header to all authenticated requests.

Refreshing the Session

Most apps no longer need to refresh the session manually - the auto-refresh described above keeps the session warm in the background and survives process kills, since the persisted session in secure storage is loaded on the next launch and rescheduled automatically. The patterns below apply when you've disabled auto-refresh via autoRefresh={false}, or when you need to guarantee a fresh session at a specific moment regardless of the auto-refresh schedule (for example, immediately before a request that cannot tolerate a 401).

The guiding principal of refreshing the session is the same, regardless of any specific app architecture or network framework.

Before every authenticated request, add your authorization header to the request the way your server expects to receive it. As an optimization it is also possible to call refreshSessionIfAboutToExpire(). This async function will preemptively refresh the session token if it is about to expire, or already expired. That code might look something like this:

// ... before every authenticated request
try {
  // refresh if needed
  await refreshSessionIfAboutToExpire()
} catch (e) {
  // fail silently - as this shouldn't affect the request being performed
}

// add authorization header
request.headers.Authorization = `Bearer ${getCurrentSessionToken()}`

After every error response - if the server responds that the session token is invalid, i.e. 401 or your equivalent, try to refresh the session token and repeat the request. Otherwise, clear the session and prompt the user to re-authenticate. That code might look something like this

// ... on every error response
// assuming 401 is returned ONLY when the session JWT is invalid
if (error.status === 401) {
  try {
    const resp = await descope.refresh(getCurrentRefreshToken())
    await updateTokens(resp.data.sessionJwt, resp.data.refreshJwt)

    // you can now retry the original request
    // NEED TO MAKE SURE THAT THIS RETRY IS ONLY PERFORMED ONCE
    // otherwise, an endless loop of failed requests might occur
    retryRequest()
  } catch (e) {
    // clear the session as the user must re-authenticate
  }
}

IMPORTANT NOTE: if you find the need to pass a reference to the refreshSessionIfAboutToExpire() updateTokens() and descope from the useSession hook into some network component, make sure it is done in a lifecycle aware method. That code might look something like this:

const descope = useDescope()
const { isSessionLoading, refreshSessionIfAboutToExpire, updateTokens } = useSession()

React.useEffect(() => {
  if (!isSessionLoading) {
    setUpNetworkRefresh(refreshSessionIfAboutToExpire, updateTokens, descope)
  }
}, [isSessionLoading refreshSessionIfAboutToExpire, updateTokens, descope])

Running Flows

We can authenticate users by building and running Flows. Flows are built in the Descope flow editor. The editor allows you to easily define both the behavior and the UI that take the user through their authentication journey. Read more about it in the Descope getting started guide.

Setup #1: Define and host your flow

Before we can run a flow, it must first be defined and hosted. Every project comes with predefined flows out of the box. You can customize your flows to suit your needs and host it. Follow the getting started guide for more details. You can host the flow yourself or leverage Descope's hosted flow page. Read more about it here. You can also check out the auth-hosting repo itself.

Some authentication methods rely on leaving the application's context to authenticate the user, such as navigating to an identity provider's website to perform OAuth (social) authentication, or receiving a Magic Link via email or text message. If you do not intend to use these authentication methods, you can skip this step. Otherwise, in order for the user to get back to your application, setting up App Links is required. Once you have a domain set up and verified for sending App Links, you'll need to handle the incoming deep links in your app, and resume the flow.

Supporting Magic Link authentication in flows requires some platform specific setup: You'll need to support associated domains.

Regardless of the platform, another path is required to handle magic link redirects specifically. For the sake of this README, let's name it /magiclink

this code example demonstrates how app links should be handled - you can customize it to fit your app

import { FlowView } from '@descope/react-native-sdk'

const [deepLink, setDeepLink] = useState('')

useEffect(() => {
  Linking.addEventListener('url', async (event) => {
    if (event.url === 'https://my-deep-link-for-authenticating.com') {
      setDeepLink(event.url)
    }
  })
  return () => {
    Linking.removeAllListeners('url')
  }
})

Add a matching Manifest declaration (Android)

<activity
        android:name=".MainActivity"
        android:exported="true"
        android:launchMode="singleTask"
        android:theme="@style/LaunchTheme"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:hardwareAccelerated="true"
        android:windowSoftInputMode="adjustResize">

    <!-- add the following at the end of the activity tag, after anything you have defined currently -->

    <intent-filter android:autoVerify="true"> <!-- autoVerify required for app links -->
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- replace with your host, the path can change must must be reflected when running the flow -->
        <!-- the path should correspond with the routing path defined above -->
        <data android:scheme="https" android:host="<YOUR_HOST_HERE>" android:path="/auth" />
        <!-- see magic link setup below for more details -->
        <data android:scheme="https" android:host="<YOUR_HOST_HERE>" android:path="/magiclink" />
    </intent-filter>

    <!-- Optional: App Links are blocked by default on Opera and some other browsers. Add a custom scheme for that use case specifically -->
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <!-- replace with something unique. this will only be used as a backup for Opera and some other browsers. -->
        <data android:scheme="myapp" android:host="auth" />
    </intent-filter>
</activity>

Run a Flow

After completing the prerequisite steps, it is now possible to run a flow. The flow will run in a dedicated FlowView component which receives FlowOptions. The FlowOptions defines all of the options available when running a flow on both Android and iOS. Read the component documentation for a detailed explanation.

import { FlowView, useHostedFlowUrl, useSession } from '@descope/react-native-sdk'

const { manageSession } = useSession()

const flowUrl = 'https://myflowUrl.com'
// If using Descope hosted flows, you can provide the flow ID directly
// const flowUrl = useHostedFlowUrl('<FLOW_ID_TO_RUN>')

<FlowView
  style={styles.fill}
  flowOptions={{
      url: flowUrl,
      androidOAuthNativeProvider: 'google',
      iosOAuthNativeProvider: 'apple',
      // any other options go here
  }}
  deepLink={deepLink} // the optional deep link we defined earlier via `useState`
  onReady={() => {
    // logic to display the flow when it's ready
  }}
  onSuccess={async (jwtResponse) => {
    try {
      await manageSession(jwtResponse)
    } catch (e) {
      // handle session management error
    }
  }}
  onError={(error: string) => {
    // handle flow errors here
  }}
/>

Flow Output

Flows can return custom data by setting a flow output in the Descope console. When present, it's available on the flowOutput field of the response the flow finishes with. The object is empty for authentications that don't originate from a flow.

onSuccess={async (jwtResponse) => {
  const orgId = jwtResponse.flowOutput.organizationId as string | undefined
  // ...
}}

Use the useDescope and useSession hooks in your components in order to get authentication state, user details and utilities

This can be helpful to implement application-specific logic. Examples:

  • Render different components if current session is authenticated
  • Render user's content
  • Logout button
import { useDescope, useSession } from '@descope/react-native-sdk'
import { useCallback } from 'react'

const App = () => {
  // NOTE - `useDescope`, `useSession`, should be used inside `AuthProvider` context,
  // and will throw an exception if this requirement is not met
  const { session } = useSession()
  const { logout } = useDescope()

  const handleLogout = useCallback(() => {
    logout()
  }, [logout])

  if (session) {
    return (
      <>
        <p>Hello {session.user.name}</p>
        <button onClick={handleLogout}>Logout</button>
      </>
    )
  }

  return <p>You are not logged in</p>
}

For more SDK usage examples refer to docs

Passkeys

Passkeys work inside flows out of the box. The passkey API lets you authenticate with the platform authenticator directly, without running a flow.

Passkeys require iOS 15 or Android 9 (API 28). This does not change the minimum versions the SDK supports - it is a runtime requirement, so keep your deployment target and minSdkVersion as they are and use isSupported() to decide whether to offer passkeys.

Setup #1: Configure the project

Enable Passkeys (WebAuthn) in the Descope Console and set the top level domain. The domain is required - unlike flows, the native API has no fallback that derives it from the caller, so passkeys will fail without it.

Setup #2.1: Associated domains on iOS

Add a webcredentials associated domain entitlement matching the domain configured above:

webcredentials:example.com

Host an assetlinks.json file on that same domain, including the get_login_creds relation.

Android derives its origin from the app's signing certificate, as android:apk-key-hash:<sha256>.

Registering these certificate fingerprints in the Descope project is optional. If none are configured, any Android origin is accepted. Registering them restricts passkey authentication to builds you signed, which is recommended for production.

Note that this is all or nothing: as soon as one fingerprint is registered, any origin that does not match a registered fingerprint is rejected. Register every certificate you ship with:

  • The debug certificate, for local development builds.
  • The release (upload) certificate.
  • The Play App Signing certificate, if the app is distributed through Google Play. Play re-signs the app with a key Google holds, so the certificate that reaches users is not your upload key. Take it from Play Console -> Setup -> App integrity -> App signing.

Omitting the Play App Signing certificate produces a failure that is easy to miss during testing: passkeys work in local and internal builds, and fail only for users who installed from the Play Store.

Usage

import { useDescope, useSession } from '@descope/react-native-sdk'

const descope = useDescope()
const { manageSession } = useSession()

// authenticates an existing user, or creates one if they don't exist yet
const resp = await descope.passkey.signUpOrIn('[email protected]')
manageSession(resp.data)

signUp(loginId, name), signIn(loginId) and add(loginId, refreshJwt) are also available.

Check isSupported() before offering passkeys, so devices that cannot use them are not shown the option:

if (await descope.passkey.isSupported()) {
  // show the passkey option
}

Troubleshooting

Fixing XCode 26+ Compatibility Issues

Standard React Native project - add this to ios/Podfile inside post_install:

post_install do |installer|
  # ...existing post_install code...

  # Workaround for fmt consteval errors with Xcode 26+
  # Remove once React Native ships fmt >= 12.1.0
  fmt_base = File.join(installer.sandbox.pod_dir('fmt'), 'include', 'fmt', 'base.h')
  if File.exist?(fmt_base)
    content = File.read(fmt_base)
    patched = content.gsub(/#  define FMT_USE_CONSTEVAL 1/, '#  define FMT_USE_CONSTEVAL 0')
    File.write(fmt_base, patched)
  end
end

Expo project - because Expo regenerates the Podfile on every prebuild, use a config plugin. Create plugins/fix-fmt-consteval.js:

const { withDangerousMod } = require('expo/config-plugins')
const fs = require('fs')
const path = require('path')

module.exports = function withFixFmtConsteval(config) {
  return withDangerousMod(config, [
    'ios',
    async (config) => {
      const podfilePath = path.join(config.modRequest.platformProjectRoot, 'Podfile')
      let podfile = fs.readFileSync(podfilePath, 'utf8')
      const marker = '# Workaround for fmt consteval errors with Xcode 26+'
      if (podfile.includes(marker)) return config

      const patch = `
    ${marker}
    fmt_base = File.join(installer.sandbox.pod_dir('fmt'), 'include', 'fmt', 'base.h')
    if File.exist?(fmt_base)
      content = File.read(fmt_base)
      patched = content.gsub(/#  define FMT_USE_CONSTEVAL 1/, '#  define FMT_USE_CONSTEVAL 0')
      File.write(fmt_base, patched)
    end
`
      podfile = podfile.replace(/(post_install do \|installer\|)/, `$1\n${patch}`)
      fs.writeFileSync(podfilePath, podfile)
      return config
    },
  ])
}

Then register it in app.json:

{
  "expo": {
    "plugins": ["./plugins/fix-fmt-consteval.js"]
  }
}

Re-run expo prebuild (or delete ios/ and run expo run:ios) and the patch will be applied.

Learn More

To learn more please see the Descope Documentation and API reference page.

Contact Us

If you need help you can email Descope Support

License

The Descope SDK for React Native is licensed for use under the terms and conditions of the MIT license Agreement.

版本与发布记录

@descope/react-native-sdk 共发布 18 个版本,首次发布于 2023/10/10;过去 12 个月发布了 9 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.13.1latest2026/09/082 个月前
0.13.02026/09/032 个月前
0.12.02026/08/162 个月前
0.11.12026/06/105 个月前
0.11.02026/06/085 个月前
0.10.12026/04/296 个月前

关于 @descope/react-native-sdk 的常见问题

@descope/react-native-sdk 是什么?

The Descope SDK for React-Native

如何安装 @descope/react-native-sdk?

在项目目录运行 npm install @descope/react-native-sdk,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(*)、react-native(*)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@descope/react-native-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@descope/react-native-sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@descope/react-native-sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >= 18.18.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@descope/react-native-sdk 可以商用吗?

@descope/react-native-sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@descope/react-native-sdk 的下载量有多少?

根据 npm 官方下载统计,@descope/react-native-sdk 在 2026/10/12 – 2026/11/10 这 30 天内被下载 101,047 次,日均约 3,368 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@descope/react-native-sdk 还在维护吗?

最新版本 0.13.1 发布于 2026/09/08(2 个月前);至今共发布 18 个版本,其中过去 12 个月发布了 9 个;首次发布于 2023/10/10。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。