JavaScript: Calling an API
This tutorial demonstrates how to make API calls for protected resources on your server. We recommend that you log in to follow this quickstart with examples configured for your account.
I want to explore a sample app
2 minutesGet a sample configured with your account settings or check it out on Github.
Most single-page apps use resources from data APIs. You may want to restrict access to those resources, so that only authenticated users with sufficient privileges can access them. Auth0 lets you manage access to these resources using API Authorization.
This tutorial shows you how to access protected resources in your API.
Create an API
In the APIs section of the Auth0 dashboard, click Create API. Provide a name and an identifier for your API. You will use the identifier later when you're configuring your Javascript Auth0 application instance. For Signing Algorithm, select RS256.
Create a Backend API
In this section, you will modify the ExpressJS that you created in part 1 so that it supports a new endpoint. This endpoint will require a valid access token to be sent in the Authorization
header for the call to be successful.
Add middleware to the backend
To begin, let's install an NPM package that will be used to validate incoming tokens to the server. From the terminal:
npm install express-oauth2-jwt-bearer
Was this helpful?
Next, open server.js
and bring in these libraries as imports at the top of the file. Also bring in the auth_config.json
file so that the script can get access to the authentication credentials that have been configured:
// .. other imports
const { auth } = require("express-oauth2-jwt-bearer");
const authConfig = require("./auth_config.json");
Was this helpful?
express-oauth2-jwt-bearer
- validates JWTs from theauthorization
header and sets thereq.auth
object
Then add a call to auth()
, which creates the middleware needed in order to validate and parse incoming access tokens. This should go after the require
statements but before any routes are defined in your app:
// create the JWT middleware
const checkJwt = auth({
audience: authConfig.audience,
issuerBaseURL: `https://${authConfig.domain}`
});
Was this helpful?
This code configures the express-oauth2-jwt-bearer
middleware with the settings that relate to your Auth0 application.
Next, open the auth_config.json
file and modify the data so that the audience
appears as a key within the JSON, using the value that you just used when creating the API:
{
"domain": "{yourDomain}",
"clientId": "{yourClientId}",
"audience": "{yourApiIdentifier}"
}
Was this helpful?
The values for domain
and clientId
should have already been specified as part of the Login tutorial. They should point to the Domain and Client ID values for your Auth0 app respectively.
Add a protected endpoint
The last thing to do on the server side is to add an API endpoint that requires an access token to be provided for the call to succeed. This endpoint will use the middleware that you created earlier in the tutorial to provide that protection in a scalable way.
Open server.js
and add a new route for /api/external
above the other routes that returns some JSON:
// ..
app.get("/api/external", checkJwt, (req, res) => {
res.send({
msg: "Your access token was successfully validated!"
});
});
// ..
Was this helpful?
Note that checkJwt
is used as the second argument here. This causes checkJwt
to be executed before the main route handler, and will reject the call and return a 401 response if:
- there is no access token present in the
Authorization
header, - or the token itself is not valid
Finally, add an error handler so that a JSON response is returned from your API in the event of a missing or invalid token:
// ..
app.use(function(err, req, res, next) {
if (err.name === "UnauthorizedError") {
return res.status(401).send({ msg: "Invalid token" });
}
next(err, req, res);
});
//..
Was this helpful?
At the end, your server.js
file will look something like the following:
const express = require("express");
const { auth } = require("express-oauth2-jwt-bearer");
const { join } = require("path");
const authConfig = require("./auth_config.json");
const app = express();
// Serve assets from the /public folder
app.use(express.static(join(__dirname, "public")));
// Create the JWT validation middleware
const checkJwt = auth({
audience: authConfig.audience,
issuerBaseURL: `https://${authConfig.domain}`
});
// Create an endpoint that uses the above middleware to
// protect this route from unauthorized requests
app.get("/api/external", checkJwt, (req, res) => {
res.send({
msg: "Your access token was successfully validated!"
});
});
// Serve the auth configuration file
app.get("/auth_config.json", (req, res) => {
res.sendFile(join(__dirname, "auth_config.json"));
});
// Serve the index page to everything else
app.get("/*", (req, res) => {
res.sendFile(join(__dirname, "index.html"));
});
// Error handler
app.use(function(err, req, res, next) {
if (err.name === "UnauthorizedError") {
return res.status(401).send({ msg: "Invalid token" });
}
next(err, req, res);
});
module.exports = app;
Was this helpful?
Test the API
With this in place, run the application using npm run dev
. In another terminal window, use the curl
tool to make a request to this API endpoint and observe the results:
curl -I localhost:3000/api/external
Was this helpful?
You should find that a 401 Unauthorized result is returned, because it requires a valid access token:
HTTP/1.1 401 Unauthorized
X-DNS-Prefetch-Control: off
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Download-Options: noopen
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Security-Policy: default-src 'self'
Content-Type: text/html; charset=utf-8
Content-Length: 1582
Date: Wed, 03 Apr 2019 13:10:43 GMT
Connection: keep-alive
Was this helpful?
Calling the API
Now you can turn your attention to the front-end application. You will update the application to provide a button to call a function which will in turn call the API that you created in the previous section.
Open index.html
and add a new button that will invoke the API call, as well as a pre
element with an ID of api-call-result
to show the result of the API call in the browser:
<button id="btn-call-api" disabled="true" onclick="callApi()">Call Api</button>
<!-- Add a container to hold the response from the call -->
<pre id="api-call-result"></pre>
Was this helpful?
Next, open public/js/app.js
. Configure the auth0
client object to specify the audience value that was added earlier to the auth_config.json
file:
const configureClient = async () => {
const response = await fetchAuthConfig();
const config = await response.json();
auth0 = await auth0Client.createAuth0Client({
domain: config.domain,
clientId: config.clientId,
authorizationParams: {
audience: config.audience // NEW - add the audience value
}
});
};
Was this helpful?
Add a new function called callApi
to app.js
, with the following content:
const callApi = async () => {
try {
// Get the access token from the Auth0 client
const token = await auth0Client.getTokenSilently();
// Make the call to the API, setting the token
// in the Authorization header
const response = await fetch("/api/external", {
headers: {
Authorization: `Bearer ${token}`
}
});
// Fetch the JSON result
const responseData = await response.json();
// Display the result in the output element
const responseElement = document.getElementById("api-call-result");
responseElement.innerText = JSON.stringify(responseData, {}, 2);
} catch (e) {
// Display errors in the console
console.error(e);
}
};
Was this helpful?
Finally, find the updateUI
function within app.js
and modify it so that the button for calling the API is enabled when the user logs in:
// public/js/app.js
const updateUI = async () => {
const isAuthenticated = await auth0Client.isAuthenticated();
document.getElementById("btn-logout").disabled = !isAuthenticated;
document.getElementById("btn-login").disabled = isAuthenticated;
// NEW - enable the button to call the API
document.getElementById("btn-call-api").disabled = !isAuthenticated;
// .. other code omitted for brevity ..
};
Was this helpful?
Now, open the browser in the application at http://localhost:3000. If the application has been stopped, run it again from the terminal using npm run dev
.
When the application starts, log in. Then, press the Call API button to make a request to the API and put the results on the screen. You should find that the result from the server is displayed on the page.