Authzee
Authzee Logo

A highly expressive grant-based authorization engine. Flatten authorization based on organizational patterns.

Less authorization rules | More granular control | Identity agnostic | All Access Control types supported

Visit the Authzee Website for full docs and playground.

#Table of Contents

#Other Docs

#Basic Example

This example shows all of the basic ideas behind Authzee using the python reference implementation reference.py.

Run basic_example.py from the root of the project after installing the dependencies from the src/requirements.txt file.

import json
from typing import Any

import jmespath

from src.reference import authorize_workflow

# 1. Define the identities - Describe who needs to be authorized
identity_defs = [
    {
        "identity_type": "User", # unique identity type
        "schema": { # JSON Schema for Users
            "type": "object",
            "properties": {
                "id": {
                    "type": "string"
                },
                "role": {
                    "type": "string"
                },
                "department": {
                    "type": "string"
                },
                "email": {
                    "type": "string",
                    "pattern": "^.+@myorg.org$"
                }
            },
            "required": [
                "id",
                "role",
                "department",
                "email"
            ]
        }
    }
]

# 2. Define resources
resource_defs = [
    {
        "resource_type": "Balloon", # Resource types must be unique
        "actions": [
            "Balloon:Read", # Action types can be prefaced by a namespace - preferred so they are not shared across resources
            "inflate", # or just plain
            "deflate",
            "pop",
            "tie"
        ],
        "schema": { # JSON Schema
            "type": "object",
            "required": [
                "id",
                "color",
                "size"
            ],
            "properties": {
                "id": {
                    "type": "string"
                },
                "color": {
                    "type": "string"
                },
                "size": {
                    "type": "string",
                    "enum": [
                        "small",
                        "medium",
                        "large"
                    ]
                }
            }
        }
    }
]

# 3. Define Contexts - Context is extra structured data that is passed to the request
context_defs = [
    { # no context
        "context_type": "NULL",
        "schema": {
            "type": "object",
            "additionalProperties": False
        }
    },
    { # any context
        "context_type": "ANY",
        "schema": {
            "type": "object"
        }
    },
    {
        "context_type": "MySpecialContext",
        "schema": {
            "type": "object",
            "additionalProperties": False,
            "required": [
                "Team"
            ],
            "properties": {
                "Team": {
                    "type": "string"
                }
            }
        }
    }
]

# 4. Define Grants - access rules
grants = [
    {
        "effect": "allow", # allow or deny
        "actions": [ # any actions from your resources or empty to match all actions
            "Balloon:Read",
            "pop"
        ],
        "query": "contains(request.identities.User[0].role, 'admin')", # JMESPath query - Runs on {"request": <request obj>, "grant": <current grant>}
        # In this case, the above query will return `true` if the calling entity's zeroth User type identity has the admin role
        "equality": True, # If the request action is in the grants actions and the query result matches this, then the grant is "applicable".
        "applicable_on_failure": False, # If true, the grant is considered applicable when the query evaluation fails. Useful as a fail-safe for deny grants.
        "data": {} # extra free form data to store with this grant
    }
]

# 5. Create an authorization request
request = {
    "identities": { # create zero or more instances of any identity
        "User": [ # Identity type, with list of instances
            {
                "id": "balloon_luvr",
                "role": "admin",
                "department": "eng",
                "email": "ldfkjdf@myorg.org"
            }
        ]
    },
    "resource_type": "Balloon", # Request access to a specific resource type
    "action": "pop", # to perform a specific action,
    "resource": { # on a specific resource.
        "id": "b123",
        "color": "green",
        "size": "medium"
    },
    "context_type": "MySpecialContext", # include a specific context type and data
    "context": {
        "Team": "ABC"
    }
}


# 6. Define a function wrapping your preferred JSON query language to return the expected schema.
def execute(expression: str, data: Any) -> Any:
    result = {
        "result": None,
        "failure": None
    }
    try:
        result['result'] = jmespath.search(expression, data)
    except Exception as exc:
        result['failure'] = f"A JMESPath Query error has occurred: {exc}"

    return result


# 7. Given all of the previous defs and grants, check if the request is authorized.
result = authorize_workflow(
    context_defs,
    identity_defs,
    resource_defs,
    grants,
    request,
    execute
)
print(json.dumps(result, indent=4))
if result['is_authorized'] is True:
    print("✅ Access granted!")
else:
    print("❌ Access denied!")

# OUTPUT:
# {
#     "is_authorized": true,
#     "grant": {
#         "effect": "allow",
#         "actions": [
#             "Balloon:Read",
#             "pop"
#         ],
#         "query": "contains(request.identities.User[0].role, 'admin')",
#         "equality": true,
#         "applicable_on_failure": false,
#         "data": {}
#     },
#     "message": "An allow grant is applicable to the request, and there are no deny grants that are applicable to the request. Therefore, the request is authorized.",
#     "error": null
# }
# ✅ Access granted!

This basic example shows:

#Complex Example

This is a more complex example that shows how to handle multiple identities, resources, and grants. It utilizes all these elements to create a more complex request for the audit, authorize, batch audit, and batch authorize workflows.

Run complex_example.py from the root of the project after installing the dependencies from the src/requirements.txt file.

#Tests

Run the tests and generate a coverage report from the root of the project after installing the dependencies from the src/requirements.txt file.

pytest -vvv --cov=./src --cov-report=term --cov-report=html tests/unit

#Website Development

The authzee.org website lives in the website/ directory. It is a static site: a home page plus a documentation site that is generated from this repo's markdown files (README.md, docs/specification.md, and docs/sdks.md). Everything the browser runs is plain HTML, CSS, and JS — the only build tooling is a small Node script that renders the markdown into styled HTML pages.

Requires Node.js 18+. From the root of the project:

cd website
npm install
npm run build
npm run serve

Edit the markdown files or the assets under website/src, then re-run npm run build to refresh. Deployment to GitHub Pages is automated on pushes to main. See website/README.md for more detail.