> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fga.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How to integrate within a framework

> Integrating FGA within a framework, such as Fastify or Fiber

This section will illustrate how to integrate Auth0 Fine-Grained Authorization (FGA) within a framework, such as [Fastify](https://www.fastify.io/) or [Fiber](https://docs.gofiber.io/).

## Before you start

**Node.js**

1. You have [obtained FGA API credentials](https://docs.fga.dev/integration/getting-your-api-keys.md) and set `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 OpenFGA SDK](https://docs.fga.dev/integration/install-sdk.md).
3. You have [configured the *authorization model*](https://docs.fga.dev/modeling.md) and [updated the *relationship tuples*](https://docs.fga.dev/integration/update-tuples.md).
4. You know how to [perform a Check](https://docs.fga.dev/integration/perform-check.md).
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.

**Go**

1. You have [obtained FGA API credentials](https://docs.fga.dev/integration/getting-your-api-keys.md) and set `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 OpenFGA SDK](https://docs.fga.dev/integration/install-sdk.md).
3. You have [configured the *authorization model*](https://docs.fga.dev/modeling.md) and [updated the *relationship tuples*](https://docs.fga.dev/integration/update-tuples.md).
4. You know how to [perform a Check](https://docs.fga.dev/integration/perform-check.md).
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 `document`s using one of the frameworks mentioned above. The service will authenticate users via [JWT tokens](https://auth0.com/docs/secure/tokens/json-web-tokens), which contain the user ID.

> [!WARNING]
>
> **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.

**Node.js**

For the context of this example, we will use the [Fastify framework](https://www.fastify.io/). For that we need to install the following packages:

* the [`fastify`](https://github.com/fastify/fastify) package that provides the framework itself
* the [`fastify-plugin`](https://github.com/fastify/fastify-plugin) package that allows integrating plugins with Fastify
* the [`fastify-jwt`](https://github.com/fastify/fastify-jwt) package for processing JWT tokens

Using [npm](https://www.npmjs.com):

```shell theme={null}
npm install fastify fastify-plugin fastify-jwt
```

Using [yarn](https://yarnpkg.com):

```shell theme={null}
yarn add fastify fastify-plugin fastify-jwt
```

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

```javascript theme={null}
// 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();
```

**Go**

For the context of this example, we will use the [Fiber framework](https://docs.gofiber.io/). For that we need to install the following Go packages:

* the [`gofiber/fiber`](https://docs.gofiber.io/) package that provides the Fiber framework itself
* the [`gofiber/jwt`](https://github.com/gofiber/jwt) middleware authentication layer for JWT
* the [`golang-jwt`](https://github.com/golang-jwt/jwt) package that provides Go support for JWT

```shell theme={null}
go get -u github.com/gofiber/fiber/v2 github.com/gofiber/jwt/v3 github.com/golang-jwt/jwt/v4
```

Next, we setup the web service with the `GET /read/{document}` route.

```go theme={null}
package main

import "github.com/gofiber/fiber/v2"

func main() {
  app := fiber.New()

  app.Get("/read/:document", read)

  app.Listen(":3000")
}

func read(c *fiber.Ctx) error {
  return c.SendString(c.Params("document"))
}
```

### 02. Authenticate and get user ID

Before we can call Auth0 Fine-Grained Authorization (FGA) to protect the `/read/{document}` route, we need to validate the user's JWT.

**Node.js**

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

In `jwt-authenticate.js`:

```javascript theme={null}
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`:

```javascript theme={null}
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.

```javascript theme={null}
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();

```

**Go**

We will now setup middleware to authenticate the incoming JWTs.

```go theme={null}
package main

import (
  "crypto/rand"
  "crypto/rsa"
  "log"

  "github.com/gofiber/fiber/v2"

  jwtware "github.com/gofiber/jwt/v3"
  "github.com/golang-jwt/jwt/v4"
)

var (
  // Do not do this in production.
  // In production, you would have the private key and public key pair generated
  // in advance. NEVER add a private key to any GitHub repo.
  privateKey *rsa.PrivateKey
)

func main() {
  app := fiber.New()

  // Just as a demo, generate a new private/public key pair on each run.
  rng := rand.Reader
  var err error
  privateKey, err = rsa.GenerateKey(rng, 2048)
  if err != nil {
    log.Fatalf("rsa.GenerateKey: %v", err)
  }

  // JWT Middleware
  app.Use(jwtware.New(jwtware.Config{
    SigningMethod: "RS256",
    SigningKey:    privateKey.Public(),
  }))

  app.Get("/read/:document", read)

  app.Listen(":3000")
}

func read(c *fiber.Ctx) error {
  user := c.Locals("user").(*jwt.Token)
  claims := user.Claims.(jwt.MapClaims)
  name := claims["name"].(string)
  return c.SendString(name + " read " + c.Params("document"))
}
```

### 03. Integrate the Auth0 FGA check API into the service

**Node.js**

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`:

```javascript theme={null}
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](https://docs.fga.dev/integration/perform-check.md) to see if the user has a relationship with the specified document.

In `authorize.js`:

```javascript theme={null}
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(403).send(`forbidden`);
      }
    } catch (err) {
      reply.send(err);
    }
  });
});
```

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

In `route-read.js`:

```javascript theme={null}
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`:

```javascript theme={null}
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();
```

**Go**

We will create two middlewares:

* `preauthorize` will parse the user's JWT and prepare variables needed to call Check API.
* `checkAuthorization` will call the [`Check API`](https://docs.fga.dev/integration/perform-check) to see if the user has a relationship with the specified document.

```go theme={null}
package main

import (
  "context"
  "crypto/rand"
  "crypto/rsa"
  "log"
  "os"

  "github.com/gofiber/fiber/v2"

  jwtware "github.com/gofiber/jwt/v3"
  "github.com/golang-jwt/jwt/v4"
  . "github.com/openfga/go-sdk/client"
)

var (
  // Do not do this in production.
  // In production, you would have the private key and public key pair generated
  // in advance. NEVER add a private key to any GitHub repo.
  privateKey *rsa.PrivateKey
)

func main() {
  app := fiber.New()

  // Just as a demo, generate a new private/public key pair on each run.
  rng := rand.Reader
  var err error
  privateKey, err = rsa.GenerateKey(rng, 2048)
  if err != nil {
    log.Fatalf("rsa.GenerateKey: %v", err)
  }

  // JWT Middleware
  app.Use(jwtware.New(jwtware.Config{
    SigningMethod: "RS256",
    SigningKey:    privateKey.Public(),
  }))

  app.Use("/read/:document", preauthorize)

  app.Use(checkAuthorization)

  app.Get("/read/:document", read)

  app.Listen(":3000")
}

func read(c *fiber.Ctx) error {
  user := c.Locals("user").(*jwt.Token)
  claims := user.Claims.(jwt.MapClaims)
  name := claims["name"].(string)
  return c.SendString(name + " read " + c.Params("document"))
}

func preauthorize(c *fiber.Ctx) error {
  // get the user name from JWT
  user := c.Locals("user").(*jwt.Token)
  claims := user.Claims.(jwt.MapClaims)
  name := claims["name"].(string)
  c.Locals("username", name)

  // parse the HTTP method
  switch (c.Method()) {
    case "GET":
      c.Locals("relation", "reader")
    case "POST":
      c.Locals("relation", "writer")
    case "DELETE":
      c.Locals("relation", "owner")
    default:
      c.Locals("relation", "owner")
  }

  // get the object name and prepend with type name "document:"
  c.Locals("object", "document:" + c.Params("document"))
  return c.Next()
}

// Middleware to check whether user is authorized to access document
func checkAuthorization(c *fiber.Ctx) error {
  fgaClient, err := NewSdkClient(&ClientConfiguration{
    ApiUrl:               os.Getenv("FGA_API_URL")
    StoreId:              os.Getenv("FGA_STORE_ID"),
    AuthorizationModelId: os.Getenv("FGA_MODEL_ID"),
    Credentials: &credentials.Credentials{
      Method: credentials.CredentialsMethodClientCredentials,
      Config: &credentials.Config{
        ClientCredentialsClientId:       os.Getenv("FGA_CLIENT_ID"),
        ClientCredentialsClientSecret:   os.Getenv("FGA_CLIENT_SECRET"),
        ClientCredentialsApiAudience:    os.Getenv("FGA_API_AUDIENCE"),
        ClientCredentialsApiTokenIssuer: os.Getenv("FGA_API_TOKEN_ISSUER"),
      },
    },
  })

  if err != nil {
    return fiber.NewError(fiber.StatusServiceUnavailable, "Unable to build OpenFGA client")
  }

  body := ClientCheckRequest{
    User: c.Locals("username").(string),
    Relation: c.Locals("relation").(string),
    Object: c.Locals("object").(string),
  }
  data, err := fgaClient.Check(context.Background()).Body(body).Execute()

  if err != nil {
    return fiber.NewError(fiber.StatusServiceUnavailable, "Unable to check for authorization")
  }

  if !(*data.Allowed) {
    return fiber.NewError(fiber.StatusForbidden, "Forbidden to access document")
  }

  // Go to the next middleware
  return c.Next()
}
```

## Related Sections

Take a look at the following sections for examples that you can try when integrating with SDK.

[Entitlements
Modeling Entitlements for a System in Auth0 FGA.](https://docs.fga.dev/modeling/advanced/patterns/entitlements.md)

[IoT
Modeling Fine-Grained Authorization for an IoT Security Camera System with Auth0 FGA.](https://docs.fga.dev/modeling/advanced/use-cases/iot.md)

[Slack
Modeling Authorization for Slack with Auth0 FGA.](https://docs.fga.dev/modeling/advanced/use-cases/slack.md)
