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.
Requirements
Installing the SDK
Install the package with:
yarn add @descope/react-native-sdk
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>
)
}
Session Management
The useSession
hook is used to manage an authenticated user session for an application.
The session manager takes care of loading and saving the session as well
as ensuring that it's refreshed when needed. 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('andy@example.com', '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
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:
try {
await refreshSessionIfAboutToExpire()
} catch (e) {
}
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
if (error.status === 401) {
try {
const resp = await descope.refresh(getCurrentRefreshToken())
await updateTokens(resp.data.sessionJwt, resp.data.refreshJwt)
retryRequest()
} catch (e) {
}
}
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.
(Android Only) Setup #2: Enable App Links
Running a flow via the React Native SDK, when targeting Android, requires setting up App Links.
This is essential for the SDK to be notified when the user has successfully
authenticated using a flow. 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:
Define a route to handle the App Link sent at the end of a flow
this code example demonstrates how app links should be handled - you can customize it to fit your app
import { useFlow } from '@descope/react-native-sdk'
const flow = useFlow()
useEffect(() => {
Linking.addEventListener('url', async (event) => {
if (event.url === 'my-deep-link-for-authenticating') {
try {
await flow.exchange(event.url)
} catch (e) {
}
}
})
return () => {
Linking.removeAllListeners('url')
}
}, [flow])
Add a matching Manifest declaration
<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">
<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" />
<data android:scheme="https" android:host="<YOUR_HOST_HERE>" android:path="/auth" />
<data android:scheme="https" android:host="<YOUR_HOST_HERE>" android:path="/magiclink" />
</intent-filter>
<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" />
<data android:scheme="myapp" android:host="auth" />
</intent-filter>
</activity>
(OPTIONAL) Setup #3: Support Magic Link Redirects
Supporting Magic Link authentication in flows requires some platform specific setup:
- On Android: add another path entry to the App Links.
This is essentially another path in the same as the app link from the previous setup step,
with different handling logic. Refer to the previous section for the manifest setup.
- On iOS: 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
Add the required Linking logic
this code example demonstrates how app links or universal links should be handled - you can customize it to fit your app
import { useFlow } from '@descope/react-native-sdk'
const flow = useFlow()
useEffect(() => {
Linking.addEventListener('url', async (event) => {
if (event.url === 'my-deep-link-for-authenticating') {
try {
await flow.exchange(event.url)
} catch (e) {
}
} else if (event.url === 'my-deep-link-for-magic-links') {
try {
await flow.resume(event.url)
} catch (e) {
}
}
})
return () => {
Linking.removeAllListeners('url')
}
}, [flow])
Run a Flow
After completing the prerequisite steps, it is now possible to run a flow.
The flow will run in a Custom Tab on Android,
or via ASWebAuthenticationSession on iOS.
Run the flow by calling the flow start function:
import { useFlow } from '@descope/react-native-sdk'
const flow = useFlow()
const { session, manageSession } = useSession()
try {
let flowAuthentication
if (session) {
flowAuthentication = {
flowId: 'flow-id',
refreshJwt: session.refreshJwt,
}
}
const resp = await flow.start('<URL_FOR_FLOW_IN_SETUP_#1>', '<URL_FOR_APP_LINK_IN_SETUP_#2>', '<OPTIONAL_CUSTOM_SCHEME_FROM_SETUP_#2>', flowAuthentication)
await manageSession(resp.data)
} catch (e) {
}
When running on iOS nothing else is required. When running on Android, flow.exchange()
function must be called.
See the app link setup for more details.
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 = () => {
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
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.