This guide makes heavy use of the Authzed Playground as an easy way to develop and validate the namespace being constructed. We recommend using the Playground whenever possible to develop your namespaces.
A namespace in Authzed is the definition of a class of object.
Examples include resources, users, groups, documents, or any kind of object either being protected or for whom something is being protected.
Namespaces can define zero or more relations, which indicate (as the name implies) how one object relates to another.
Examples include roles (this user has this role on this object), membership (this group is a member of this other group), ownership (this resource is owned by this organization), etc.
Namespaces in Authzed are defined in the Authzed namespace configuration format, which is a textual representation of a series of Protocol Buffer messages:
This guide will use the tenant name
example on namespaces. The Authzed Playground will accept any tenant name, but make sure to replace with your own tenant before trying against a real Authzed API.
For this example, let's imagine a basic system consisting of a resource to be protected, such as a document, and users that can potentially access that resource via one or more roles such as
To begin, we first need to define the namespaces to represent the two classes of objects in our system:
So far, our namespaces don't do much: They define the two classes of objects for our system, but as they don't have any relations defined, they cannot be related to one another in any form.
Our next step, therefore, is to decide how our namespaces can relate to one another, thus defining both the kind of data we can store in Authzed, as well as the questions we can ask of it via Check.
For this example, we've chosen a simple RBAC-style permissions model, where users can be granted a role, such as reader, on our resource (
The choice of RBAC therefore means that the relationship between our resource (
document) and our users will be defined by the roles we want, and thus, we can start by defining a relation on our document to represent one of these roles (here,
Note that the definition of
reader above is a direct definition: By not specifying anything but the name of the relation, we indicate to Authzed that the relation will only consist of its own data, unrelated to other relations.
To validate that our namespace definition is correct, the Authzed Playground supports the defintion of Validation Tuples to define the test data for our system. In addition, the Playground has an Expected Relations section, which defines a set of permissions to lookup, and the full set of users found for each.
With the definition of our two object classes as namespaces, and the
reader relation on our
document resource, we now have enough to answer a basic question:
Can a specific user/group/object view a specific document?
In Authzed, such a question is answered via use of a Check call, which takes in two objects (the
object and the
subject), as well as a possible relation between them (the
action), and returns whether the userset is reachable from the target, via the relation specified.
To add this question for validation in the Authzed Playground, we must translate the question into the Validation Tuples and the Expected Relations.
To translate the question, we first must write a tuple to represent the test data for the request.
First, let's reframe our question a bit to make the example easier:
To test the above, we need to add at least one relation from a document to a user, indicating that the user can view the document.
To do so, let's rephrase the question into a Check:
Note that we've reversed the order here: Tuples always go from the target (here
specificdocument) to the userset. Since we've reversed the order, we've also changed the name of the attribute being checked from view to
reader, to match the relation we've defined.
Our next step is to translate the objects into their object reference forms:
Here, we've indicated to Authzed that the document is of kind
example/document, while the userset is
example/user, since those are the namespaces we defined above. The portion of the reference after the
: is the ID of the object.
Note that here we are using IDs
specificuser in the object references.
In a real system, these IDs would most likely be the primary key of the rows for these objects in the database tables representing documents and users, respectively. Users are also sometimes represented by an external ID, such as an e-mail address or the
sub field of an OAuth token.
The choice of object IDs is up to you, but must be unique and stable within a namespace.
Next, we need to indicate how the user is translated into a userset: In Authzed, a userset is itself an object, and is therefore represented as an object reference (here
example/user:specificuser) as well as a relation.
In this example, we make of the implicitly defined special relation
..., which indicates that we want to treat the user as an opaque object:
Note that we could have just as easily defined a relation on the user and used it instead. A defined non-
... relation is typically used if you want to have multiple ways to represent an object as a userset.
For example, if the userset was a group, you might want to reference
example/group:specificgroup#members, to indicate it is the group's members that have
reader, rather than the group itself.
The final step in translating is to link the object references by the relation we've created:
To connect our two object references via our relation
reader, we use the syntax
@ to create the final tuple.
We now have a valid tuple representing that
specificuser can view
Now that we have test data, we can write an expected relation definition for the playground to validate that our namespace configuration and test data are valid.
Validation rules within the Playground are defined in the Expected Relations section.
Expected Relations is a dictionary, in YAML form, going from our verification objects and relations, to the expected related objects for that object and relation.
Recall the question from above that we wished to ask:
Translating this into an expected relation, we place the
action as a key of the YAML block:
We then list the expected subject(s) for that key.
Since our validation tuples only contains a single user, we only specify a single expected user, in the square brackets
Next, we must specify the expected relation to be found, in angled brackets:
Why we need to repeat the expected relation will become important later in the document.
Now that we've written a complete set of namespace configurations, validation tuple data and expected relations, we can hit the
Validate button and see that our configuration is indeed valid:
We've just successfully written our very first namespace configurations, and now have a working permissions system for documents.
While being able to ask whether a user is a reader of a document is super useful, it is expected that the majority of permissions systems will consist of more than a single role.
As we discussed at the beginning of the guide, for our example we'd like to have a second role, that of
writer, which will allow us to check if a user is a writer on the document.
To begin, we once again start by adding another relation, in this case
Next, we'd like to be able to test our new relation, so we add another validation tuple for a different user:
Finally, we add an expected relation for the new relation, to validate it:
We verify this via the playground:
The above configuration and validation exposes one issue, however: All users that are assigned to the relation
writer should also be assigned to the relation
As a naive solution, we could write a
reader tuple for every user when we write
writer, but that would get difficult to maintain very quickly.
Instead, ideally we'd like a user being assigned to
writer to be implicitly assigned to
reader, such that we only ever need to write one tuple representing the user's actual relation/role.
Fortunately, Authzed supports definining implicit relationships via the user of
userset_rewrite rule can be thought of an override: It indicates to Authzed how to compute the set of userset tuples reachable from a relation.
To begin, let's take our existing
reader relation and add a
In the above, we add a
userset_rewrite with a
union rule, which tells Authzed to union together all the users found for each child rule.
The first rule we add is
_this, which indicates to Authzed that
reader should always include its own usersets (since we are defining validation tuples for it).
Next, we want to include all the usersets found in
writer as well. We do so by adding another
child to the
union block with a
computed_userset pointing to
This tells Authzed to include all usersets found in the
writer relation on the same namespace.
Now that we've updated our namespace configuration to have
reader implicitly include all users found in
writer, we must update our expected relations to include the user(s) as well:
In the above example, we've added
example/user:differentuser#... to the
reader list, as that user will now be included in
Note, however, that the contents of the expected relation (in
differentuser is different: It says writer instead of reader, as expect that the permission of reader will have be answered by
differentuser being assigned the relation
The inclusion of the expected relation in the angle brackets ensures that validation verifies not only the users found, but also how they were found.
We can now verify via the playground that our permission was implied properly:
As we've seen above, we can use
userset_rewrite to define implicit permissions, such as writers automatically having reader permission. Implicit permissions within a namespace, however, are often insufficient: Sometimes permissions need to be inherited between namespaces.
As an example: Imagine that we add the concept of an
organization to our permissions system, where any user that is an administrator of an organization automatically gains both
writer permissions on any
document within that organization... how would we define such a permissions model?
To begin, we must first define the namespace that represents our organization. Here, we include the
admin relation, to represent the administrator role for users:
In order for our inheritance to function, we must define a way to indicate that a document "lives" under an organization. Fortunately, this is just another relationship (between a
document and its parent
organization), so we can use another relation within the
Here we've chosen to call this relation
docorg, but it could be called anything; it is generally recommended to use either a contraction of the two namespaces being connected or, alternatively, a term representing the actual relationship between the objects in those namespaces (such as
Now that we've defined the
relation to hold our new relationship, we need to define the relationship itself in our test data:
Note the use of the organization as the "user" in this relation tuple: Here the
subject of the tuple is
docorg and the
Once this tuple is added, we can add the document to the Expected Relations list, to verify that the relationship does exist:
This also means you can issue a Check that
someorg is the organization for the document: Since all relationships in Authzed are stored using the same tuple system, you can Check not just for users, but any form of userset.
Now that we have a means of stating that a document is owned by an organization, and a relation to define administrators on the organization itself, our next step is to connect the permissions defined on the document to the the adminstrator.
To do so, we once again make use of
userset_rewrite. Recall our existing namespace configuration for
In the above existing configuration, we've defined that
reader consists of the users found in itself (
_this), as well as in the relation
writer, to ensure that all writers are implicitly also readers.
Our task now is to add further implicit rules: All users which are in
admin on the document's organization should implicitly be both
To define this implicit relationship between namespaces, we first change
writer so it itself supports a
Once again we start with a child that includes
_this, as we have already defined users that are directly applied to the
Our next step is to add another branch to the
union that will include all users in the
Uh oh... The
document namespace doesn't have an
admin relation... how do we get to the organization namespace?
The answer is a new operator in namespace configurations:
tuple_to_userset provides a means of "walking" across one relation to another namespace, and the other relation within.
As defined above, we want to "walk" from the
document across the
docorg relation, to the organization, and from there we can look in the
admin relation. Therefore, we update our namespace confing like so:
tuple_to_userset is a bit verbose, so it is important to explain.
The first child of
tuple_to_userset is a
tupleset, which indicates the
relation to "walk" from the current object (in this case:
document). Here the
tupleset and its
relation indicates we want Authzed to "walk" from the document to any subjects found via the
Note that the walk does not specify the target namespace: This means if objects under two namespaces are referenced via the
docorg relation, the "walk" will succeed across both.
Using a unique name for relations referenced by
tuple_to_userset is therefore a really good practice.
The second child of
tuple_to_userset is a
computed_userset; it is very similar to the one used above for implicitly defining that all readers are writers, with one extra piece: we specify an explicit
TUPLE_USERSET_OBJECT. This is necessary to indicate to Authzed that the relation within the
computed_userset should be checked on the result of the "walk", rather than the original object.
No other values are supported here, so just always specify
TUPLE_USERSET_OBJECT when using
Now that we've declared that all users in
admin on the organization are also implicitly within
writer, let's define at least one user in our test data to be an adminstrator:
Finally, we can add the user to the declarations in Expected Relations and verify that the inheritance works:
Note that we have to add
someadminuser to both
reader still implicitly receives all members of
This starts to demonstrate the true power of Authzed: We've defined a single additional implicit rule yet received the closure of all our rules.
Note the expectation of
someadminuser, instead of
writer on the document: the permission is being granted by virtue of the user being an admin on the organization.
And that's it! We've successfully created a working set of namespace configurations, with both implicit and inherited permissions, added test data, and can now validate the entire bundle by making use of the Authzed Playground:
Want to get started for real? We’ll model your permissions with you. Speak with one of our engineers