> For the complete documentation index, see [llms.txt](https://fusionauth.io/docs/llms.txt)

# Fine-Grained Authorization | FusionAuth Docs

An overview of fine-grained authorization (FGA) with Permify.

# Fine-Grained Authorization

[Edit on GitHub](https://github.com/FusionAuth/fusionauth-site/blob/main/astro/src/content/docs/extend/fine-grained-authorization.mdx)

[View Markdown](https://fusionauth.io/docs/extend/fine-grained-authorization.md)

Fine-grained authorization (FGA) makes granular decisions about what data and features users can access within your applications.

Unlike the role-based access control (RBAC) provided by FusionAuth [roles](https://fusionauth.io/docs/get-started/core-concepts/roles.md) and [applications](https://fusionauth.io/docs/get-started/core-concepts/applications.md), fine-grained authorization enables you to define custom permissions for specific resources. You can also implement attribute-based access control (ABAC), relationship based access control (ReBaC), or create custom permission models tailored to your application's requirements.

## Examples[#](#examples)

Here are examples of systems where FGA is useful:

*   Facebook Groups, where all members can post, but only the original poster or group admins can edit a post.
*   A healthcare system, where a doctor can only create, view or modify records for their patients. Patients can view their records or delegate access to a guardian. Only admins can delete a record.
*   Google Drive, where a user can be granted permissions to a single file, a folder of files, or a drive containing many folders.
*   A fintech application, where an organization can require that approvals over a certain amount be approved either by an admin or by two organization members

## Usage[#](#usage)

To use Authorization With FusionAuth FGA by Permify to implement fine-grained authorization in your web or mobile application, do the following:

*   Install Permify.
*   Set up your Permify schema including users and application entities.
*   Add authorization data mapping relationships between FusionAuth users and Permify entities.
*   Add permission checks to your application to control access to functionality and data.

Permify uses the term entities to refer to anything that can have a relation in a Permify authorization schema. This is distinct from [FusionAuth entities](https://fusionauth.io/docs/get-started/core-concepts/entity-management.md), which has no formal schema and is instead managed by code.

For the remainder of this document, when the term entities is used, it refers to Permify entities. They may also be called fine-grained entities.

Let's look at each of these steps in more detail using an example.

## Licensing And Support[#](#licensing-and-support)

You can run Permify the open-source project on your own for free, forever. For more information, see the [Permify documentation](https://fusionauth.io/permify-docs.md).

Support for FusionAuth FGA by Permify requires an Enterprise plan, as does provisioning FusionAuth FGA by Permify instances in FusionAuth Cloud. [Contact us](https://fusionauth.io/contact.md) for more details.

## The Example Scenario[#](#the-example-scenario)

Suppose you have a banking application for which you want to manage access. Each bank has roles such as `member` or `teller`. People can have different roles at different banks. In addition, there are actions that can only be performed at certain times of day.

Using fine-grained authorization allows you to control access to each part of the banking application.

If you want to see a fully functioning application, [clone the example GitHub repository](https://github.com/FusionAuth/fusionauth-example-fine-grained-authorization/) and follow the instructions in the readme.

### Installing Permify[#](#installing-permify)

To install Permify, please refer to the [Permify deployment guides](https://fusionauth.io/permify-docs/setting-up/installation/intro.md). These also offer guidance on system requirements and how to deploy the software into Kubernetes and other environments.

For local development, you can run Permify using Docker similarly to how you can run FusionAuth.

The following excerpt from a `docker-compose.yml` file creates a Permify instance for this banking example:

*Permify docker compose section*

```yaml
permify:
  image: ghcr.io/permify/permify
  command: serve
  environment:
    PERMIFY_LOG_LEVEL: warn
    PERMIFY_AUTHN_ENABLED: true
    PERMIFY_AUTHN_METHOD: preshared
    PERMIFY_AUTHN_PRESHARED_KEYS: "sample_api_key_please_change"
  ports:
    - "3476:3476"
    - "3478:3478"
```

Review the Permify configuration reference for [supported configuration options](https://fusionauth.io/permify-docs/setting-up/configuration.md).

### Set Up The Permify Schema[#](#set-up-the-permify-schema)

The following schema defines the relationships between entities in the example banking scenario:

*FGA schema*

```plaintext
entity user {} 

entity bank {

    relation vp @user    
    relation teller @user    
    relation member @user
    
    attribute open_hour integer
    attribute close_hour integer

  
    action account = teller or vp or member
    action makechange = teller and is_in_time_range(open_hour, close_hour)
    action admin = teller or vp

}

rule is_in_time_range(open_hour integer, close_hour integer) {
  (open_hour <= context.data.current_hour) && (close_hour >= context.data.current_hour) 
}
```

The `user` entity represents users and the `bank` entity represents a bank.

The `bank` entity has `relation`s, `attribute`s and `action`s:

*   a `relation` defines a relationship between two entities, such as the `vp` role for a user
*   an `attribute` is a value associated with an entity, such as the `open_hour` for a bank
*   an `action` is the name of a permission that can be checked using the Permify API or SDK, such as viewing the `account` page

You can also write a `rule` like `is_in_time_range`. Rules use attributes from an entity to codify more complicated permission logic. Rules can be global or associated with a particular entity definition.

You can test a custom schema using the [Permify playground](https://play.permify.co/). This lets you verify correctness as well as visualize relationships.

For this tutorial, run the following Node.js script to load the schema into Permify:

*FGA schema load script*

```plaintext
import * as permify from "@permify/permify-node";
import * as dotenv from "dotenv";
import { readFileSync } from "fs";
import { join } from "path";

// Load environment variables
dotenv.config();

// Validate required environment variables
if (!process.env.PRESHARED_KEY) {
  console.error('Error: Missing PRESHARED_KEY from .env');
  process.exit(1);
}

if (!process.env.PERMIFY_ENDPOINT) {
  console.warn('Warning: PERMIFY_ENDPOINT not set, using default localhost:3478');
}

// Initialize Permify client
const permifyclient = permify.grpc.newClient({
  endpoint: process.env.PERMIFY_ENDPOINT || "localhost:3478",
  cert: null,
  insecure: process.env.NODE_ENV !== "production",
  pk: null,
  certChain: null,
},
  permify.grpc.newAccessTokenInterceptor(process.env.PRESHARED_KEY)
);

/**
 * Reads the authorization model from authmodel.txt
 * @returns {string} The authorization model content
 */
function readAuthModel() {
  try {
    const filePath = join(process.cwd(), 'authmodel.txt');
    const content = readFileSync(filePath, 'utf-8');
    
    if (!content.trim()) {
      throw new Error('authmodel.txt is empty');
    }
    
    return content;
  } catch (error) {
    if (error.code === 'ENOENT') {
      console.error('Error: authmodel.txt not found in current directory');
    } else {
      console.error(`Error reading authmodel.txt: ${error.message}`);
    }
    process.exit(1);
  }
}

/**
 * Creates the schema in Permify
 * @param {string} schemaContent - The authorization model schema
 */
async function createSchema(schemaContent) {
  try {
    console.log('Creating schema in Permify...');
    
    const response = await permifyclient.schema.write({
      tenantId: process.env.TENANT_ID || "t1",
      schema: schemaContent,
    });
    
    console.log(`Schema version: ${response.schemaVersion}`);
    
    return response;
  } catch (error) {
    console.error('Error creating schema:', error.message);
    
    if (error.details) {
      console.error('Details:', error.details);
    }
    
    process.exit(1);
  }
}

/**
 * Main function to orchestrate the schema creation
 */
async function main() {
  console.log('Starting Permify schema creation...\n');
  
  // Read the authorization model
  const authModel = readAuthModel();
  
  // Create the schema
  await createSchema(authModel);
}

// Run the script
main().catch((error) => {
  console.error('Unexpected error:', error);
  process.exit(1);
});
```

### Add Authorization Data[#](#add-authorization-data)

Once you have a schema, add authorization data to define entity relationships and attributes. This data will be used during the permission check step to determine if requested permissions are allowed.

The following example assigns each user a role at a bank with an Id of `1`:

*Loading relationships*

```plaintext
async function writeRelationshipTuples() {
  try {
    console.log('Writing relationship tuples...');
    
    const response = await permifyclient.data.write({
      tenantId: process.env.TENANT_ID || "t1",
      metadata: {
        schemaVersion: "", // Optional: specify schema version
      },
      tuples: [
        {
          entity: {
            type: "bank",
            id: "1"
          },
          relation: "vp",
          subject: {
            type: "user",
            id: "00000000-0000-0000-0000-000000000001"
          }
        },
        {
          entity: {
            type: "bank",
            id: "1"
          },
          relation: "member",
          subject: {
            type: "user",
            id: "00000000-0000-0000-0000-111111111111"
          }
        },
        {
          entity: {
            type: "bank",
            id: "1"
          },
          relation: "teller",
          subject: {
            type: "user",
            id: "00000000-0000-0000-0000-222222222222"
          }
        }
      ]
    });
    
    console.log(`Snap token: ${response.snapToken}`);
    
    return response;
  } catch (error) {
    console.error('Error writing relationship tuples:', error.message);
    if (error.details) {
      console.error('Details:', error.details);
    }
    throw error;
  }
}
```

You can also assign attributes to entities. The following example assigns the bank with the Id of `1` an opening hour and a closing hour:

*Loading attributes*

```plaintext
async function writeAttributes() {
  try {
    console.log('\nWriting attributes...');

    const openValue = Any.fromJSON({
        typeUrl: 'type.googleapis.com/base.v1.IntegerValue',
        value: IntegerValue.encode(IntegerValue.fromJSON({ data: 7 })).finish()
    });
    
    const closeValue = Any.fromJSON({
        typeUrl: 'type.googleapis.com/base.v1.IntegerValue',
        value: IntegerValue.encode(IntegerValue.fromJSON({ data: 17 })).finish()
    });
    
    const response = await permifyclient.data.write({
      tenantId: process.env.TENANT_ID || "t1",
      metadata: {
      },
      attributes: [
        {
          entity: {
            type: "bank",
            id: "1"
          },
          attribute: "open_hour",
          value: openValue
        },
        {
          entity: {
            type: "bank",
            id: "1"
          },
          attribute: "close_hour",
          value: closeValue
        }
      ]
    });
    
    console.log(`Snap token: ${response.snapToken}`);
    
    return response;
  } catch (error) {
    console.error('Error writing attributes:', error.message);
    if (error.details) {
      console.error('Details:', error);
    }
    throw error;
  }
}
```

### Add Permission Checks[#](#add-permission-checks)

Finally, add permission checks to your application code. A check returns a value based on whether the action is allowed. Permify uses multiple pieces of data to determine if a given permission is granted. These include:

*   the requested permission, which corresponds to the action defined in the schema, such as `account` or `admin`
*   the entity Ids and types
*   the subject Id, which indicates who is asking for this permission
*   any context, such as the time or the requester's IP address

Here's an example which has the following data:

*   a permission stored in the **`permission`** variable
*   an entity of type `bank` with an identifier of `1`
*   a subject of type `user` with an identifier drawn from a token
*   the current hour

*Checking permissions*

```plaintext
const now = new Date();
const mtTime = new Date(now.toLocaleString("en-US", {timeZone: "America/Denver"}));
const hour = mtTime.getHours();

let response = await permifyclient.permission.check({
  tenantId: "t1",
  metadata: {
    schemaVersion: "",
    depth: 20,
  },
  entity: {
    type: "bank",
    id: "1",
  },
  permission: permission,
  subject: {
    type: "user",
    id: decodedFromJwt?.sub
  },
  context: {
    data: {
        "current_hour": hour
    }
  }
});

let checkresult = response.can === permify.grpc.base.CheckResult.CHECK_RESULT_ALLOWED;
```

This check compares the value returned from Permify to the `CHECK_RESULT_ALLOWED` constant to determine if the check succeeded.

#### Where To Place Checks[#](#where-to-place-checks)

The example application maps permissions directly to pages and does the check on every request.

There are other points in any application where you can enforce authorization. These are business logic specific.

You can also perform multiple checks. For example, you can check a user's permission:

*   in the UX, which offers a better user experience, but don't rely on this to secure access
*   in the backend API
*   at the gateway
*   when performing non-idempotent operations such as modifying data
*   when performing high risk operations

## Designing Your Schema[#](#designing-your-schema)

Designing your authorization schema is a large topic that won't be covered here. Permify has modeling documentation:

*   [A modeling authorization guide](https://fusionauth.io/permify-docs/getting-started/modeling.md)
*   [RBAC modeling guides](https://fusionauth.io/permify-docs/modeling-guides/rbac/custom-roles.md)
*   [ABAC modeling guides](https://fusionauth.io/permify-docs/modeling-guides/abac/public-private.md)
*   [ReBAC modeling guides](https://fusionauth.io/permify-docs/modeling-guides/rebac/hierarchies.md)

If you'd like assistance beyond the documentation, [contact us](https://fusionauth.io/contact.md).

## Limitations[#](#limitations)

When using FusionAuth FGA by Permify, both FusionAuth and Permify are required. Plan to manage and upgrade both of these software applications.

[Maintaining the authorization data](#add-authorization-data) is a continuous process, not a one-time operation. Plan to keep the following data synced between the source of record and Permify:

*   user data
*   application specific entities
*   relations between users and entities

For user data, you can use the [User Create](https://fusionauth.io/docs/extend/events-and-webhooks/events/user/user-create.md) and [User Update](https://fusionauth.io/docs/extend/events-and-webhooks/events/user/user-update.md) webhooks to propagate user data. Make these webhooks transactional, but fast, so use something like a durable queue to receive the events. Or if you have Kafka in your system already, [publish them to Kafka](https://fusionauth.io/docs/extend/events-and-webhooks/kafka.md). Then, use one of the [Permify SDKs](https://fusionauth.io/permify-docs/getting-started/enforcement.md#sdks) and whenever changes occur, update Permify.

For application-specific entities and relations, use one of the [Permify SDKs](https://fusionauth.io/permify-docs/getting-started/enforcement.md#sdks) and whenever changes occur, update Permify.

You can store relations in FusionAuth, in the `user.data` field. In this case, you'll need to sync relations from FusionAuth to Permify using the same webhook consumer that updates user data.

## Links[#](#links)

*   Read about [FusionAuth FGA by Permify](https://fusionauth.io/permify-docs.md).
*   Learn more about [different authorization models](https://fusionauth.io/articles/identity-basics/authorization-models.md).
*   Run an [example application with fine-grained authorization](https://github.com/FusionAuth/fusionauth-example-fine-grained-authorization).