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
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.
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 define claims about a request to be considered during authorization.
Operations are datalog facts that define what action is being performed in a request. They are defined as follows:
$operation must be a string and match one of the below:
- SQL operations:
- DDL: for operating on database objects & schema (e.g. table, view)
- DML: for performing data manipulation
- DQL: for performing queries
- DDL: for operating on database objects & schema (e.g. table, view)
- 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.
Resources are datalog facts that define what is being accessed in a request. They are defined as follows:
$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
- 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.
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:
$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
Users are datalog facts that define what user is submitting a request. They are defined as follows:
$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 are not recommended in production for security reasons.
*) 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:
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:
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 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");
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 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.
Updated 3 months ago