Authorization Modelling
Melange uses OpenFGA’s DSL syntax to define authorization models. This page covers the basics - for comprehensive modelling guidance, see the OpenFGA documentation.
Schema Structure
A Melange schema file (schema.fga) has this structure:
model
schema 1.1
type user
type organization
relations
define owner: [user]
define admin: [user] or owner
define member: [user] or adminKey elements:
model- Required headerschema 1.1- Schema version (Melange supports 1.1)type- Defines an object type (users, resources, etc.)relations- Permissions and roles for that type
Types
Types represent entities in your system:
type user
type organization
type repository
type teamTypes with no relations (like user) are typically subjects - the “who” in permission checks.
Relations
Relations define how subjects connect to objects:
Direct Assignment
Allow specific subject types to be directly assigned:
type document
relations
define owner: [user]
define editor: [user, team][user]- Only users can be directly assigned[user, team]- Users or teams can be assigned
Role Hierarchy (or)
Build role hierarchies where higher roles imply lower ones:
type organization
relations
define owner: [user]
define admin: [user] or owner # admins + all owners
define member: [user] or admin # members + all admins (+ owners)The or keyword creates a union - permission is granted if ANY condition matches.
Parent Inheritance (from)
Inherit permissions from a related parent object:
type repository
relations
define org: [organization] # parent relationship
define can_read: can_read from org # inherit from org
define can_write: can_write from orgThe from keyword creates a tuple-to-userset relationship:
- Look up the
orgrelation to find the parent organization - Check
can_readon that organization
Exclusion (but not)
Deny permission when a condition is true:
type pull_request
relations
define repo: [repository]
define author: [user]
define can_review: can_read from repo but not authorUsers can review if they have can_read on the repo AND are NOT the author.
Wildcard/Public Access
Allow public access using wildcards:
type repository
relations
define public: [user:*]
define can_read: public or memberThe [user:*] syntax means “any user”. In your tuples view, map public resources to subject_id = '*'.
Common Patterns
Organization with Teams
model
schema 1.1
type user
type team
relations
define member: [user]
type organization
relations
define owner: [user]
define admin: [user, team#member] or owner
define member: [user, team#member] or admin
type repository
relations
define org: [organization]
define can_read: member from org
define can_write: admin from org
define can_delete: owner from orgThe team#member syntax means “users who are members of a team”.
Document Sharing
model
schema 1.1
type user
type folder
relations
define owner: [user]
define viewer: [user] or owner
type document
relations
define parent: [folder]
define owner: [user]
define editor: [user] or owner
define viewer: [user] or editor or viewer from parentDocuments inherit viewer access from their parent folder.
Multi-Tenant SaaS
model
schema 1.1
type user
type tenant
relations
define admin: [user]
define member: [user] or admin
type project
relations
define tenant: [tenant]
define owner: [user]
define member: [user] or owner or member from tenant
define can_read: member
define can_write: owner or admin from tenantValidation
Validate your schema syntax:
melange validate --schema schemas/schema.fgaFor full validation including semantic checks, use the OpenFGA CLI:
# Install OpenFGA CLI
go install github.com/openfga/cli/cmd/fga@latest
# Validate schema
fga model validate --file schemas/schema.fgaBest Practices
1. Start with Roles, Add Permissions
Define roles first, then derive permissions from them:
type repository
relations
# Roles (assigned directly)
define owner: [user]
define admin: [user] or owner
define writer: [user] or admin
define reader: [user] or writer
# Permissions (derived from roles)
define can_read: reader
define can_write: writer
define can_delete: owner2. Use Consistent Naming
- Roles:
owner,admin,member,viewer - Permissions:
can_read,can_write,can_delete,can_invite - Parent relations:
org,parent,folder,repo
3. Avoid Deep Inheritance Chains
Each from hop adds latency. Keep inheritance shallow:
# Good: 1 hop
define can_read: can_read from org
# Avoid: Multiple hops
define can_read: can_read from parent from grandparent4. Use Exclusion Sparingly
Exclusion patterns (but not) are slower than positive checks. Use them only when necessary:
# Necessary: prevent self-review
define can_review: can_read from repo but not author
# Consider alternatives for simple casesFurther Reading
- OpenFGA Modelling Guide - Comprehensive modelling documentation
- OpenFGA Playground - Interactive schema editor and testing
- Building Blocks - Detailed explanation of relations
- OpenFGA Compatibility - What features Melange supports