I have a public-facing API and need to authorize external users (from outside my system) who are trying to consume this API. These users are already logged into their respective systems and possess access tokens issued by their own identity providers (IDPs). My goal is to validate these tokens before granting access to my API and process the request only if the user has the required claims. Unauthorized users should be denied access.
To implement this, I plan to use Auth0 as the authorization mechanism on my side. I also need to handle the following aspects effectively:
3.Communication Between Systems:
4.Auth0 Integration:
5.Security Considerations:
By default, Auth0 issues something known as opaque token instead of a JSON Web Token(JWT). It happens when the token is intended only for Auth0's own API. To get a JWT access token from Auth0, which you can verify at your own backend server:
This Identifier servers as the API Audience which will be used to request a JSON Web Token from Auth0.
import { Auth0Provider } from "@auth0/auth0-react";
<Auth0Provider
domain="YOUR_AUTH0_DOMAIN" // The domain of your Auth0 Application
clientId="YOUR_AUTH0_CLIENT_ID" // The client id of your Auth0 Application
authorizationParams={{
redirect_uri: window.location.origin,
audience: "AUTH0_IDENTIFIER", // The identifier from your new API
}}
>
{/* Rest of your app */}
</Auth0Provider>;
const accessToken = await getAccessTokenSilently({
audience: "AUTH0_AUDIENCE", // The same audience i.e The Identifier from your new API
});
const sendRequest = async () => {
try {
const response = await axios.get(
"YOUR_API",
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
);
} catch (err) {
console.error(err);
}
};
jsonwebtoken
jwks-rsa
const jwt = require("jsonwebtoken");
const jwksClient = require("jwks-rsa");
const client = jwksClient({
jwksUri: "https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json", // The domain of your Auth0 Application
});
function getKey(header, callback) {
// This function will fetch the signing key for your accessTokens
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key.getPublicKey();
callback(null, signingKey);
});
}
const validateToken = (req, res, next) => {
// This function will validate the accessToken received in the request header
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res.status(401).json({ error: "No token provided" });
}
const token = authHeader.split(" ")[1];
jwt.verify(
token,
getKey,
{
audience: "YOUR_API_IDENTIFIER", // Identifier from your new API
issuer: "https://YOUR_AUTH0_DOMAIN/", // The domain of your Auth0 Application
algorithms: ["RS256"],
},
(err, decoded) => {
if (err) {
console.log(err);
return res.status(401).json({ error: "Token verification failed" });
} else {
console.log("Token verification successful");
req.user = decoded; // Pass decoded token to next middleware
next();
}
}
);
};
module.exports = validateToken;
The decoded token will contain the details of the user. However it will not contain the Roles of the user.
For claims validation, you can assign "Roles" to users in Auth0. To verify roles of each user at your backend server, you will need a Management API for your Auth0 Application. Using that Management API you can make calls from your backend server to Auth0 to verify user's Roles.