Skip to main content

How To Integrate Within A Framework

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

Before You Start

  1. You have obtained the necessary environment variables: `FGA_API_URL`, `FGA_STORE_ID`, `FGA_API_TOKEN_ISSUER`, `FGA_API_AUDIENCE`, `FGA_CLIENT_ID` and `FGA_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_API_URL, FGA_STORE_ID, FGA_API_TOKEN_ISSUER, FGA_API_AUDIENCE, FGA_CLIENT_ID and FGA_CLIENT_SECRET as environment variables.

Step By Step

Assume that you want to have a web service for documents using one of the frameworks mentioned above. The service will authenticate users via JWT tokens, which contain the user ID.

Note

The reader should set up their own login method based on their OpenID connect provider's documentation.

Assume that you want to provide a route GET /read/{document} to return documents depending on whether the authenticated user has access to it.

01. Install And Setup Framework

The first step is to install the framework.

For the context of this example, we will use the Fastify framework. For that we need to install the following packages:

  • 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 tokens

Using npm:

npm install fastify fastify-plugin fastify-jwt

Using yarn:

yarn add fastify fastify-plugin fastify-jwt

Next, we setup the web service with the GET /read/{document} route in file app.js.

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

// Declare the 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 Okta Fine Grained Authorization (FGA) to protect the /read/{document} route, we need to validate the user's JWT.

The fastify-jwt package allows validation of JWT tokens, as well as providing access to the user's identity.

In 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 hook of a route to protect it and access the user information inside the JWT:

In 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 };
},
);
};

Finally, update app.js to register the newly added hooks.

const fastify = require('fastify')({ logger: true });
const jwtAuthenticate = require('./jwt-authenticate');
const routeread = require('./route-read');

fastify.register(jwtAuthenticate);
fastify.register(routeread);

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

03. Integrate The Okta FGA Check API Into The Service

First, we will create a decorator preauthorize to parse the incoming HTTP method as well as name of the document, and set the appropriate relation and object that we will call Check on.

In 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);
}
});
});

Next, we will create a decorator called authorize. This decorator will invoke the Check API to see if the user has a relationship with the specified document.

In authorize.js:

const fp = require('fastify-plugin');
const { OpenFgaClient, CredentialsMethod } = require('@openfga/sdk'); // OR import { OpenFgaClient } from '@openfga/sdk';

module.exports = fp(async function (fastify, opts) {
fastify.decorate('authorize', async function (request, reply) {
try {
// configure the openfga api client
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: { // Credentials are not needed if connecting to the Playground API
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER,
apiAudience: process.env.FGA_API_AUDIENCE,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
}
});
const { allowed } = await fgaClient.check({
user: request.user,
relation: request.relation,
object: request.object,
});
if (!allowed) {
reply.code(401).send(`Not authenticated`);
}
} catch (err) {
reply.send(err);
}
});
});

We can now update the GET /read/{document} route to check for user permissions.

In 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 };
},
);
};

Finally, we will register the new hooks in app.js:

const fastify = require('fastify')({ logger: true });
const jwtAuthenticate = require('./jwt-authenticate');
const preauthorize = require('./preauthorize');
const authorize = require('./authorize');
const routeread = require('./route-read');

fastify.register(jwtAuthenticate);
fastify.register(preauthorize);
fastify.register(authorize);
fastify.register(routeread);

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 Okta FGA.

IoT

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

Slack

Modeling Authorization for Slack with Okta FGA.

Have Feedback?

You can use any of our support channels for any questions or suggestions you may have.