Skip to main content
This guide demonstrates practical authorization scenarios using Frontier’s RBAC model with complete API examples.

Example 1: E-Commerce Store Authorization

Let’s build a complete authorization system for an e-commerce platform called “Raystack Store” with custom shopping cart permissions.

Scenario

Raystack Store needs to:
  • Manage financial documents in a project called “Financials”
  • Control access to sensitive files with custom permissions
  • Allow the accountant (Jane) to read financial files
  • Prevent unauthorized access

Step 1: Create Custom Permissions

First, create custom permissions for file storage:
permissions:
  - name: get
    namespace: storage/file
    metadata:
      description: "Allows retrieving files from storage"
  - name: delete
    namespace: storage/file
    metadata:
      description: "Allows deleting files from storage"
  - name: post
    namespace: storage/file
    metadata:
      description: "Allows uploading files to storage"
Generated permission slugs:
  • storage_file_get
  • storage_file_delete
  • storage_file_post

Step 2: Create Custom Roles

Create roles for different file access levels:
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/raystack-store/roles' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
--data-raw '{
  "name": "storage_file_owner",
  "title": "Storage File Owner",
  "permissions": [
    "storage_file_get",
    "storage_file_post",
    "storage_file_delete"
  ],
  "metadata": {
    "description": "Full ownership and control over files"
  }
}'

Step 3: Create the Resource

John creates a sensitive financial file in the Financials project:
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/projects/financials-project-id/resources' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
--data-raw '{
  "name": "sensitive-info.txt",
  "namespace": "storage/file",
  "metadata": {
    "content_type": "text/plain",
    "description": "Q4 2023 financial details"
  }
}'
Response:
{
  "resource": {
    "id": "92f69c3a-334b-4f25-90b8-4d4f3be6b825",
    "name": "sensitive-info.txt",
    "namespace": "storage/file",
    "projectId": "financials-project-id"
  }
}

Step 4: Grant Access with Policy

John grants Jane (accountant) read access to the file:
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/policies' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
--data-raw '{
  "roleId": "storage_file_reader",
  "principalId": "[email protected]",
  "principalType": "app/user",
  "resourceId": "92f69c3a-334b-4f25-90b8-4d4f3be6b825",
  "resourceType": "storage/file"
}'

Step 5: Verify Access

Check if Jane can read the file:
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/check' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Basic amFuZUByYXlzdGFjay5jb206cGFzc3dvcmQ=' \
--data-raw '{
  "permission": "get",
  "resource": "storage/file:92f69c3a-334b-4f25-90b8-4d4f3be6b825"
}'
Check if Jane can delete (should fail):
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/check' \
-H 'Content-Type: application/json' \
-H 'Authorization: Basic amFuZUByYXlzdGFjay5jb206cGFzc3dvcmQ=' \
--data-raw '{
  "permission": "delete",
  "resource": "storage/file:92f69c3a-334b-4f25-90b8-4d4f3be6b825"
}'

Example 2: Shopping Cart Management

Build authorization for a shopping cart service with multiple user roles.

Step 1: Define Cart Permissions

cart-permissions.yaml
permissions:
  - name: delete
    namespace: potato/cart
  - name: update
    namespace: potato/cart
  - name: get
    namespace: potato/cart
  - name: list
    namespace: potato/cart

Step 2: Create Cart Roles

1

Cart Admin Role

Full control over all cart operations
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/ecommerce-org/roles' \
-H 'Content-Type: application/json' \
--data-raw '{
  "name": "cart_admin",
  "title": "Cart Administrator",
  "permissions": [
    "potato_cart_delete",
    "potato_cart_update",
    "potato_cart_get",
    "potato_cart_list"
  ]
}'
2

Cart Manager Role

Can view and modify carts but not delete
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/ecommerce-org/roles' \
-H 'Content-Type: application/json' \
--data-raw '{
  "name": "cart_manager",
  "title": "Cart Manager",
  "permissions": [
    "potato_cart_update",
    "potato_cart_get"
  ]
}'
3

Cart Viewer Role

Read-only access to carts
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/ecommerce-org/roles' \
-H 'Content-Type: application/json' \
--data-raw '{
  "name": "cart_viewer",
  "title": "Cart Viewer",
  "permissions": [
    "potato_cart_get",
    "potato_cart_list"
  ]
}'

Step 3: Assign Roles to Team

Grant different access levels to team members:
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/ecommerce-org/policies' \
-H 'Content-Type: application/json' \
--data-raw '{
  "roleId": "cart_admin",
  "principalId": "[email protected]",
  "principalType": "app/user"
}'

Example 3: Multi-Project Access Control

Manage access across multiple projects with hierarchical permissions.

Scenario Structure

Organization: TechCorp
├── Project: Backend Services
│   ├── Resource: compute/instance:api-server
│   └── Resource: compute/instance:worker-node
└── Project: Data Analytics  
    ├── Resource: storage/bucket:raw-data
    └── Resource: storage/bucket:processed-data

Access Requirements

1

Alice: Platform Admin

Full access to entire organization
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/techcorp/policies' \
-H 'Content-Type: application/json' \
--data-raw '{
  "roleId": "app_organization_owner",
  "principalId": "[email protected]",
  "principalType": "app/user"
}'
Result: Alice can access all projects and resources.
2

Bob: Backend Project Manager

Manage only Backend Services project
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/projects/backend-services/policies' \
-H 'Content-Type: application/json' \
--data-raw '{
  "roleId": "app_project_manager",
  "principalId": "[email protected]",
  "principalType": "app/user"
}'
Result: Bob can manage Backend Services but not Data Analytics.
3

Data Team: Analytics Viewers

Read-only access to Data Analytics project
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/projects/data-analytics/policies' \
-H 'Content-Type: application/json' \
--data-raw '{
  "roleId": "app_project_viewer",
  "principalId": "data-team",
  "principalType": "app/group"
}'
Result: All data team members can view Data Analytics resources.

Permission Checks

curl -L -X POST 'http://127.0.0.1:7400/v1beta1/check' \
-H 'Authorization: Bearer alice-token' \
--data-raw '{
  "permission": "update",
  "resource": "org:techcorp"
}'
# Returns: {"status": true}

Example 4: Compute Instance Deployment

Control who can deploy and manage compute instances.

Step 1: Create Compute Permissions

compute-permissions.yaml
permissions:
  - name: create
    namespace: compute/instance
  - name: deploy
    namespace: compute/instance
  - name: restart
    namespace: compute/instance
  - name: delete
    namespace: compute/instance
  - name: get
    namespace: compute/instance

Step 2: Create Deployment Roles

curl -L -X POST 'http://127.0.0.1:7400/v1beta1/roles' \
-H 'Content-Type: application/json' \
--data-raw '{
  "name": "instance_operator",
  "title": "Instance Operator",
  "permissions": [
    "compute_instance_deploy",
    "compute_instance_restart",
    "compute_instance_get"
  ],
  "metadata": {
    "description": "Deploy and restart instances"
  }
}'

Step 3: Create Instance and Assign Access

Create Instance
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/projects/backend-services/resources' \
-H 'Content-Type: application/json' \
--data-raw '{
  "name": "prod-api-server",
  "namespace": "compute/instance",
  "metadata": {
    "region": "us-east-1",
    "instance_type": "t3.large"
  }
}'
Grant Operator Access
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/policies' \
-H 'Content-Type: application/json' \
--data-raw '{
  "roleId": "instance_operator",
  "principalId": "devops-team",
  "principalType": "app/group",
  "resourceId": "prod-api-server-id",
  "resourceType": "compute/instance"
}'

Step 4: Verify Deployment Permission

curl -L -X POST 'http://127.0.0.1:7400/v1beta1/check' \
-H 'Authorization: Bearer devops-member-token' \
--data-raw '{
  "permission": "deploy",
  "resource": "compute/instance:prod-api-server-id"
}'
Response: {"status": true} - DevOps team members can deploy.

Example 5: Service Account Authorization

Grant API access to service accounts for automated systems.

Scenario

A billing service needs to:
  • Read organization information
  • View billing details
  • Create billing entries

Step 1: Create Service User

curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/acme-corp/serviceusers' \
-H 'Content-Type: application/json' \
--data-raw '{
  "name": "billing-service",
  "metadata": {
    "description": "Automated billing system"
  }
}'

Step 2: Create Custom Billing Role

curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/acme-corp/roles' \
-H 'Content-Type: application/json' \
--data-raw '{
  "name": "billing_service_role",
  "title": "Billing Service Role",
  "permissions": [
    "app_organization_get",
    "app_organization_billingview",
    "app_organization_billingmanage"
  ]
}'

Step 3: Grant Role to Service User

curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/acme-corp/policies' \
-H 'Content-Type: application/json' \
--data-raw '{
  "roleId": "billing_service_role",
  "principalId": "billing-service-id",
  "principalType": "app/serviceuser"
}'

Step 4: Service Account Makes API Calls

curl -L -X POST 'http://127.0.0.1:7400/v1beta1/check' \
-H 'Authorization: Basic YmlsbGluZy1zZXJ2aWNlOmNsaWVudC1zZWNyZXQ=' \
--data-raw '{
  "permission": "billingmanage",
  "resource": "org:acme-corp"
}'
Response: {"status": true}

Common Authorization Patterns

Pattern 1: Role Hierarchy

Implement role inheritance with different permission levels:
Viewer → Manager → Owner → Admin
Viewer:  [get]
Manager: [get, update]
Owner:   [get, update, delete]
Admin:   [get, update, delete, administer]

Pattern 2: Team-Based Access

Use groups for team access management:
Create Team Group
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/acme/groups' \
-H 'Content-Type: application/json' \
--data-raw '{"name": "frontend-team"}'
Add Members to Group
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/groups/frontend-team/members' \
--data-raw '{"userId": "[email protected]"}'

curl -L -X POST 'http://127.0.0.1:7400/v1beta1/groups/frontend-team/members' \
--data-raw '{"userId": "[email protected]"}'
Grant Role to Entire Group
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/projects/frontend-app/policies' \
--data-raw '{
  "roleId": "app_project_manager",
  "principalId": "frontend-team",
  "principalType": "app/group"
}'

Pattern 3: Temporary Access

Grant time-limited access by creating and later revoking policies:
Grant Temporary Access
# Create policy
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/organizations/acme/policies' \
--data-raw '{
  "roleId": "app_organization_viewer",
  "principalId": "[email protected]",
  "principalType": "app/user",
  "metadata": {
    "expires": "2024-12-31",
    "reason": "Q4 audit access"
  }
}'

# Later, revoke access
curl -L -X DELETE 'http://127.0.0.1:7400/v1beta1/organizations/acme/policies/{policy-id}'

Pattern 4: Resource-Specific Access

Grant access to specific resources only:
Project A Access Only
curl -L -X POST 'http://127.0.0.1:7400/v1beta1/projects/project-a/policies' \
--data-raw '{
  "roleId": "app_project_viewer",
  "principalId": "[email protected]",
  "principalType": "app/user"
}'
# User can ONLY view Project A, not Project B

Troubleshooting Authorization

Check Why Access is Denied

1

Verify User Identity

Ensure the user is authenticated and credentials are valid
curl -L -X GET 'http://127.0.0.1:7400/v1beta1/users/self' \
-H 'Authorization: Bearer user-token'
2

List User Policies

Check what roles the user has
curl -L -X GET 'http://127.0.0.1:7400/v1beta1/users/[email protected]/policies'
3

Inspect Role Permissions

Verify the role contains the needed permission
curl -L -X GET 'http://127.0.0.1:7400/v1beta1/roles/app_project_viewer'
4

Check Permission Format

Ensure permission slug is correct
# Incorrect: app.project.get
# Correct: app_project_get or just "get"
5

Verify Resource Format

Check resource namespace and ID format
# Valid formats:
# app/project:project-id
# project:project-id
# org:org-name

Common Issues

Cause: Role is disabled or permission slug doesn’t matchSolution: Check role state and verify permission slugs match exactly
curl -L -X GET 'http://127.0.0.1:7400/v1beta1/roles/{role-id}'
# Check: "state": "enabled" and permissions array
Cause: Policy principal is the group, but Check API uses user IDSolution: This is expected. The group relationship automatically cascades to members via SpiceDB’s member sub-relation.
Cause: Permission wasn’t created or slug format is wrongSolution: List permissions and verify the slug
curl -L -X GET 'http://127.0.0.1:7400/v1beta1/permissions?namespace=custom/resource'
Cause: Using wrong authentication method or client ID/secretSolution: Use Basic auth with service user credentials
# Encode: service-user-id:client-secret in base64
curl -H 'Authorization: Basic <base64-encoded-credentials>'

Best Practices Summary

Start with Predefined Roles

Use built-in roles like app_organization_owner before creating custom roles

Use Groups for Teams

Manage team access via groups instead of individual user policies

Grant Least Privilege

Assign minimal permissions needed; use viewer roles by default

Document Custom Roles

Add descriptive metadata explaining purpose and usage

Regular Access Reviews

Audit policies periodically to remove unnecessary access

Test Permission Checks

Always verify authorization works before production deployment

Next Steps

API Reference

Explore complete API documentation for all authorization endpoints

Build docs developers (and LLMs) love