Create a sign-in function

This tutorial shows how you can use Fauna’s credentials feature to build a user registration and login system.

The examples in this tutorial use the Fauna Dashboard and Fauna’s demo data.

Setup

Typically, applications use an email and password to sign up new users. Each user is identified by a unique email address.

Update the Demo database to support this setup for customers.

  1. In the Dashboard, open the Demo database.

  2. In the Dashboard, add the following to the Customer collection’s schema:

    collection Customer {
      unique [.email]
    
      index byEmail {
        terms [.email]
      }
    
      ...
    }

The change adds a unique constraint to the email field. Each customer must have a unique email address.

The change also adds a byEmail index. You can use the index to fetch customers based on their email.

Create a sign-up function

In the Dashboard, add the following userSignup() function:

@role(server)
function userSignup(email, password) {
  let customer = Customer.create({
    email: email
  })
  Credential.create({
    document: customer,
    password: password
  })
}

The function registers new customers. It accepts a customer email and password. It uses the email to create an identity document in the Customer collection.

It then uses the password to create a credential for the document.

The function runs using the built-in server role.

Create a sign-in function

Add the following userLogin() function:

@role(server)
function userLogin(email, password) {
  let customer = Customer.byEmail(email)!.first()
  Credential.byDocument(customer)
    ?.login(password, Time.now().add(3600, "second"))
}

The function lets registered users sign in. It accepts a customer email and password. It uses the email and the byEmail index to find the customer.

It then uses the Credential.byDocument() and login() methods to create a temporary access token that’s tied to the customer.

The client application can use this token to access Fauna data on the customer’s behalf. The token automatically expires after 60 minutes (3600 seconds).

The access token has the roles assigned to the Customer collection. For the Demo database, the Customer collection’s privileges are defined in the customer role:

role customer {
  membership Customer

  privileges Store {
    read
  }

  privileges Product {
    read
  }

  privileges Order {
    read {
      predicate (ref => Query.identity() == ref.customer)
    }
  }

  ...
}

The userLogin() function runs using the built-in server role.

Create a role for unauthenticated users

Add the following unauthUser role:

role unauthUser {

  privileges userSignup {
    call
  }

  privileges userLogin {
    call
  }
}

To limit access, the unauthUser role only has call privileges for the userSignup() and userLogin() functions. A client application can use this as the default Fauna API key until a user logs in to generate a temporary access token.

Test the functions

Next, run FQL queries that use the UDFs and mimic the login workflow of a client application.

  1. In the Fauna Shell for the Demo database, create an API key for the unauthUser role:

    Key.create({ role: "unauthUser" })

    Copy the key in the response’s secret property:

    {
      id: "394634072325881920",
      coll: Key,
      ts: Time("2099-04-08T21:20:29.185Z"),
      role: "unauthUser",
      secret: "..."
    }
  2. In the Shell, select Secret and paste in the key.

    This lets you run queries using the unauthUser role’s privileges.

  3. Run the following query to mimic the registration of a new user:

    userSignup("jane.doe@example.com", "password123")

    The query returns a credential, but the current API key doesn’t have permission to view it. This is expected.

    Credential("394635664113532992") /* permission denied */
  4. Run the following query to mimic the login of the user:

    userLogin("jane.doe@example.com", "password123")

    The response’s secret property contains an access token for the user.

    {
      id: "394634552573689922",
      coll: Token,
      ts: Time("2099-04-08T21:28:07.205Z"),
      ttl: Time("2099-04-08T22:28:07.046023Z"),
      document: Customer("394634467388424258"),
      secret: "..."
    }
  5. Copy and paste the key into the Shell’s Secret.

  6. Run the following query to access Product documents on the user’s behalf:

    Product.byName("limes")

    The query returns a set of matching Product documents:

    {
      data: [
        {
          id: "394634876725231680",
          coll: Product,
          ts: Time("2099-04-08T21:33:16.430Z"),
          name: "limes",
          description: "Conventional, 1 ct",
          ...
        },
        ...
      ]
    }
  7. Run the following query to check access to the Manager collection:

    Manager.all()

    The query returns an empty set:

    {
      data: []
    }

    The user’s token doesn’t grant read access to the Manager collection.

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!