react-native logo
javascript logo

Call a Protected API Endpoint from an Expo Mobile App

Published on May 30, 2023
Photo of Juan Cruz Martinez
Juan Cruz MartinezStaff Developer Advocate

One of the most common requirements when building mobile applications is calling an API to retrieve and save data. In scenarios where the application authenticates its users, it is common that the API will implement protection against unauthorized access, which could mean that only authenticated users would have access to make requests, or it could use more sophisticated authorization rules to validate what a user can do or access from an API.

In this guide, we will discuss the steps involved in calling a protected API endpoint from a React Native mobile app using Expo and Auth0 by Okta, including setting up authentication, handling access tokens, and sending authorized requests.

By the end of this guide, you will clearly understand how to securely call a protected API endpoint from your mobile application.

How Do We Call a Protected API Endpoint

You need to follow a few basic steps to call a protected API endpoint using OAuth2 and Auth0.

Here's an overview of how to call a protected API endpoint using OAuth:

  1. Register your application with the OAuth2 provider: To get started, you need to register your application with the OAuth2 provider, such as Auth0. This involves creating a client ID and client secret that your application will use to authenticate with the provider.
  2. Authenticate the user: To access protected resources on behalf of the user, you need to authenticate the user with the OAuth2 provider. This involves redirecting the user to the provider's login page, where they will enter their credentials and grant your application permission to access their protected resources.
  3. Obtain an access token: After the user has been authenticated, your application will receive an access token that can be used to access protected resources on behalf of the user.
  4. Call the protected API endpoint: Once you have obtained an access token, you can use it to call the protected API endpoint. The API server will validate the access token to ensure that the user has the necessary permissions to access the requested resource. The access token is typically included in the HTTP Authorization header in the format Bearer {access_token}.
  5. Handle errors: It's important to handle any errors that may occur during the request, such as expired or invalid access tokens. You can use the OAuth2 library to automatically refresh the access token if it has expired or become invalid, or display an appropriate error message to the user.

In this tutorial, we'll build a React Native mobile application with Expo, and we'll implement all the steps provided above using Auth0 and the React Native Auth0 SDK. Although we won't focus on the API code, we'll provide a sample API for you to follow along.

Before you continue, be aware that the React Native Auth0 SDK is incompatible with the "Expo Go" app. It is compatible only with Custom Dev Client and EAS builds.

Register Your Application with Auth0

Before we start coding, let's create a client application on Auth0. For that, you'll need an Auth0 account, if you don't have it yet, you can sign up for free.

Once in the dashboard, move to the Applications section and follow these steps:

  1. Click on Create Application.
  2. Provide a friendly name for your application (for example, My React Native App) and choose Native as the application type.
  3. Finally, click the Create button.

Next, you need to set up the Allowed Callback and Allowed Logout URLs. The first value tells Auth0 to which URL a user should redirect after the user authenticates, while the second value tells Auth0 to which URL the user should redirect after they log out.

Both fields will contain the same values, which are structured as follows:

  • iOS: {schema}://{yourDomain}/ios/{schema}/callback
  • Android: {schema}://{yourDomain}/android/{schema}/callback

The value for {schema} is defined in your React Native application. For this tutorial, we'll use auth0-rn-demo. The {yourDomain} value is available in the settings tab of your Auth0 application.

The Auth0 domain, client id, and client secret values are available in the settings tab of your Auth0 application

Take note of those fields as we'll use them later, but don't worry. You can always get them back from your Auth0 dashboard.

Now we are ready to build and set up the Allowed Callback and Allowed Logout URLs. In order to set the values for both Android and iOS, we'll add the two different URLs as a comma-separated string. Here's what it looks like for my chosen schema and domain: auth0-rn-demo://YOUR_AUTH0_DOMAIN/ios/com.auth0.rndemo/callback,auth0-rn-demo://YOUR_AUTH0_DOMAIN/android/com.auth0.rndemo/callback.

After you enter the values on the fields, click the Save Changes button at the end of the form to apply them.

Create an Expo Application

To get us started, let's create a new Expo application using the Expo CLI:

COMMAND
npx create-expo-app react-native-auth0-demo

Finally, enter the application folder:

COMMAND
cd react-native-auth0-demo

Now we are ready to start adding authentication to the application.

Add Authentication

Now that the basic settings for connecting your application to Auth0 are in place, it's time to add authentication to your application. This involves making some changes to your application and using the settings you have configured. Let's take it one step at a time.

COMMAND
npx expo install react-native-auth0 dotenv

Since the React Native Auth0 SDK runs custom native code that must be configured at build time, we'll have to config Expo Config Plugins with Auth0 details and generate the native source code.

The first step is to rename the file app.json to app.config.js, this is because we'll run code in the settings to load data from environment variables. Then change the structure of the file to return the JSON equivalent. At the end, your file will look like this:

app.config.js
export default {
"expo": {
"name": "react-native-auth0-demo",
"slug": "react-native-auth0-demo",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"favicon": "./assets/favicon.png"
}
}
};

Now, we are ready to add the Auth0 and schema settings:

app.config.js
export default {
"expo": {
...
"ios": {
"bundleIdentifier": "com.auth0.rndemo", // 👈 new code
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"package": "com.auth0.rndemo" // 👈 new code
},
...
// 👇 new code
"plugins": [
[
"react-native-auth0",
{
"domain": "{yourDomain}",
"customScheme": "auth0-rn-demo"
}
]
],
// 👆 new code
...
}
};

Finally, we can run npx expo prebuild to generate the native code. This process may take a few minutes, and at the end, it will update a few things and create two new folders, android and ios, with the respective native code for each platform.

Next, we can start working on the application code. Since we don't need any default boilerplate for the file App.js we'll clear it out and start from zero.

Here is what our new App.js looks like:

App.js
import { StyleSheet, Text, View } from 'react-native';
function Home() {
return (
<View style={styles.container}>
<Text style={styles.header}> Auth0 React Native - Login </Text>
</View>
);
}
export default function App() {
return (
<Home />
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
header: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});

At this point, the app is quite dull as it only displays simple text on the screen.

Configure the Auth0Provider component

The new Auth0 hooks API relies on a React Context for state management. You'll have to set up the Auth0Provider component at the uppermost level of your application.

We do it in our App component, wrapping the Home component we created.

App.js
import {Auth0Provider} from 'react-native-auth0'; // 👈 new code
...
export default function App() {
return (
<Auth0Provider domain={"{yourDomain}"} clientId={"{yourClientId}"}> {/* 👈 new code */}
<Home />
</Auth0Provider> // 👈 new code
);
}
...

The Auth0Provider expects the Auth0 Domain and Client ID from your Auth0 application.

Add login to your application

To initiate the login flow in your application and redirect users to the Auth0 Universal Login page for authentication, you simply need to call the authorize function from the Auth0 React Native SDK as follows:

App.js
// 👇 updated code
import {useAuth0, Auth0Provider} from 'react-native-auth0';
import { StyleSheet, Text, View, Button } from 'react-native';
// 👆 updated code
const customScheme = 'auth0-rn-demo'; // 👈 new code
function Home() {
// 👇 new code
const {authorize, user} = useAuth0();
const onLogin = async () => {
try {
await authorize({
scope: 'openid profile email'
}, {
customScheme: customScheme
});
} catch (e) {
throw Error('There was an issue authenticating the user. Please try again.');
}
};
// 👆 new code
...
}
...

For our use case, we'll set the scope and the customScheme of our application, however at this step you can also provide additional parameters. You can learn more about authorize on the official docs.

Next, we attached our onLogin function to a button on the screen, and we can run the application.

App.js
const Home = () => {
...
return (
<View style={styles.container}>
<Text style={styles.header}> Auth0 React Native - Login </Text>
// 👇 new code
<Button
onPress={onLogin}
title={'Log In'}
/>
// 👆 new code
</View>
);
};

Now you can run your application on your favorite emulator.

For iOS:

COMMAND
npm run ios

For Android:

COMMAND
npm run android

Running the application, you should see screens like these:

Application's start page
Auth0 Universal Login Screen

If you have tried the application and it worked as intended thus far, you'll notice that after logging in with your user, the application interface didn't change. You still have the "login" button as if nothing happened.

Let's fix that next by adding a logout button that will be visible when the user is authenticated.

Add logout to your application

To log users out, we simply need to redirect them to the Auth0 logout endpoint by calling the clearSession method from the Auth0 SDK. This will remove the sessions from the authentication server and log the user out from the application state.

Let's add it now to our application:

App.js
const Home = () => {
const {authorize, user, clearSession} = useAuth0(); // 👈 updated code
...
// 👇 new code
const onLogout = async () => {
try {
await clearSession({customScheme: customScheme});
} catch (e) {
console.log('Log out cancelled');
}
};
const loggedIn = user !== undefined && user !== null;
// 👆 new code
return (
<View style={styles.container}>
<Text style={styles.header}> Auth0 React Native - Login </Text>
// 👇 updated code
<Button
onPress={loggedIn ? onLogout : onLogin}
title={loggedIn ? 'Log Out' : 'Log In'}
/>
// 👆 updated code
</View>
);
};

When you now run the application, first you are asked to log in, and after you do, the button will change to "Log Out", and after clicking you will be logged out of the session going back to the application's first state.

Show user profile information

Now that we can control the flow of our application depending if a user is logged in or not, we can also choose to display information about the logged-in user on the screen.

Doing it is very simple with Auth0, as during authentication, the SDK receives an ID Token, which contains basic information about the user, and we can get access to that information through the useAuth0 hook.

Let's see an example:

App.js
const Home = () => {
...
return (
<View style={styles.container}>
<Text style={styles.header}> Auth0 React Native - Login </Text>
// 👇 new code
{user && <Text>You are logged in as {user.name}</Text>}
{!user && <Text>You are not logged in</Text>}
// 👆 new code
<Button
onPress={loggedIn ? onLogout : onLogin}
title={loggedIn ? 'Log Out' : 'Log In'}
/>
</View>
);
...
}

Simply by leveraging the user object we can get the user name, email and other properties available in your ID Token.

Now we are ready to talk with an API.

Set up an API

Before we can continue working on our mobile application, we'll need an API. If you already have an API protected with Auth0, great!, you can use that. If not, I recommend downloading one of our sample applications from the developer center.

I recommend downloading and setting up the Express.js Code Sample as it uses JavaScript, but any other API will do. You can opt to learn how to protect an API with Auth0, or checkout to the ready to go repository and set up your Auth0 environment variables.

After you have your API up and running, continue with this tutorial.

Call a Protected Endpoint

Calling a protected endpoint is a three steps process, first is to authenticate the user, which we have already done. Next is to get an Access Token and finally to make the HTTP call to an API endpoint with the Access Token.

The first step is the most time-consuming as you need to set many things up. Steps two and three are much more straightforward.

Request an Access Token

To call a protected API, your React Native application needs to make an HTTP request by including a valid access token in the Authorization header.

But before we can request an access token from Auth0, we need to make sure we identify the API we want access to during the authorize call. We do that by passing the audience (or API identifier) to the method call.

You set up this value when you created an Auth0 API, if you don't remember it, no worries, you can always get it from the Auth0 Dashboard.

The audience or API identifier can be found in the API section of your Auth0 Dashboard

Let's add this new value into our code:

App.js
...
const onLogin = async () => {
try {
await authorize({
scope: 'openid profile email',
audience: '{yourAudience}', // 👈 new code
}, {
customScheme: customScheme
});
} catch (e) {
throw Error('There was an issue authenticating the user. Please try again.');
}
};
...

The next step is to request your access token, which you can do in a single line of code, thanks to the Auth0 React Native SDK.

const accessToken = (await getCredentials()).accessToken;

Access tokens can be hard to work with as there are many things to consider, like Refresh Tokens, expirations, safe storage, etc. Thankfully with the Auth0 React Native SDK, we don't need to worry about any of that, as all the nuisances are abstracted into the getCredentials function.

Let's integrate it into our app in the next section.

Calling a protected endpoint

In this last step of the tutorial, we'll write a new function to retrieve an access token and call an API endpoint. If you use one of our sample APIs from the Developer Center, you can take the following code as is. However, if you run your own API, you may want to change the endpoint and how you process the output.

Let's see this in action:

App.js
import { StyleSheet, Text, View, Button, Alert } from 'react-native'; // 👈 updated code
...
const Home = () => {
const {authorize, clearSession, user, getCredentials} = useAuth0(); // 👈 updated code
...
// 👇 new code
const onCallAPI = async () => {
const accessToken = (await getCredentials()).accessToken;
const apiResponse = await fetch('http://localhost:6060/api/messages/protected', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
Alert.alert(await apiResponse.text());
}
// 👆 new code
return (
<View style={styles.container}>
<Text style={styles.header}> Auth0 React Native - Login </Text>
{user && <Text>You are logged in as {user.name}</Text>}
{!user && <Text>You are not logged in</Text>}
// 👇 new code
{user && <Button title="Call API" onPress={onCallAPI} />}
// 👆 new code
<Button
onPress={loggedIn ? onLogout : onLogin}
title={loggedIn ? 'Log Out' : 'Log In'}
/>
</View>
);
...

If everything goes well and you try the application, you'll see an Alert message from the app with the API outputs.

Example of successful API integration

Summary

Congrats! If you made it here, you have learned a lot.

You successfully set up a React Native mobile application with Auth0 to authenticate users, adapt the UI to the authentication state, and finally retrieve an access token for a specific API and then call a protected endpoint of that API.

We also covered some fundamentals of web security and how the Auth0 React Native SDK can help you solve some of the hurdles of handling tokens.

You can download the final source code built in this guide from this Github repository.

Thanks for reading!