TOPICS
LANGUAGE

Security Reference

This page covers both administrative security and application security.

FaunaDB Key System

Access to the FaunaDB API uses secrets that correspond to access keys, which authenticate connections as having particular permissions. The access key system applies both to administrator- and server-level connections, as well as to object- and user-level connections.

Keys are defined as instances of the keys class. Like databases, keys exist within the system-global root database context.

Keys are tied to a specific database and allow access to its contents. The level of access a key provides depends on its role.

You must copy the key’s secret out of the key when it is first created and store it securely. Only a BCrypt hash of the key is stored on disk. It is impossible to recover the key’s secret if it is discarded.

Field Name Value Type Description
database Database ref The database associated with this key.
role String The key’s role. Either admin, server, server-readonly, or client.
secret String The key’s authentication secret. Only present on creation.
hashed_secret String The key’s hashed authentication secret.
priority Number A priority between 1 and 500, inclusive. Defaults to 1.
data Object A JSON object. Optional.

Access Keys

Keys belong to one of four roles: either admin, server, server-readonly, or client.

Admin Keys

Keys with the admin role are used for managing databases and keys. An admin key can be used to create and destroy databases and keys. They should be very well protected.

Admin keys for Cloud accounts are created in the cloud keys interface. For Enterprise installments the configured root key is used.

Server Keys

Keys with the server role bypass all permissions checks within the database they’re assigned to. Because they provide unrestricted access, they should be well protected and only used in trusted or server-side environments.

Server Read-Only Keys

Keys with the server-readonly role allow read-only access to all data within the database they’re assigned to. Because they provide unrestricted read access, they should be well protected and only used in trusted or server-side environments.

Client Keys

Keys with the client role are restricted to actions and resources that are specifically marked with the public permission. Because their access is controlled, they are suitable for embedding in untrusted environments, such as mobile clients.

Typically they are used as part of an application’s user authentication flow, or to access public data, such as an application’s logged-out view.

Token Access

Tokens allow direct access to FaunaDB by application users. Their use is optional. Each token has an associated instance. Requests made with a token apply resource-level permissions checks based on its instance.

Both server and client keys can generate tokens. Token generation with a client key requires a password. The password is optional when using a server key, but will be verified if provided.

Permissions

FaunaDB has a built-in permissions system that allows for fine-grained control over access to data.

Access is controlled by a resource’s permissions object. The identity of a request’s key is checked against the value of the permission field corresponding to the resource and action being taken.

Permission fields may be set to one of the following values:

Value Access Allowed
empty Only server keys are allowed.
“public” Any key is allowed.
instance ref Only tokens belonging to the specified instance are allowed.
class ref Only tokens belonging to instances in the specified class are allowed.

Class Permissions

Creating, reading, and modifying an instance in a class is controlled by the class’s permissions. Applicable fields on a class are:

Permission Field Action Allowed
create Creating an instance in the class.
read Reading instances in the class.
write Writing to instances in the class.

An instance also has permissions, which are applied in addition to permissions defined on its class. The permissions object on an instance may contain these fields:

Permission Field Action Allowed
read Reading this instance.
write Writing to this instance.

User-Defined Function Permissions

Calling a function is controlled by its permissions. Applicable fields are:

Permission Field Action Allowed
call Calling the function.

Index Permissions

Query access to an index is controlled by its permissions. Applicable fields are:

Permission Field Action Allowed
read Querying the index.

Delegates

An instance may delegate access on its behalf to other instances by adding the other instances’ refs to its delegates list. Any tokens belonging to a member of delegates will be granted access as though they were tokens belonging to the delegating instance.

For example, if classes/users/1 has read access to classes/spells/1 but classes/users/2 does not, classes/users/1 may grant access to classes/users/2 with the following query:


curl https://db.fauna.com/ \
    -d '{
          "update": { "ref": { "class": "users" }, "id": 1 },
          "params": {
            "object": {
              "delegates": [ { "ref": { "class": "users" }, "id": 2 } ]
            }
          }
        }'

client.Query(
  Update(
    Ref(Class("users"), 1),
    Obj("delegates", Arr(Ref(Class("users"), 2)))));

client.query(
  Update(
    Ref(Class(Value("users")), Value(1)),
    Obj("delegates", Arr(Ref(Class(Value("users")), Value(2))))));

result, _ := client.Query(
    f.Update(
        f.RefClass(f.Class("users"), "1"),
        f.Obj{"delegates": f.Arr{f.RefClass(f.Class("users"), "2")}},
    ),
)

fmt.Println(result)

client.query(
  Update(
    Ref(Class("users"), 1),
    Obj("delegates" -> Arr(Ref(Class("users"), 2)))))

client.query(
  q.update(
    Ref(q.class_expr("users"), 1),
    {"delegates": [Ref(q.class_expr("users"), 2)]}
  ))

$client.query do
  update ref(class_('users'), 1),
         delegates: [ref(class_('users'), 2)]
end

client.query(
    Update(
        ref: Ref(class: Class("users"), id: 1),
        to: Obj(
            "delegates" => Arr(Ref(class: Class("users"), id: 2))
        )
    )
)

client.query(
  q.Update(
    q.Ref(q.Class("users"), 1),
    { delegates: [q.Ref(q.Class("users"), 2)] }))
.then((ret) => console.log(ret))

=> map[ref:{1 0xc420118d80 <nil>} ts:1527631398281881 delegates:[{2 0xc420119000 <nil>}]]

=> { ref: Ref(id=1, class=Ref(id=users, class=Ref(id=classes))),
  ts: 1527631398281881,
  delegates: [ Ref(id=2, class=Ref(id=users, class=Ref(id=classes))) ] }

Now, when classes/users/2 attempts to read classes/spells/1, they will be granted the same level of access as classes/users/1.

Delegates are not transitive–in the example above, classes/users/2 may not delegate classes/users/1’s permissions to another user.