About Biscuit Authorization

Fine-tuned permissions in a decentralized network.

Background

Biscuits are a special bearer token used to authorize access to resources (e.g. tables) and are associated with a public/private key pair. Public keys are provided when the user creates a resource (such as a table) and are used to validate biscuit signatures during authorization for requests against that resource. Users create and attenuate their own biscuits (using the corresponding private key) without the Space and Time platform holding your secrets— full self-custody of your tables! A biscuit must contain, at minimum, a single capability (i.e. security configuration, such as "I am allowed to read from this table") specifying what operation is allowed on which resource

Public Key Cryptography

Biscuit authorization is based on public key cryptography, meaning each biscuit is associated with a public key and private key. The private key is used to sign the biscuit during creation, and the public key is used to validate the biscuit. When creating a table, you must specify a public key. It will be used to validate signatures on all biscuits provided during request authorization thereafter, and the public key cannot be changed. Make sure to keep your private key safe, as anyone with access can generate biscuits with any level of authority.

🚧

Biscuit public/private keys are independent of user public/private keys. Do not use the keypair you use for authentication for resource authorization.

Biscuit Standards

This section outlines the biscuit standards supported by Space and Time.

📘

We are working on adding new standards to enable even more authorization support. Check back soon!

Facts

Facts define claims about a request to be considered during authorization.

Operation

Operations are datalog facts that define what action is being performed in a request. They are defined as follows:

sxt:operation($operation);

The $operation must be a string and match one of the below:

  • SQL operations:
    • DDL: for operating on database objects & schema (e.g. table, view)
      • ddl_create : SQL CREATE command
      • ddl_alter : SQL ALTER command
      • ddl_drop : SQL DROP command
    • DML: for performing data manipulation
      • dml_insert : SQL INSERT command
      • dml_update : SQL UPDATE command
      • dml_merge : DQL MERGE command
      • dml_delete : SQL DELETE command
    • DQL: for performing queries
      • dql_select : SQL SELECT command
  • Kafka ICM operations:
    • kafka_icm_create : ICM create
    • kafka_icm_read : ICM read
    • kafka_icm_update : ICM update
    • kafka_icm_delete : ICM delete

📘

Operation facts should not be specified in your biscuit - they are defined for reference purposes only. The authorization engine will reject biscuits with operation facts.

Resource

Resources are datalog facts that define what is being accessed in a request. They are defined as follows:

sxt:resource($resource);

The $resource must be a lowercase string and match a valid, existing resource.

  • SQL resources: It must include the full resource identifier (i.e., it must include both the namespace and resource name). As an example, the Transactions table in the ETH namespace would be eth.transactions.
  • Kafka ICM resources:
    • kafka_icm_infrastructure_group : Kafka infrastructure groups
    • kafka_icm_user : Kafka users
    • kafka_icm_acl : Kafka ACLs
    • kafka_icm_topic : Kafka topics
    • kafka_icm_topic_mapping_config : Kafka topic mapping configuration

📘

Resource facts should not be specified in your biscuit - they are defined for reference purposes only. The authorization engine will reject biscuits with resource facts.

Capability

Capabilities are datalog facts that define what operation is allowed on a given resource, enabling easy specification of a operation-resource matrix. They are defined as follows:

sxt:capability($operation, $resource);

The $operation must conform to Operation requirements, and the $resource must conform to Resource requirements.

📘

A basic biscuit needs at least one capability. Your biscuit payload should primarily contain capability facts

User

Users are datalog facts that define what user is submitting a request. They are defined as follows:

sxt:user($userId);

The $userId must be a valid platform user identifier.

📘

User facts should not be specified in your biscuit - they are defined for reference purposes only. The authorization engine will reject biscuits with user facts.

Wildcards

🚧

Wildcards are not recommended in production for security reasons.

Wildcards (*) represent any in the context of biscuit facts. They can be used as a replacement for explicit definitions of both Operation and Resource values in Capability facts. They ultimately serve as a means to bypass strict access control definitions to allow for more flexible use of biscuits.

As an example, you might want to create a biscuit authorizing access to all operations on a given resource (e.g., the TEST.CUSTOMER table). You would define a wildcard operation capability like this:

sxt:capability("*", "test.customer");

In another example, you might not know all tables you'll create with the same public key, but you're developing an application that should be able to query all of them. You would define a wildcard resource capability like this:

sxt:capability("dql_select", "*");

A word on wildcards and security

Note that wildcards can be dangerous from a security perspective. Since biscuits are bearer tokens, anyone who has access to the token (i.e., bears it) can perform anything that the token authorizes. For this reason, we do not recommend using wildcards in a production application. Instead, wildcards are great for fast development of applications when security considerations matter less. After building your functionality, we HIGHLY recommend that you go back and regenerate biscuits with explicit capabilities defined to limit yourself to security risks.

Checks

Checks are a flexible datalog terms that enable simple validation of datalog facts. If a check fails, the request will be rejected by the authorizer. For all of the above facts, you can define a check to limit how a biscuit can be used.

For example, if you want to create a biscuit authorizing read-only access to your table (e.g., the TEST.CUSTOMER table), you can also add a check to ensure that your user (e.g., the john_doe user) is the only one for whom the biscuit authorization can succeed. Simply define the following payload:

sxt:capability("dql_select", "test.customer");
check if sxt:user("john_doe");

Working with Biscuits

Biscuit manipulation (creating, testing, attenuating, etc.) does not involve the platform, meaning you can create biscuits however you want, whenever you want. Please refer to the biscuit cli guide for more details on how to operate on biscuits on your local machine. The only requirement to create a biscuit is to have access to the appropriate private key. If you use the wrong private key, your biscuit signature won't be valid for the desired table.

Let's say you just created a table called TEST.CUSTOMER and want to give yourself read-write access. You would specify the following payload in your biscuit:

sxt:capability("dql_select", "test.customer");
sxt:capability("dml_insert", "test.customer");
sxt:capability("dml_update", "test.customer");
sxt:capability("dml_delete", "test.customer");

If you later decide you want to give your friend access to query your table, you have two options. You can either generate a new biscuit with only the dql_select capability, or you can attenuate your existing biscuit (adding a user check as defined above). Note that biscuits can never grow in scope, they can only become more restrictive (i.e., attenuated). So if you share a biscuit with someone else, they can never add new capabilities!

Namespacing

Namespacing is now supported across the platform! Note that the above documentation now includes the sxt: prefix. If you've already generated biscuits, don't worry. We're keeping backwards compatibility with the old datalog (i.e., you can still write capability("dql_select", "test.customer")), but note that this old style is now considered deprecated and will be removed in a future release. Please plan on migrating your biscuits to use the new namespace prefix.