Skip to main content

How to integrate within a framework

note
Auth0 Fine Grained Authorization (FGA) is the early-stage product we are building at Auth0 to solve fine-grained authorization at scale. Sign up for the Developer Community Preview to try it out, and join our Discord community if you are interested in learning more about our plans.

Please note that at this point in time, it is not considered production-ready and does not come with any SLAs; availability and uptime are not guaranteed. Limitations of Auth0 FGA during the Developer Community Preview can be found here.

This section will illustrate how to integrate Auth0 Fine Grained Authorization (FGA) within a framework environment, such as Fastify and Fiber.

Before you start

  1. You have obtained the environment, store id, client id and client secret.
  2. You have installed the SDK.
  3. You have configured the authorization model and updated the relationship tuples.
  4. You know how to perform check
  5. You have loaded FGA_ENVIRONMENT, FGA_STORE_ID, FGA_CLIENT_ID and FGA_CLIENT_SECRET as environment variables.

Step by Step

Assume that you want to validate whether the authenticated user has read access to the specified document via GET /read/{document}. For this example, we assume that the user id is in the JWT token encoded with the GET request.

01. Install and setup framework SDK

The first step is to install the framework SDK.

We will install npm packages fastify, fastify-plugin and fastify-jwt. For the context of this document, we will use the Fastify framework. For that we need to install:

  • the fastify package that provides the framework itself
  • the fastify-plugin package that allows integrating plugins with Fastify
  • the fastify-jwt package for processing JWT token, used to authenticate the user and validate their identity.

Using npm:

npm install fastify fastify-plugin fastify-jwt

Using yarn:

yarn add fastify fastify-plugin fastify-jwt

Next, we setup /read/{document} endpoint with fastify.


// Require the framework and instantiate it
const fastify = require('fastify')({ logger: true });

// Declare a route
fastify.get('/read/:document', async (request, reply) => {
return { read: request.params.document }
});

// Run the server!
const start = async () => {
try {
await fastify.listen(3000);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
}
start();

02. Authenticate and get user ID

Before we can call Auth0 Fine Grained Authorization (FGA) to check for read relationship, we need to validate the bearer JWT token. Reader should setup their own login method based on their OpenID connect provider's documentation.

The fastify-jwt hook allows validation of the issued JWT as well as access to user's identity.

First, create a plugin to authenticate.

For jwt-authenticate.js

const fp = require('fastify-plugin');

module.exports = fp(async function(fastify, opts) {
fastify.register(require('fastify-jwt'), {
secret: {
private: readFileSync(`${path.join(__dirname, 'certs')}/private.key`, 'utf8'),
public: readFileSync(`${path.join(__dirname, 'certs')}/public.key`, 'utf8')
},
sign: { algorithm: 'RS256' }
});

fastify.decorate('authenticate', async function(request, reply) {
try {
await request.jwtVerify();
} catch (err) {
reply.send(err);
}
});
});

Then use the preValidation of a route to protect it and access the user information inside:

For route-read.js,

module.exports = async function(fastify, opts) {
fastify.get(
'/read/:document',
{
preValidation: [fastify.authenticate]
},
async function(request, reply) {
// the user's id is in request.user
return { read: request.params.document }
}
)
};

Then update the app.js to register with the newly added hooks.


// Require the framework and instantiate it
const fastify = require('fastify')({ logger: true });
const jwt-authenticate = require('./jwt-authenticate');
const routeread = require('./route-read');

fastify.register(jwt-authenticate);
fastify.register(routeread);

// Run the server!
const start = async () => {
try {
await fastify.listen(3000);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
}
start();

With that, we are ready to integrate Auth0 FGA check API into the framework.

03. Integrate Auth0 FGA check API into framework

To integrate Auth0 FGA check API, we will create a plugin for authorization

We will create a decorator called authorize. This decorate will will call invoke the check API on whether the user has read relationship with the specified document.

Before we do that, we will need to create a decorator preauthorize to parse the HTTP method as well as name of the document. For file preauthorize.js

const fp = require('fastify-plugin')

module.exports = fp(async function(fastify, opts) {
fastify.decorate('preauthorize', async function(request, reply) {
try {
switch (request.method) {
case 'GET':
request.relation = 'reader';
break;
case 'POST':
request.relation = 'writer';
break;
case 'DELETE':
default:
request.relation = 'owner';
break;
}
request.object = `document:${request.params.document}`;
} catch (err) {
reply.send(err);
}
});
});

We have a new file authorize.js

const fp = require('fastify-plugin')
const { Auth0FgaApi } = require('@auth0/fga'); // OR import { Auth0FgaApi } from '@auth0/fga';

module.exports = fp(async function(fastify, opts) {
fastify.decorate('authorize', async function(request, reply) {
try {
// configure the auth0 fga api client
const auth0fga = new Auth0FgaApi({
environment: process.env.FGA_ENVIRONMENT,
storeId: process.env.FGA_STORE_ID,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
});
const { allowed } = await auth0fga.check({
tuple_key: {
user: request.user,
relation: request.relation,
object: request.object,
}
});
if (!allowed) {
reply.code(401).send(`Not authenticated`);
}
} catch (err) {
reply.send(err);
}
});
});

We can then update the read route to check for authorize.

For route-read.js,

module.exports = async function(fastify, opts) {
fastify.get(
'/read/:document',
{
preValidation: [fastify.authenticate, fastify.preauthorize, fastify.authorize]
},
async function(request, reply) {
// the user's id is in request.user
return { read: request.params.document };
}
)
};

We will also need to register this new hook


// Require the framework and instantiate it
const fastify = require('fastify')({ logger: true });
const jwt-authenticate = require('./jwt-authenticate');
const preauthorize = require('./preauthorize');
const authorize = require('./authorize');
const routeread = require('./route-read');

fastify.register(jwt-authenticate);
fastify.register(preauthorize);
fastify.register(authorize);
fastify.register(routeread);

// Run the server!
const start = async () => {
try {
await fastify.listen(3000);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
}
start();

Entitlements

Modeling Entitlements for a System in Auth0 FGA.

IoT

Modeling Fine Grained Authorization for an IoT Security Camera System with Auth0 FGA.

Slack

Modeling Authorization for Slack with Auth0 FGA.

Have Feedback?

Join us on the Discord community if you have any questions or suggestions.