Skip to main content

Protecting Your First App

Introduction

This guide walks through the steps required to deeply integrate an application with Authzed or SpiceDB. Not all software requires this level of integration, but it is preferable for greenfield applications or applications that are central in an architecture with multiple services.

Instead of introducing an unfamiliar example app and altering various locations in its code, this guide is written such that each step is a standalone snippet of code that demonstrates an integration point and finding where those points exist in your codebase is an exercise left to the reader.

Prerequisites

One of:

Installation

The first step to integrating any software is ensuring you have an API client.

Each tool is installed with its ecosystem's package management tools:

brew install authzed/tap/zed
zed context set blog grpc.authzed.com:443 t_your_token_here_1234567deadbeef

Defining and Applying a Schema

Regardless of whether or not you have a preexisting schema written, integrating a new application will typically require you add new definitions to the Schema.

As a quick recap, Schemas define the objects, their relations, and their checkable permissions that will be available to be used with the permission system.

We'll be using the following blog example throughout this guide:

definition blog/user {}

definition blog/post {
relation reader: blog/user
relation writer: blog/user
permission read = reader + writer
permission write = writer
}

This example defines two types of objects that will be used in the permissions system: user and post. Each post can have two kinds of relations to users: reader and writer. Each post can have two permissions checked: read and write. The read permission unions together both readers and writers, so that any writer is implicitly granted read, as well. Feel free to start with design to modify and test your own experiments in the playground.

With a schema designed, we can now move on using our client to to apply that schema to the permission system.

zed schema write <(cat << EOF
definition blog/user {}

definition blog/post {
relation reader: blog/user
relation writer: blog/user

permission read = reader + writer
permission write = writer
}
EOF
)
note

Similar to applying schema changes for relational databases, all changes to a schema must be backwards compatible.

In production environments where relations change, you will likely want to write data migrations and apply those changes using a schema migration toolchain.

Storing Relationships

After a permission system has its schema applied, it is ready to have its relationships be created, touched, or deleted. Relationships are live instances of relations between objects. Because the relationships stored in the system can change at runtime, this is a powerful primitive for dynamically granting or revoking access to the resources you've modeled. When applications modify or create rows in their database, they will also typically create or update relationships.

info

Writing relationships returns a ZedToken which is critical to ensuring performance and consistency.

In the following example, we'll be creating two relationships: one making Emilia a writer of the first post and another making Beatrice a reader of the first post. You can also touch and delete relationships, but those are not as immediately useful for an empty permission system.


zed relationship create blog/post:1 writer blog/user:emilia
zed relationship create blog/post:1 reader blog/user:beatrice

Checking Permissions

Permissions Systems that have stored relationships are capable of performing permission checks. Checks do not only test for the existence of direct relationships, but will also compute and traverse transitive relationships. For example, in our example schema, writers have both write and read, so there's no need to create a read relationship for a subject that is already a writer.

danger

If you are performing a CheckPermission immediately after a WriteSchema or WriteRelationships call, make sure to supply a ZedToken from the WriteRelationships response or set for full consistency. Otherwise, the CheckPermission executing will likely use the cached schema/relationships, and not return what you expect!

The following examples demonstrate exactly that:


zed permission check blog/post:1 read blog/user:emilia --revision "zedtokenfromwriterel" # true
zed permission check blog/post:1 write blog/user:emilia --revision "zedtokenfromwriterel" # true
zed permission check blog/post:1 read blog/user:beatrice --revision "zedtokenfromwriterel" # true
zed permission check blog/post:1 write blog/user:beatrice --revision "zedtokenfromwriterel" # false
note

In addition to checking permissions, it is also possible to perform checks on relations to determine membership.

This goes against the best practice because computing permissions are far more flexible, but can be useful when used with discretion.