Authzee official SDKs offer the same general API's and architecture. This makes it easier to switch languages by standardizing SDK patterns, and still leave room for language specific functionality and syntax.
They offer a flexible and scalable general purpose interface, but they are opinionated in their APIs.
If this doesn't fit your use case you are free to create your own! Try to stay compliant with the Authzee spec for the sake of portability.
NOTE - This document is not a specification but a list of recommendations. It may change and will not effect the specification or specification version of Authzee.
SDKs are considered:
- Authzee Compliant - Follows the Authzee specification.
- Maintained - Actively maintained.
- SDK Standard - Follows the Authzee SDK standard. It's not a bad thing if the library does not follow the standard. You can expect a different interface than the official SDKs.
- Official - Branded as the official Authzee SDK for a language. Again, not a bad thing if the library isn't official.
| Language | Code Repo | Package - Repo | Authzee Compliant | Maintained | SDK Standard | Official |
|---|---|---|---|---|---|---|
| python | btemplep/authzee-py | authzee - pypi.org | ✅ | ✅ | ✅ | ✅ |
The following sections outline Authzee SDK standards. All examples are given in python or JSON with python naming conventions, but the SDKs should change this based on the convention of the language.
The suggested architecture for the high level API of SDKs is to have a primary class, Authzee, and create instances from it. This class provides the only public API to the Authzee SDKs.
NOTE - The docs will use class and method terminology, but for languages that don't, translate as so:
- Classes -> struct definitions
- Class instances or objects -> struct instances
- Methods -> struct methods or functions that act on a struct
Under this object, identity definitions, resource definitions, and the JMESPath search function are static. The Authzee object is created with a compute module and a storage module. The compute module will be used to provide the compute resources for running workflows, and the storage module will be used to store and retrieve grants and other compute state objects.
NOTE - The Standard describes the minimum expectations of what an Authzee SDK should meet. SDKs are welcome to have more functionality!!!
- Low Level API
- Authzee Class
- Compute Modules
- Storage Modules
- Module Locality
- Standard Types
- Storage Latches
Authzee SDKs should offer both a high and low level APIs.
The majority of this document will focus on the high level APIs that are more easily consumed.
The low level APIs should also exist, and directly follow the specification for Authzee. This is to give a core point of logic for the higher level APIs, and the ability to use a Authzee specification-like interface directly.
It should include these values to import:
identity_definition_schema- The current identity definition schema.resource_definition_schema- The current resource definition schema.authzee_version- The current version of the Authzee specification supported.
It should include these functions from the authzee reference:
def validate_definitions(
identity_defs: List[Dict[str, AnyJSON]],
resource_defs: List[Dict[str, AnyJSON]]
) -> Dict[str, AnyJSON]:def generate_schemas(
identity_defs: List[Dict[str, AnyJSON]],
resource_defs: List[Dict[str, AnyJSON]]
) -> Dict[str, AnyJSON]:def validate_grants(
grants: List[Dict[str, AnyJSON]],
schema: Dict[str, AnyJSON]
) -> Dict[str, AnyJSON]:def validate_request(
request: Dict[str, AnyJSON], schema:
Dict[str, AnyJSON]
) -> Dict[str, AnyJSON]:def evaluate_one(
request: Dict[str, AnyJSON],
grant: Dict[str, AnyJSON],
search: Callable[[str, AnyJSON], AnyJSON]
) -> Dict[str, AnyJSON]:def audit(
request: Dict[str, AnyJSON],
grants: List[Dict[str, AnyJSON]],
search: Callable[[str, AnyJSON], AnyJSON]
) -> Dict[str, List[Dict[str, AnyJSON]]]:def authorize(
request: Dict[str, AnyJSON],
grants: List[Dict[str, AnyJSON]],
search: Callable[[str, AnyJSON], AnyJSON]
) -> Dict[str, AnyJSON]:def audit_workflow(
identity_defs: List[Dict[str, AnyJSON]],
resource_defs: List[Dict[str, AnyJSON]],
grants: List[Dict[str, AnyJSON]],
request: Dict[str, AnyJSON],
search: Callable[[str, AnyJSON], AnyJSON]
):def authorize_workflow(
identity_defs: List[Dict[str, AnyJSON]],
resource_defs: List[Dict[str, AnyJSON]],
grants: List[Dict[str, AnyJSON]],
request: Dict[str, AnyJSON],
search: Callable[[str, AnyJSON], AnyJSON]
):The Authzee class should take these arguments when created:
- Identity Definitions
- Resource Definitions
- JMESPath search function
- Compute Module type and arguments
- Storage Module type and arguments
If the language supports async, there should also be an async version, AuthzeeAsync.
These are the methods for the Authzee class. For the AuthzeeAsync class, they should all be async.
def start() -> None:- Start up Authzee app.
- Initialize runtime resources
- Needs to run before any methods or vars are accessed.
- Run the same method for compute and storage modules.
- After this method is complete these public instance vars or getters must be available:
- locality - Authzee Module Locality to tell the limit of where other Authzee instances can be created.
- parallel_paging_supported - if the instance of Authzee supports processing grant pages in parallel according to the compute and storage combination.
def shutdown() -> None:- shutdown authzee app
- clean up runtime resources
def setup() -> None:- Construct backend resources for compute and storage
- one time setup
def teardown() -> None:- tear down backend resources
- destructive - may lose all storage and compute etc.
def enact(new_grant: NewGrant) -> Grant:- add a new grant.
def repeal(grant_uuid: UUID) -> None:- delete a grant.
def get_grant(grant_uuid: UUID) -> Grant:- Get a grant by UUID
def get_grants_page(
effect: str | None,
action: str | None,
page_ref: str | None,
grants_page_size: int
) -> GrantsPage:- Retrieve a page of grants
def get_grant_page_refs_page(
effect: str | None,
action: str | None,
page_ref: str | None,
grants_page_size: int,
refs_page_size: int
) -> PageRefsPage:- Retrieve a page of grant page references for parallel pagination
- For some storage modules this may not be possible, check the
parallel_pagingvalue.
def audit_page(
request: AuthzeeRequest,
page_ref: str | None,
page_size: int,
parallel_paging: bool,
refs_page_size: int
) -> AuditPage:- Run the Audit Workflow for a page of results.
- Parallel pagination will send a whole page of grant page refs to be computed at a time which can help to cut down on latency between pages but may produce significantly more results.
def authorize(
request: AuthzeeRequest,
page_size: int,
grants_page_size: int,
refs_page_size: int
) -> AuthorizeResult:- Run the Authorize Workflow.
Compute modules provide a standard API for running workflows on compute. Compute Modules should not be used directly but through the Authzee class. They have direct access to the storage module and use it to retrieve grants. They may also use the storage module to create and retrieve latches that help with compute state. Especially for compute that is spread across multiple systems.
NOTE - If the language supports async, then the compute module functions are expected to be async. Even if the underlying functionality is not async, this is to simplify the API between the
Authzeeapp and the compute modules. As well as avoid having to create a sync and async version of each compute module.
Compute Modules should take any module specific arguments when created.
Compute modules objects should implement these methods:
def start(
identity_defs: List[IdentityDef],
resource_defs: List[ResourceDef],
search: Callable[[str, Any], Any],
storage_type: Type[StorageModule],
storage_kwargs: Dict[str, Any]
) -> None:- start up compute module
- run before use
- After this method is complete these public instance vars or getters must be available:
- locality - Compute Module Locality
- parallel_paging_supported - if the compute module supports processing grants with parallel paging
def shutdown() -> None:- shutdown compute module
- clean up runtime resources
def setup() -> None:- Construct backend resources for compute
- one time setup
def teardown() -> None:- tear down backend resources
- destructive - may lose all long lasting compute resources
def audit_page(
request: AuthzeeRequest,
page_ref: str | None,
grants_page_size: int,
parallel_paging: bool,
refs_page_size: int
) -> AuditPage:- Run the Audit Workflow for a page of results.
- Parallel pagination will send a whole page of grant page refs to be computed at a time which can help to cut down on latency between pages but may produce significantly more results.
def authorize(
request: AuthzeeRequest,
page_size: int,
grants_page_size: int,
refs_page_size: int
) -> AuthorizeResult:- Run the Authorize Workflow.
Storage modules provide a standard API for storing and retrieving grants and Storage Latches.
NOTE - If the language supports async, then the storage module functions are expected to be async. Even if the underlying functionality is not async, this is to simplify the API between the pieces.
Storage Modules should take any module specific arguments when created.
Storage modules should implement these methods:
def start(
identity_defs: List[IdentityDef],
resource_defs: List[ResourceDef]
) -> None:- start up storage module
- run before use
- After this method is complete these public instance vars or getters must be available:
- locality - Storage Module Locality
- parallel_paging_supported - if the storage modules supports parallel paging (returning a page of grant page references).
def shutdown() -> None:- shutdown storage module
- clean up runtime resources
def setup() -> None:- Construct backend resources for storage
- one time setup
def teardown() -> None:- tear down backend resources
- destructive - may lose all long lasting compute resources
def enact(new_grant: NewGrant) -> Grant:- add a new grant.
def repeal(grant_uuid: UUID) -> None:- delete a grant.
def get_grant(grant_uuid: UUID) -> Grant:- Get a grant by UUID
def get_grants_page(
effect: str | None,
action: str | None,
page_ref: str | None,
grants_page_size: int
) -> GrantsPage:- get a page of grants
def get_grant_page_refs_page(
effect: str | None,
action: str | None,
page_ref: str | None,
grants_page_size: int,
refs_page_size: int
) -> PageRefsPage:- get a page of grant page references for parallel pagination
- For some storage modules this may not be possible, check the
parallel_pagingvalue.
def create_latch() -> StorageLatch:- Create a new storage latch by UUID
def get_latch(storage_latch_uuid) -> StorageLatch:- Get a storage latch by UUID
def set_latch(storage_latch_uuid) -> StorageLatch:- Set a storage latch by UUID
def delete_latch(storage_latch_uuid) -> None:- Delete a storage latch by UUID
def cleanup_latches(before: Datetime) -> None:- Delete all latches before the specified datetime.
- Workflows should clean up their own latches, but in case of a failure this can be used to clean up zombie latches.
NOTE - When listing grants there are 2 filters:
effectandaction. Storage modules should partition grants on these 2 fields.
Module Locality is a way to describe "where" a compute module, storage module, or Authzee instance could be located in relation to one another. This will determine the compute localities that are compatible with specific storage localities. It will also limit how Authzee instances can be created.
- process - The module is localized to the same process as the Authzee app.
- Compute resources are shared with the same process as the Authzee app.
- Storage is localized to the same process and is not shared with any other instances of the Authzee app (other processes).
- Authzee instances must exist within the same process.
- system - The module is localized to the same system as the Authzee app.
- Compute resources are shared with the same system as the Authzee app.
- Storage is localized to the same system and is shared with other Authzee app instances on the system.
- Authzee instances must exist within the same system.
- network - The module is localized to network connectivity with the Authzee app.
- Compute resources are shared with systems that are reachable across the same network as the Authzee app.
- Storage is shared with systems that are reachable across the same network as the Authzee app.
- More precisely, the storage and compute is available over the network from all Authzee instances.
Compute localities are only compatible with storage localities that are the same or have a "larger" locality.
The compute locality compatibility matrix with storage localities:
| _____________\Storage Locality Compute Locality\ _______________ |
Process | System | Network |
|---|---|---|---|
| Process | ✅ | ✅ | ✅ |
| System | ❌ | ✅ | ✅ |
| Network | ❌ | ❌ | ✅ |
Authzee Localities are usually the same as the storage locality.
Storage latches are flag like objects kept in the storage module.
{
"storage_latch_uuid": "7fa89195-d455-444c-ad53-9f1c66a0fc85",
"set": false,
"created_at": "2025-07-20T04:13:17.292144Z"
}Storage latches can only be created, set, or deleted. They cannot be unset.
Compute modules may call on the storage module to create latches to manage the state of workflows. When compute is shared over the network this becomes a necessary piece to communicate different workflow statuses.
The input and output objects (data class instances, struct instances) should take a standard form when dealing with the Authzee class. The Authzee class provides the only public API to the SDKs, but the compute and storage classes are expected to provide consistent APIs to make compute and storage classes interchangeable.
Standard Types:
Authzee relies on pagination to make it's operations scalable.
page_ref represents a string token to a specific page of resources. To get the first page of a resource the page_ref should have a null value. next_ref is present in responses to be passed on the next function call to retrieve the next page. When next_ref is a null value, the current page is considered the last and should not be passed back to the function.
Grants should offer more flexibility over the reference implementation, and should be standard across the SDKs.
{
"grant_uuid": "6ce44005-8735-45ac-ae76-38e22e66f615",
"name": "My grant name",
"description": "Longer description here to explain what the grant is for.",
"tags": {
"some_key": "some_val"
},
"effect": "allow",
"actions": [
"Balloon:Pop",
"Balloon:Inflate"
],
"query": "contains(request.identities.Group[? contains(grant.data.allowed_groups, cn)]",
"query_validation": "validate",
"equality": true,
"data": {
"allowed_groups": "MyGroup"
},
"context_schema": {
"type": "object",
"required": [
"some_context_field"
]
},
"context_validation": "none"
}They should provide these additional fields over the Grant Specification, and they should also be available to query during runtime.
| Field | Type | Required | Description |
|---|---|---|---|
grant_uuid |
string(UUID) | ✅ | UUID for the grant. |
name |
string | ✅ | People friendly name for the grant |
description |
string | ✅ | People friendly long description for the grant. |
tags |
object[string, string] | ✅ | Additional people metadata for the grant. An object whose keys and values are strings. |
Generally speaking, grants should only be created or deleted, never updated. This simplifies many storage and compute structures and allows for more scalability.
Used when creating a new grant. The same as Grant without the grant_uuid field as that is generated by Authzee.
A single page of Grants.
{
"grants": [],
"next_ref": "abc123"
}| Field | Type | Required | Description |
|---|---|---|---|
grants |
array[Grant] | ✅ | The array of grants. |
next_ref |
string OR null | ✅ | A token used to reference the next page of grants to retrieve from Authzee/the storage module. |
A page of page references.
{
"page_refs": [
"abc123"
],
"next_ref": "def456"
}| Field | Type | Required | Description |
|---|---|---|---|
page_refs |
array[strings] | ✅ | An array of page references that can be used to retrieve several pages of a resource in parallel. |
next_ref |
string OR null | ✅ | A token used to reference the next page of page refs to retrieve from Authzee/the storage module. |
The standard "Request" object used to initiate an Authzee workflow. Should match the Authzee Request Specification, where some fields are updated depending on the identity and resource definitions.
A page of Audit Workflow results. Conforms to the Audit Workflow Results, where some fields are updated depending on the identity and resource definitions. It will also have a next_ref field for pagination.
The Authorize Workflow Results, which conforms to the Authzee specification, where some fields are updated depending on the identity and resource definitions.
JMESPath libraries offer the ability extend functionality by making new functions available in JMESPath queries. Authzee purposely takes a JMESPath search function as an argument so that custom functions can be used. Authzee SDKs should also offer a set of JMESPath functions out of the box the are helpful to Authzee grant queries.
- INNER JOIN - Join 2 arrays in a fashion similar to an SQL INNER JOIN.
- regex Find - Run a regex pattern on a string or array of strings to find the first match.
- regex Find All - Run a regex pattern on a string or array of strings to find all matches.
- regex Groups - Run a regex pattern on a string or array of strings to find the first match, and extract the groups.
- regex Groups All - Run a regex pattern on a string or array of strings to find all matches, and extract the groups.
The sections are given in the same format as the JMESPath Built-in Function Specification
array[object] inner_join(array[any] $lhs, array[any] $rhs, expression->boolean expr)
Modeled after SQL INNER JOIN functionality. Takes 2 arrays and an expression and returns all combinations of elements from the arrays where the expression evaluates to true.
Examples:
| Expression | Result |
|---|---|
|
|
|
[]
|
Simple python function example:
from typing import Any, Dict, List
import jmespath
def inner_join(lhs: List[Any], rhs: List[Any], expr: str) -> List[Dict[str, Any]]:
result = []
for l in lhs:
for r in rhs:
if jmespath.search( # Should use passed in jmespath search function.
expr,
{
"lhs": l,
"rhs": r
}
) is True:
result.append(
{
"lhs": l,
"rhs": r
}
)
return resultstring|null|array[string|null] regex_find(string $pattern, string|array[string] $subject)
WARNING! - Regex evaluation differs based on the underlying language/library implementation. Regex evaluation is not standardized across programming languages, and it's not expected for the SDKs to create standard regex evaluation at this point. The general functionality of the JMESPath custom functions should match between languages though.
The return value depends on the subject type:
string- Run a regex pattern against a string and return the first occurrence of the pattern ornullif there are none.array[string]- Run a regex pattern on an array of strings and return an equal length array where each element is the first occurrence of the pattern ornullif there are none.
Examples:
| Expression | Result |
|---|---|
regex_find('pattern.*', 'some string here')
|
null
|
regex_find('string.+', 'some string here')
|
"string here"
|
regex_find('string.+', `["something", "here"]`)
|
[null, null]
|
regex_find('string.+', `["something", "a string now", "here"]`)
|
[null, "string now", null]
|
Simple Python Example:
import re
from typing import List, Union
def regex_find(pattern: str, subject: Union[str, List[str]]) -> Union[None, str, List[Union[None, str]]]:
if type(subject) is str:
match = re.search(pattern, subject)
if match is not None:
return match.group()
else:
return None
if type(subject) is list:
result = []
for sub in subject:
match = re.search(pattern, sub)
if match is not None:
result.append(match.group())
else:
result.append(None)
return resultarray[string]|array[array[string]] regex_find_all(string $pattern, string|array[string] $subject)
WARNING! - Regex evaluation differs based on the underlying language/library implementation. Regex evaluation is not standardized across programming languages, and it's not expected for the SDKs to create standard regex evaluation at this point. The general functionality of the JMESPath custom functions should match between languages though.
The return value depends on the subject type:
string- Run a regex pattern against a string and return an array of all occurrences of the pattern in the string.array[string]- Run a regex pattern on an array of strings and return an equal length array of results where each element is an array of all occurrences of the pattern in the string.
Examples:
| Expression | Result |
|---|---|
regex_find_all('pattern', 'some string here')
|
[]
|
regex_find_all('string[0-9]', 'some string3 here string4')
|
["string3", "string4"]
|
regex_find_all('string.+', `["something", "here"]`)
|
[[], []]
|
|
|
Simple Python Example:
import re
from typing import List, Union
def regex_find_all(pattern: str, subject: Union[str, List[str]]) -> Union[List[str], List[List[str]]]:
if type(subject) is str:
return re.findall(pattern, subject)
if type(subject) is list:
result = []
for sub in subject:
result.append(re.findall(pattern, sub))
return resultnull|array[string|null]|array[array[string|null]|null] regex_groups(string|array[string] $subject, string $pattern)
WARNING! - Regex evaluation differs based on the underlying language/library implementation. Regex evaluation is not standardized across programming languages, and it's not expected for the SDKs to create standard regex evaluation at this point. The general functionality of the JMESPath custom functions should match between languages though.
The return value depends on the subject type:
string- Run a regex pattern against a string and return an array of all groups from the first occurrence of the pattern, ornullif there are no pattern matches. If a group has no value it will benull.array[string]- Run a regex pattern on an array of strings and return an equal length array where each element is an array of groups from the first occurrence of the pattern ornullif there are no pattern matches. If a group has no value it will benull.
Examples:
| Expression | Result |
|---|---|
regex_groups('pattern.*', 'some string here')
|
null
|
regex_groups('string.+', 'some string here')
|
[]
|
|
[null, "my_group9"]
|
regex_groups('string.+', `["something", "here"]`)
|
[null, null]
|
regex_groups('string.+', `["something", "a string now", "here"]`)
|
[null, [], null]
|
|
[null, [null, "my_group9"], null]
|
Simple Python Example:
import re
from typing import List, Union
def regex_groups(
pattern: str,
subject: Union[str, List[str]]
) -> Union[
None,
List[Union[None, str]],
List[
Union[
None,
List[
Union[None, str]
]
]
]
]:
if type(subject) is str:
match = re.search(pattern, subject)
if match is not None:
return list(match.groups())
else:
return None
if type(subject) is list:
result = []
for sub in subject:
match = re.search(pattern, sub)
if match is not None:
result.append(list(match.groups()))
else:
result.append(None)
return resultarray[array[string|null]]|array[array[array[string|null]]] regex_groups_all(string|array[string] $subject, string $pattern)
WARNING! - Regex evaluation differs based on the underlying language/library implementation. Regex evaluation is not standardized across programming languages, and it's not expected for the SDKs to create standard regex evaluation at this point. The general functionality of the JMESPath custom functions should match between languages though.
The return value depends on the subject type:
string- Run a regex pattern against a string and return an array where each item is an array of groups for each occurrence of the pattern. If a group has no value it will benull.array[string]- Run a regex pattern on an array of strings and return an equal length array where each element is an array of all occurrences of the pattern. Each element in the array of occurrences is an array of the groups. If a group has no value it will benull.
Examples:
| Expression | Result |
|---|---|
regex_groups_all('pattern.*', 'some string here')
|
[]
|
regex_groups_all('string.+', 'some string here')
|
[[]]
|
|
|
regex_groups_all('string.+', `["something", "here"]`)
|
[[], []]
|
regex_groups_all('string.+', `["something", "a string now", "here"]`)
|
[[], [[]], []]
|
|
|
Simple Python Example
import re
from typing import List, Union
def regex_groups_all(pattern: str, subject: Union[str, List[str]]) -> Union[List[str], List[List[str]]]:
if type(subject) is str:
return [list(m.groups()) if m is not None else None for m in re.finditer(pattern, subject)]
if type(subject) is list:
result = []
for sub in subject:
result.append(
[list(m.groups()) if m is not None else None for m in re.finditer(pattern, sub)]
)
return result