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.
- Scalable - Handle complex authorization scenarios across large systems.
- Separation - Keep authorization rules separate from business code.
- Dependable - Built on top of existing specifications that are widely used. JSON Schema (Draft 2020-12) and JMESPath standards (or other JSON query language). Authzee has a specification and reference implementation as well.
- Extensible - Easily adapt to new identity types, resources, and authorization patterns. Extend the JMESPath query language for custom capabilities.
- ACL - Access Control List support
- RBAC - Role-Based Access Control support
- ABAC - Attribute-Based Access Control support
- ReBAC - Relationship-Based Access Control support
- Ultra expressive - Create very fine-grained controls that are highly maintainable.
- Auditable - Core auditing functionality built in from the ground up to easily perform access checks.
- Agnostic - Works with any identity provider and resources. New or existing. Authzee is not an identity provider and does not provide a means to store and source identity.
- Multi-lingual - Uses widespread standards that are available in most programming languages.
- The example reference implementation in this repo uses python for ease of access.
- Other single file reference implementations are available as well.
- The reference implementation only defines the core Authzee engine. Compute and storage implementations are handled at the SDK level for each language.
#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:
- A calling entity with a "User" identity "balloon_luvr", requesting to pop a balloon.
- A grant that allows admin users to read and pop balloons.
- The authorization succeeds because the calling entity has a user identity with the admin role.
#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
npm install- install the build dependencies (first time only).npm run build- render the markdown into the static site underwebsite/dist.npm run serve- servewebsite/distat http://localhost:8080.
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.