AuthZed Product Documentation
Concepts
Restricted API Access

Restricted API Access

Restricted API Access is functionality exclusive to AuthZed products that restricts access to SpiceDB for API Tokens.

This functionality enables organizations to apply the principle of least-privilege to services accessing SpiceDB. For example, read-only tokens can be created for services that should never need to write to SpiceDB.

Those familiar with configuring IAM on the major cloud providers should feel comfortable with the basic concepts:

  • Service Accounts
  • Tokens
  • Roles
  • Policies

Components

Service Accounts

Service Accounts represent your unique workloads. We recommend creating a Service Account for each application that will access the SpiceDB API.

By default Service Accounts have no access to the SpiceDB API; you must apply a Role to gain access.

Tokens

Tokens are long-lived credentials for Service Accounts. SpiceDB clients must provide a Token in the Authorization header of an API request to perform actions granted to the Service Account.

Service Accounts can have an arbitrary number of Tokens.

We recommend deploying new Tokens before deprovisioning any old Tokens to avoid downtime.

Token Format

⚠️
The entire contents of a Token is considered secret.

Tokens come in the form of {prefix}_{key}.

Here's what an example Restricted API Access Token looks like.

sdbst_h256_thisisnotaverysecuresecret

This is what you should forward in your API calls to your AuthZed Dedicated cluster.

If you are using [static configuration] in your own SpiceDB Enterprise deployment, you'd need to generate a token hash to be included in your configuration YAML. Please note you should hash the cleartext secret without the prefix. You can generate the hash of a secret as follows:

echo -n thisisnotaverysecuresecret | sha256sum

The command should output the hash, which can be referenced in your static configuration

71c73ba92f2032416b18a4f4fffb2a825755bea6a8430f2622ab1f3fb35a10d0

Roles

Roles define rules for accessing the SpiceDB API. Roles are bound to Service Accounts to apply those rules to all API Tokens representing the Service Account.

Each rule is composed of an SpiceDB API method (e.g. CheckPermissions, WriteRelationships) and an optional CEL expression (opens in a new tab). Rules are evaluated at request-time and CEL expressions are provided the request payload in order to dynamically evaluate each request.

Any public SpiceDB API type is available to the CEL expression so that you can traverse any type and its fields using language operators. For more details on CEL's language definition, refer to CEL language specification (opens in a new tab).

The following variables are provided the CEL expression varying based on the request method:

  • WriteRelationshipsRequest
  • ReadRelationshipsRequest
  • DeleteRelationshipsRequest
  • WriteSchemaRequest
  • ReadSchemaRequest
  • CheckPermissionRequest
  • LookupResourcesRequest
  • LookupSubjectsRequest
  • ExpandPermissionTreeRequest
  • WatchRequest

Policies

Policies are what bind Roles to a Service Account.

Each policy is composed of a unique identifer for the policy itself, the principal (the target of the role assignment), and any roles being assigned.

Task-Specific Configuration

zed backup/zed restore

To configure a service account for use with zed backup and zed restore, you'll need the following APIs:

On a Service Account on the source PS:

## For backup
# Exporting relationships
authzed.api/ExportBulkRelationships
authzed.api/BulkExportRelationships
 
# Dumping existing schema
authzed.api/ReadSchema

On a Service Account on the destination PS:

## For restore
## Put these on the DESTINATION PS
# Importing relationships
authzed.api/ImportBulkRelationships
authzed.api/BulkImportRelationships
 
# Retrying failed relationships
authzed.api/WriteRelationships
 
# Writing new schema
authzed.api/WriteSchema

Example Rule CEL Expressions

These are some examples of CEL expressions that you might attach to Permissions on a Role.

Resource-type Write Limit

This CEL expression disables the ability for writes to occur on anything but the provided resource type.

This is useful for limiting an application to only be able to perform writes to SpiceDB for the type objects that it owns.

WriteRelationshipsRequest.updates.all(x, x.relationship.resource.object_type == "resource")

Subject-type Write Limit

This CEL expression disables the ability for writes to occur on anything but the provided subject type.

WriteRelationshipsRequest.updates.all(x, x.relationship.subject.object.object_type == "user")

Create-only Write Limit

This CEL expression disables the ability for writes to perform updates; they can only create new relationships.

WriteRelationshipsRequest.updates.all(
  x,
  x.operation == authzed.api.v1.RelationshipUpdate.Operation.OPERATION_CREATE,
)

Resource-type Read Limit

This CEL expression limits the ReadRelationships API from being able to list anything but the a specific resource type.

ReadRelationshipsRequest.relationship_filter.resource_type == "resource"

Blocking Schema Writes

This CEL expression prevents any schema writes that contain the substring "blockchain". This example could be extended to prevent PII or undesirable patterns from reaching a production schema.

!WriteSchemaRequest.schema.contains("blockchain")

Limit Checks to one Permission

This CEL expression limits CheckPermissions requests to only be able to check a particular permission.

CheckPermissionRequest.permission == "admin"

Configuration

The process for setting up this feature varies depending on the AuthZed product you're using.

Dedicated & Cloud

Using the web dashboard, navigate to the Permission System's "Access" tab.

Self-Hosted

Use the following command-line flags:

FlagDescriptionDefault
--extender-authzed-fgam-endpointdefines the external SpiceDB endpoint used to authorize operations for the authzed-fgam extender. If a file:// endpoint is provided, server is run embedded with static configuration
--extender-authzed-fgam-preshared-keydefines the external SpiceDB preshared key used to authorize operations for the authzed-fgam extender. Ignored if endpoint is local (file://)
--extender-enabledmust be set to authzed-fgam

If you set --extender-authzed-fgam-endpoint to a file, it must be a YAML configuration file.

⚠️

This configuration file should be treated like a secret because it contains token hashes.

Here's an example showcasing the structure of static configuration:

role:
  - id: "admin"
    permission:
      authzed.v1/ReadSchema:           ""
      authzed.v1/WriteSchema:          ""
      authzed.v1/ReadRelationships:    ""
      authzed.v1/WriteRelationships:   ""
      authzed.v1/DeleteRelationships:  ""
      authzed.v1/CheckPermission:      ""
      authzed.v1/LookupResources:      ""
      authzed.v1/LookupSubjects:       ""
      authzed.v1/ExpandPermissionTree: ""
      authzed.v1/Watch:                ""
service_account:
  - id: "my_microservice"
    token:
      - id: "token_01"
        hash: "71c73ba92f2032416b18a4f4fffb2a825755bea6a8430f2622ab1f3fb35a10d0"
      - id: "token_02"
        hash: "fcdfc4fa3c5c7381789d90c3c67f6cebf151cbf7e7555e91e77be2aa3e0a4bdf"
policy:
  - id: "microservice_with_admin"
    principal_id: "my_microservice"
    principal_type: "service_account"
    roles:
      - "admin"

Enabling without downtime

If you want to apply a configuration to an existing SpiceDB cluster without downtime, you must conduct an upgrade process with the following steps:

  1. Create pre-shared keys that follow the Restricted Access Token format for each client of your SpiceDB instance. Using some Bash:

    # Generate your secret (substitute your preferred method for generating a cryptographically-secure random string here)
    # This will be a part of the token
    SECRET="$(base64 < /dev/random | head -c64)"; echo "$SECRET"
    # g2l2/YjC3jFg6FdV080qiqBPvCrlLuc9GcHutgHF4WhVjsg7+AvlqLmoCrJEC68t
     
    # Hash that secret using sha256sum
    # This will go in your FGAM configuration as the token hash
    # NOTE: truncate the trailing spaces and "-". You just want the alphanum characters.
    HASH="$(echo -n "$SECRET" | sha256sum | cut -d" " -f1)"; echo "$HASH"
    # 1d619ac2f5013845c5f2df93add92fc87e88ca6c57d19a77d1b189663f1ff5b0
     
    # Add the prefix "sdbst_h256_" to create the token that you'll supply to your client
    printf "token: sdbst_h256_%s\nhash: %s\n" "$SECRET" "$HASH"
    # token: sdbst_h256_g2l2/YjC3jFg6FdV080qiqBPvCrlLuc9GcHutgHF4WhVjsg7+AvlqLmoCrJEC68t
    # hash: 1d619ac2f5013845c5f2df93add92fc87e88ca6c57d19a77d1b189663f1ff5b0
  2. Prepare the FGAM configuration YAML. You'll add the hashes that you generated in the previous step to the hash key in the token list for each respective token. This process heavily depends on what each client needs:

    1. You may want to start with FGAM tokens bound to a admin-like Role, since that's what the original PSKs effectively were. This is probably lower risk, and then from there you can move to start trimming down permissions.
    2. Or you may want to move directly to downscoped tokens for your individual services, creating the tokens you need. This may be simple if you have few clients, but more complex as the number of clients grow, and with a bigger blast radious of impact on rollout. A minimal configuration would look something like:
    role:
      - id: "admin"
        permission:
          authzed.v1/CheckPermission: ""
    service_account:
      - id: "my_microservice"
        token:
          - id: "token_01"
            hash: "1d619ac2f5013845c5f2df93add92fc87e88ca6c57d19a77d1b189663f1ff5b0"
    policy:
      - id: "microservice_with_admin"
        principal_id: "my_microservice"
        principal_type: "service_account"
        roles:
          - "admin"
  3. Set the created tokens as valid preshared keys in your SpiceDB instance. You can do this by defining multiple PSKs via the ENV or flags as comma separated values:

    spicedb serve --grpc-preshared-key="<old_preshared_key>,<new_token_1>,...,<new_token_n>"

    Deploy SpiceDB with this new configuration.

  4. Update all your clients to use the new tokens that you've created, according to which token should have which permissions.

  5. Deploy SpiceDB with the new Restricted Access configuration.

Prior to the migration, the keys that your client sends will be treated as preshared keys. After the migration, the keys that your client sends will be treated as Restricted Access keys.

© 2025 AuthZed.