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

# Modeling ABAC with OpenFGA

> Learn how to model Attribute-Based Access Control (ABAC) patterns using stored and dynamic attributes in OpenFGA.

export const OpenFGACodeBlock = ({code, title}) => {
  const [ready, setReady] = useState(false);
  useEffect(() => {
    if (window.openfgaDsl) {
      setReady(true);
      return;
    }
    let script = document.querySelector('script[src="/openfga-dsl-highlight.js"]');
    const loaded = () => {
      if (window.openfgaDsl) setReady(true);
    };
    if (!script) {
      script = document.createElement('script');
      script.src = '/openfga-dsl-highlight.js';
    }
    script.addEventListener('load', loaded);
    if (!script.isConnected) document.head.appendChild(script);
    return () => script.removeEventListener('load', loaded);
  }, []);
  const trimmed = code.replace(/^\n/, '').replace(/\n$/, '');
  const tokens = ready && window.openfgaDsl ? window.openfgaDsl.tokenize(trimmed) : [{
    text: trimmed,
    type: 'default'
  }];
  const colors = ready && window.openfgaDsl ? window.openfgaDsl.colors : {};
  const themeStyle = {
    '--openfga-dark-background': colors.background,
    '--openfga-dark-default': colors.default,
    '--openfga-dark-accent': colors.green
  };
  return <div className="openfga-code-viewer" style={themeStyle}>
      {title && <div className="openfga-code-viewer__title">{title}</div>}
      <div className="openfga-code-viewer__code" data-language="dsl">
        {tokens.map((t, i) => <span className="openfga-code-token" data-token={t.type || 'default'} key={i} style={{
    '--openfga-dark-token-color': t.color || colors.default
  }}>
            {t.text}
          </span>)}
      </div>
    </div>;
};

Attribute-Based Access Control (ABAC) extends traditional relationship-based access control by incorporating attributes into authorization decisions. While Role-Based Access Control (RBAC) answers "does this user have this role?", ABAC answers questions like "does this user have this role AND is their email verified?" or "can this user access this resource given their current session context?".

With OpenFGA, you can model ABAC patterns using attributes that are stored in the system or attributes that are provided dynamically with each authorization request.

This guide covers two main approaches:

1. **Stored attributes**: Attributes persisted in OpenFGA as part of your relationship data
2. **Request-time attributes**: Dynamic attributes sent with each authorization check request

## Stored Attribute Data

When attributes are relatively static and can be stored alongside your relationship data, you can model them directly in OpenFGA. This approach works well for attributes like "email verified", "SSO enabled", or "account tier".

Consider a scenario where you want to allow users to perform an action only if their organization has SSO enabled. Below are three patterns for modeling this requirement.

### Modeling Attributes as Relations

#### Using `user:*` as a Boolean Flag

The simplest approach for boolean attributes is using the [Public Access](/docs/modeling/public-access) pattern. By assigning `user:*` to a relation, you effectively create a boolean flag that applies to all users.

**When to use this approach:**

* You need a simple true/false flag
* The attribute applies uniformly to all users in a context
* You want minimal tuple overhead

<OpenFGACodeBlock
  code={`model: |
\x20 model
\x20   schema 1.1

\x20 type user
\x20\x20
\x20 type organization
\x20   relations
\x20     define member : [user]
\x20     define sso_enabled : [user:*]
\x20     define can_use_sso : sso_enabled and member

tuples:
\x20 - user: user:*
\x20   relation: sso_enabled
\x20   object: organization:acme
\x20 - user: user:anne
\x20   relation: member
\x20   object: organization:acme

tests:
\x20 - check:
\x20   - user: user:anne
\x20     object: organization:acme
\x20     assertions:
\x20       can_use_sso: true`}
/>

#### Using Self-Relations

An alternative approach is to use a self-referential relation, where an object points to itself to indicate an attribute is set. This pattern makes the relationship semantics more explicit.

**When to use this approach:**

* You want clearer semantics about what the attribute represents
* The attribute is a property of the object itself rather than a universal grant
* You prefer explicit object references over wildcards

<OpenFGACodeBlock
  code={`model: |
\x20 model
\x20   schema 1.1

\x20 type user

\x20 type organization
\x20   relations
\x20     define member : [user]
\x20     define sso_enabled : [organization]
\x20     define can_use_sso : member from sso_enabled

tuples:
\x20 - user: organization:acme
\x20   relation: sso_enabled
\x20   object: organization:acme

\x20 - user: user:anne
\x20   relation: member
\x20   object: organization:acme

tests:
\x20 - check:
\x20   - user: user:anne
\x20     object: organization:acme
\x20     assertions:
\x20       can_use_sso: true`}
/>

### Storing Attributes in Conditional Tuple Context

For attributes that require typed values beyond simple booleans, you can store the attribute value directly in the condition context of a relationship tuple. This approach combines the relationship with its associated attribute data.

**When to use this approach:**

* You need typed attribute values (strings, numbers, etc.)
* The attribute is tightly coupled to a specific relationship
* You want to avoid additional tuples for attribute storage

<OpenFGACodeBlock
  code={`model: |
\x20 model
\x20   schema 1.1
\x20\x20\x20\x20
\x20 type user
\x20\x20
\x20 type organization
\x20   relations
\x20     define member : [user]
\x20     define sso_enabled : [organization#member with sso_enabled]
\x20     define can_use_sso : sso_enabled

\x20 condition sso_enabled(sso_enabled: bool) {
\x20   sso_enabled
\x20 }

tuples:
\x20 - user: organization:acme#member
\x20   relation: sso_enabled
\x20   object: organization:acme
\x20   condition:
\x20     name: sso_enabled
\x20     context:
\x20       sso_enabled : true

\x20 - user: user:anne
\x20   relation: member
\x20   object: organization:acme

tests:
\x20 - check:
\x20   - user: user:anne
\x20     object: organization:acme
\x20     assertions:
\x20       can_use_sso: true`}
/>

## Request-Time Attribute Data

Some attributes cannot be stored because they are dynamic and change with each request. Common examples include the current time, client IP address, or the user's current session context. For these scenarios, you provide the attribute values at request time.

For time-based, IP-based conditions and more, see the [Conditions documentation](/docs/modeling/conditions) for detailed examples.

### Multi-Organization Session Context

A common use case is when users can authenticate to multiple organizations but should only access resources belonging to their currently active organization. When accessing content, you need to verify the content belongs to the organization the user is currently logged into.

You can achieve this with either contextual tuples or conditional relationship tuples.

#### Using Contextual Tuples

With this approach, you add a relation (e.g., `user_in_context`) that is not stored but sent as a contextual tuple with each request. This contextual tuple represents the user's current session context.

**When to use this approach:**

* Session context is determined entirely at request time
* You want to keep stored tuples simple and context-free
* The context applies across multiple relation checks

<OpenFGACodeBlock
  code={`model: |
\x20 model
\x20   schema 1.1

\x20 type user

\x20 type organization
\x20   relations
\x20     define user_in_context: [user]
\x20     define project_editor: [user] and user_in_context
\x20     define project_viewer: [user] and user_in_context

\x20 type project
\x20   relations
\x20     define organization: [organization]
\x20     define editor: project_editor from organization
\x20     define viewer: project_viewer from organization

tuples:
\x20 - user: user:anne
\x20   relation: project_editor
\x20   object: organization:acme

\x20 - user: organization:acme
\x20   relation: organization
\x20   object: project:acme-website

\x20 - user: user:anne
\x20   relation: project_editor
\x20   object: organization:contoso

\x20 - user: organization:contoso
\x20   relation: organization
\x20   object: project:contoso-website

tests:
\x20 - name: Anne can access the acme-website when she's in context of the acme organization
\x20   tuples:
\x20     # This contextual tuple represents Anne's current session
\x20     - user: user:anne
\x20       relation: user_in_context
\x20       object: organization:acme
\x20   check:
\x20     - user: user:anne
\x20       object: project:acme-website
\x20       assertions:
\x20           editor: true
\x20     - user: user:anne
\x20       object: project:contoso-website
\x20       assertions:
\x20           editor: false
\x20 - name: Anne can access the contoso-website when she's in context of the contoso organization
\x20   tuples:
\x20     # This contextual tuple represents Anne's current session
\x20     - user: user:anne
\x20       relation: user_in_context
\x20       object: organization:contoso
\x20   check:
\x20     - user: user:anne
\x20       object: project:acme-website
\x20       assertions:
\x20           editor: false
\x20     - user: user:anne
\x20       object: project:contoso-website
\x20       assertions:
\x20           editor: true`}
/>

#### Using Conditional Relationship Tuples

Instead of adding contextual tuples, you can attach conditions to your stored relationship tuples and provide the context values at request time. The condition compares stored values against request-time values.

**When to use this approach:**

* You want to avoid sending contextual tuples with every request
* The condition logic involves comparing stored and request-time values
* You prefer conditions over additional relations in your model

<OpenFGACodeBlock
  code={`model: |
\x20 model
\x20   schema 1.1

\x20 type user

\x20 type organization
\x20   relations
\x20     define project_editor: [user with in_context]
\x20     define project_viewer: [user with in_context]

\x20 type project
\x20   relations
\x20     define organization: [organization]
\x20     define editor: project_editor from organization
\x20     define viewer: project_viewer from organization

\x20 condition in_context(project_org: string, user_org: string) {
\x20     project_org == user_org
\x20 }

tuples:
\x20 - user: user:anne
\x20   relation: project_editor
\x20   object: organization:acme
\x20   condition:
\x20     name: in_context
\x20     context:
\x20       project_org: "acme"

\x20 - user: user:anne
\x20   relation: project_editor
\x20   object: organization:contoso
\x20   condition:
\x20     name: in_context
\x20     context:
\x20       project_org: "contoso"

\x20 - user: organization:acme
\x20   relation: organization
\x20   object: project:acme-website

\x20 - user: organization:contoso
\x20   relation: organization
\x20   object: project:contoso-website

tests:
\x20 - name: Anne can access the acme-website when she's in context of the acme organization
\x20   check:
\x20     - user: user:anne
\x20       object: project:acme-website
\x20       context:
\x20         user_org: "acme"
\x20       assertions:
\x20           editor: true
\x20     - user: user:anne
\x20       object: project:contoso-website
\x20       context:
\x20         user_org: "acme"
\x20       assertions:
\x20           editor: false
\x20 - name: Anne can access the contoso-website when she's in context of the contoso organization
\x20   check:
\x20     - user: user:anne
\x20       object: project:acme-website
\x20       context:
\x20         user_org: "contoso"
\x20       assertions:
\x20           editor: false
\x20     - user: user:anne
\x20       object: project:contoso-website
\x20       context:
\x20         user_org: "contoso"
\x20       assertions:
\x20           editor: true`}
/>

## Choosing the Right Approach

| Approach | Best For | Pros | Cons |
| - | - | - | - |
| `user:*` boolean flag | Simple on/off attributes | Minimal tuples, easy to understand | Limited to boolean values |
| Self-relations | Object-level properties | Clear semantics, explicit references | Requires self-referential tuple |
| Contextual tuples | Dynamic session context | Context-free stored data, flexible | Must send tuples with every request |
| Conditional relationship tuples | Comparing stored vs. request values | No extra tuples per request, powerful conditions | Condition logic can become complex |

When deciding between these approaches, consider:

* **Data volatility**: Use stored attributes for stable data, request-time attributes for dynamic data
* **Model complexity**: Start with simpler patterns (like `user:*`) and evolve to more complex ones as needed
* **Attribute types**: Use conditions when you need typed values beyond booleans

## Related Sections

Check out these related resources for more information about ABAC patterns in OpenFGA

<CardGroup>
  <Card title="Conditions" href="/docs/modeling/conditions">
    Learn how to use conditions for time-based, IP-based, and other dynamic checks.
  </Card>

  <Card title="Contextual Tuples" href="/docs/interacting/contextual-tuples">
    Understand how to send dynamic relationship data with each request.
  </Card>

  <Card title="Public Access" href="/docs/modeling/public-access">
    Learn about the user:\* pattern for granting access to everyone.
  </Card>

  <Card title="Modular Authorization Models" href="/docs/modeling/modular-models">
    Learn how to break down your authorization model into modules.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.