Fauna is Secure by Default

date
Jan 4, 2021
published
slug
fauna-is-secure-by-default
description
One of the shocking good things I have learned about Fauna DB is how it approaches security. It’s amazing how roles work to grant access and automatically filter user data.
I have used Fauna DB before, but never with authentication. I had only been recording analytics data and nothing more. With this new project, I wanted authentication and user accounts. I created a user collection in FQL.
CreateCollection({name: 'Users'}) 
I even added data associated with the logged in email address.
Create(Collection('users'), {
  data: {
    email: 'jane@example.com',
	}
});
I was then able to authenticate against this user without any issue and get a user secret token. This is a special token that should be used for actions that user makes against the database. The theory is with this token, the user can only access data they have access too.
The first thing I wanted them to do, was assign a firstName and lastName to the record.
Update(Match(Index('users_by_email', 'name@example.com')), {
  data: {
		firstName: 'Jane',
    lastName: 'Doe',  
	}
})
And I get a access denied error. Here it turns out the server key has access to all the collections, index, and documents. But the user does not have access and requires that we setup permissions first. This required another piece of FQL.
CreateRole({
  name: "user",
  membership: {
    resource: Collection("users"),
  },
  privileges: [
    {
			resource: Collection("users"), 
			actions: {
				read: true, 
				write: Query(
          Lambda("ref",
            Equals(
              CurrentIdentity(),
              Var("ref")
            )
          )
        )
			}
		}
  ]
})
Basically what we did was create the role of user that documents within the users collection belong too. Then we gave all those users the ability to read any user, and the ability to only write when their CurrentIdentity matches the reference to the record.
In other words CurrentIdentity() returns a ref to the authenticated users document. The Var(“ref”) retrieves the reference from the lambda which is this document. It them compares those using the Equals method.
So this seems complicated, and there are easier permissions models. But these CreateRole method that works with ABAC (attribute based access controls) is really powerful.
Because now the user can only read documents they have access too. So you can use this to filter out results that belong to a organization, a user, or a team. And all that logic was removed from your application and move to the database.
If we were instead to setup the role with a Query on the read action, we could control what the user could see when we select this data with there key later. In this case we made sure the user can only read there own document, and no other users.
CreateRole({
  name: "user",
  membership: {
    resource: Collection("users"),
  },
  privileges: [
    {
			resource: Collection("users"), 
			actions: {
				read: Query(
          Lambda("ref",
            Equals(
              CurrentIdentity(),
              Var("ref")
            )
          )
        ),
				write: Query(
          Lambda("ref",
            Equals(
              CurrentIdentity(),
              Var("ref")
            )
          )
        ),
			}
		}
  ]
})
Now that the role is created, I can do queries like this where I don’t have to specify the user, or filter out the results to only include them.
Paginate(
	Documents(Collection('users')),
	{ size: 3 }
)
This separation is brilliant, because now a senior developer, or a database admin can setup the roles. Any developer building the apps can’t accidentally expose data that should not be exposed because of bugs within the application logic. It keeps those access controls closer to the account and the data store. This is really unique and powerful. It simplifies queries, but it does put a larger upfront mental cost to learn FQL and build correct ABAC roles.
There is real time savings here. Less bugs in the application where you have to write access. This gives me a lot more confidence moving forward with projects that have authentication.