Documentation

TOPICS
LANGUAGE

User Authentication Tutorial

Introduction

FaunaDB offers built-in identity, authentication and password management. This tutorial will walk through how to create user identities, authenticate them, and manage their sessions.

Getting Started

If you’ve been following along since the beginning you can skip to Setting up the Schema.

If you have not done the previous tutorials, you will need an admin API key. Create an admin key using the admin keys interface for your FaunaDB Cloud account, or if you are using FaunaDB Enterprise, use the configured root key for your cluster.

Create a Database


curl https://db.fauna.com/ \
    -u fnACrVRyZhACAOvXOTRzOpg1Qqq5oPHKMfOLgcHQ: \
    -d '{ "create_database": { "object": { "name": "my_app" } } }'

client.Query(CreateDatabase(Obj("name", "my_app")));

System.out.println(
      client.query(CreateDatabase(Obj("name", Value("my_app")))
   ).get());

result, _ := client.Query(f.CreateDatabase(f.Obj{"name": "my_app"}))

fmt.Println(result)

client.query(CreateDatabase(Obj("name" -> "my_app")))

client.query(q.create_database({"name": "my_app"}))

$client.query do
  create_database name: 'my_app'
end

client.query(CreateDatabase(Obj("name" => "my_app")))

client.query(q.CreateDatabase({ name: "my_app" }))
  .then((ret) => console.log(ret))

=> HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "databases/my_app" },
    "class": { "@ref": "databases" },
    "ts": 1520225686389954,
    "name": "my_app"
  }
}

=> {
  "ref": { "@ref": "databases/my_app" },
  "class": { "@ref": "databases" },
  "ts": 1520225686389954,
  "name": "my_app"
}

=> ObjectV({
   ref=RefV(id = "my_app", class = RefV(id = "databases")),
   ts=LongV(1527146032847201),
   name=StringV(my_app)
})

=> map[ref:{my_app 0xc420126220 <nil>} ts:1520225686389954 name:my_app]

=> {
  "ref": { "@ref": "databases/my_app" },
  "class": { "@ref": "databases" },
  "ts": 1520225686389954,
  "name": "my_app"
}

=> {
  "ref": { "@ref": "databases/my_app" },
  "class": { "@ref": "databases" },
  "ts": 1520225686389954,
  "name": "my_app"
}

=> {
  "ref": { "@ref": "databases/my_app" },
  "class": { "@ref": "databases" },
  "ts": 1520225686389954,
  "name": "my_app"
}

=> {
  "ref": { "@ref": "databases/my_app" },
  "class": { "@ref": "databases" },
  "ts": 1520225686389954,
  "name": "my_app"
}

=> { ref: Ref(id=my_app, class=Ref(id=databases)),
  ts: 1528127545620042,
  name: 'my_app' }

If you’ve used the my_app database with any of the previous tutorials this query will return an error since the database already exists.

Getting Access to the Database

Create an initial server key for our database by using an admin key. The server key has unrestricted access to a single database.


curl https://db.fauna.com/ \
    -u fnACrVRyZhACAOvXOTRzOpg1Qqq5oPHKMfOLgcHQ: \
    -d '{
          "create_key": {
            "object": { "database": { "database": "my_app" }, "role": "server" }
          }
        }'

client.Query(
  CreateKey(
    Obj("database", Database("my_app"), "role", "server")));

System.out.println(
   client.query(
      CreateKey(
         Obj(
            "database", Database(Value("my_app")),
            "role", Value("server")
         )
      )
   ).get());

result, _ := client.Query(
    f.CreateKey(
        f.Obj{"database": f.Database("my_app"), "role": "server"},
    ),
)

fmt.Println(result)

client.query(
  CreateKey(
    Obj("database" -> Database("my_app"), "role" -> "server")))

client.query(
  q.create_key(
    {"database": q.database("my_app"), "role": "server"}
  ))

$client.query do
  create_key database: database('my_app'), role: 'server'
end

client.query(
    CreateKey(
        Obj(
            "database" => Database("my_app"),
            "role" => "server"
        )
    )
)

client.query(
  q.CreateKey(
    { database: q.Database("my_app"), role: "server" }))
.then((ret) => console.log(ret))

=> HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "keys/192903209473278464" },
    "class": { "@ref": "keys" },
    "ts": 1520225686419239,
    "database": { "@ref": "databases/my_app" },
    "role": "server",
    "secret": "fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq",
    "hashed_secret": "$2a$05$k9x7YbL5CYY9KIMhW6RaIOILyEwY3IHuX0NWvK9iUMWk8TJ7k1H8O"
  }
}

=> {
  "ref": { "@ref": "keys/192903209473278464" },
  "class": { "@ref": "keys" },
  "ts": 1520225686419239,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq",
  "hashed_secret": "$2a$05$k9x7YbL5CYY9KIMhW6RaIOILyEwY3IHuX0NWvK9iUMWk8TJ7k1H8O"
}

=> ObjectV({
  ref=RefV(id = "200313471592563200", class = RefV(id = "keys")),
  ts=LongV(1527292663072154),
  database=RefV(id = "my_app", class = RefV(id = "databases")),
  role=StringV(server),
  secret=StringV(fnACx6gKhgACAMX-dNu5rCRxSrVhxldegi1Emu-J),
  hashed_secret=StringV($2a$05$BPdyeiNJJdRx2hJicvHNrODT1RHG7xI2Lc5pjnveYWnXggCkGBc7y)
})

=> map[
  ref:{192903209473278464 0xc42011ca60 <nil>}
  ts:1520225686419239
  database:{my_app 0xc42011cc00 <nil>}
  role:server
  secret:fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq
  hashed_secret:$2a$05$k9x7YbL5CYY9KIMhW6RaIOILyEwY3IHuX0NWvK9iUMWk8TJ7k1H8O
]

=> {
  "ref": { "@ref": "keys/192903209473278464" },
  "class": { "@ref": "keys" },
  "ts": 1520225686419239,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq",
  "hashed_secret": "$2a$05$k9x7YbL5CYY9KIMhW6RaIOILyEwY3IHuX0NWvK9iUMWk8TJ7k1H8O"
}

=> {
  "ref": { "@ref": "keys/192903209473278464" },
  "class": { "@ref": "keys" },
  "ts": 1520225686419239,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq",
  "hashed_secret": "$2a$05$k9x7YbL5CYY9KIMhW6RaIOILyEwY3IHuX0NWvK9iUMWk8TJ7k1H8O"
}

=> {
  "ref": { "@ref": "keys/192903209473278464" },
  "class": { "@ref": "keys" },
  "ts": 1520225686419239,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq",
  "hashed_secret": "$2a$05$k9x7YbL5CYY9KIMhW6RaIOILyEwY3IHuX0NWvK9iUMWk8TJ7k1H8O"
}

=> {
  "ref": { "@ref": "keys/192903209473278464" },
  "class": { "@ref": "keys" },
  "ts": 1520225686419239,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq",
  "hashed_secret": "$2a$05$k9x7YbL5CYY9KIMhW6RaIOILyEwY3IHuX0NWvK9iUMWk8TJ7k1H8O"
}

=> { ref: Ref(id=201188951594107392, class=Ref(id=keys)),
  ts: 1528127585813144,
  database: Ref(id=my_app, class=Ref(id=databases)),
  role: 'server',
  secret: 'fnACysRJGIACAHiL_5f0UxHlPFIZgq876ptMNJ72',
  hashed_secret: '$2a$05$wm6Zwl8dhlY3hFbJj0JrHuSt4YszzsDRd9KdxVbxg98yVOMJiEp0i' }

Create a client key that we’ll embed in our public clients. The client key only has access to resources marked with public permissions.


curl https://db.fauna.com/ \
    -u fnACrVRyZhACAOvXOTRzOpg1Qqq5oPHKMfOLgcHQ: \
    -d '{
          "create_key": {
            "object": { "database": { "database": "my_app" }, "role": "client" }
          }
        }'

client.Query(
  CreateKey(
    Obj("database", Database("my_app"), "role", "client")));

client.query(
  CreateKey(
    Obj(
      "database", Database(Value("my_app")),
      "role", Value("client")
    )));

client.Query(
    f.CreateKey(
        f.Obj{"database": f.Database("my_app"), "role": "client"},
    ),
)

client.query(
  CreateKey(
    Obj("database" -> Database("my_app"), "role" -> "client")))

client.query(
  q.create_key(
    {"database": q.database("my_app"), "role": "client"}
  ))

$client.query do
  create_key database: database('my_app'), role: 'client'
end

client.query(
    CreateKey(
        Obj(
            "database" => Database("my_app"),
            "role" => "client"
        )
    )
)

client.query(
  q.CreateKey(
    { database: q.Database("my_app"), role: "client" }));

=> HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "keys/192903223010394624" },
    "class": { "@ref": "keys" },
    "ts": 1520225699329618,
    "database": { "@ref": "databases/my_app" },
    "role": "client",
    "secret": "fnACrVR1k5ACAEDMAXJyHLF4UiVpZBpqBYvpl3UO",
    "hashed_secret": "$2a$05$hPuxA6II2xKhtEouRUEo4ul2OAJeFHDucA27miIlNoL2bfW7RNfge"
  }
}

=> {
  "ref": { "@ref": "keys/192903223010394624" },
  "class": { "@ref": "keys" },
  "ts": 1520225699329618,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "fnACrVR1k5ACAEDMAXJyHLF4UiVpZBpqBYvpl3UO",
  "hashed_secret": "$2a$05$hPuxA6II2xKhtEouRUEo4ul2OAJeFHDucA27miIlNoL2bfW7RNfge"
}

=> {
  "ref": { "@ref": "keys/192903223010394624" },
  "class": { "@ref": "keys" },
  "ts": 1520225699329618,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "fnACrVR1k5ACAEDMAXJyHLF4UiVpZBpqBYvpl3UO",
  "hashed_secret": "$2a$05$hPuxA6II2xKhtEouRUEo4ul2OAJeFHDucA27miIlNoL2bfW7RNfge"
}

=> {
  "ref": { "@ref": "keys/192903223010394624" },
  "class": { "@ref": "keys" },
  "ts": 1520225699329618,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "fnACrVR1k5ACAEDMAXJyHLF4UiVpZBpqBYvpl3UO",
  "hashed_secret": "$2a$05$hPuxA6II2xKhtEouRUEo4ul2OAJeFHDucA27miIlNoL2bfW7RNfge"
}

=> {
  "ref": { "@ref": "keys/192903223010394624" },
  "class": { "@ref": "keys" },
  "ts": 1520225699329618,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "fnACrVR1k5ACAEDMAXJyHLF4UiVpZBpqBYvpl3UO",
  "hashed_secret": "$2a$05$hPuxA6II2xKhtEouRUEo4ul2OAJeFHDucA27miIlNoL2bfW7RNfge"
}

=> {
  "ref": { "@ref": "keys/192903223010394624" },
  "class": { "@ref": "keys" },
  "ts": 1520225699329618,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "fnACrVR1k5ACAEDMAXJyHLF4UiVpZBpqBYvpl3UO",
  "hashed_secret": "$2a$05$hPuxA6II2xKhtEouRUEo4ul2OAJeFHDucA27miIlNoL2bfW7RNfge"
}

=> {
  "ref": { "@ref": "keys/192903223010394624" },
  "class": { "@ref": "keys" },
  "ts": 1520225699329618,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "fnACrVR1k5ACAEDMAXJyHLF4UiVpZBpqBYvpl3UO",
  "hashed_secret": "$2a$05$hPuxA6II2xKhtEouRUEo4ul2OAJeFHDucA27miIlNoL2bfW7RNfge"
}

=> {
  "ref": { "@ref": "keys/192903223010394624" },
  "class": { "@ref": "keys" },
  "ts": 1520225699329618,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "fnACrVR1k5ACAEDMAXJyHLF4UiVpZBpqBYvpl3UO",
  "hashed_secret": "$2a$05$hPuxA6II2xKhtEouRUEo4ul2OAJeFHDucA27miIlNoL2bfW7RNfge"
}

=> {
  "ref": { "@ref": "keys/192903223010394624" },
  "class": { "@ref": "keys" },
  "ts": 1520225699329618,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "fnACrVR1k5ACAEDMAXJyHLF4UiVpZBpqBYvpl3UO",
  "hashed_secret": "$2a$05$hPuxA6II2xKhtEouRUEo4ul2OAJeFHDucA27miIlNoL2bfW7RNfge"
}

Setting up the Schema

We’ll represent our users using the users class. When a new user creates an account, they will be required to provide an email address and password for identification. This operation uses our server key.


curl https://db.fauna.com/ \
    -u fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq: \
    -d '{ "create_class": { "object": { "name": "users" } } }'

client.Query(CreateClass(Obj("name", "users")));

client.query(CreateClass(Obj("name", Value("users"))));

client.Query(f.CreateClass(f.Obj{"name": "users"}))

client.query(CreateClass(Obj("name" -> "users")))

client.query(q.create_class({"name": "users"}))

$client.query do
  create_class name: 'users'
end

client.query(CreateClass(Obj("name" => "users")))

client.query(q.CreateClass({ name: "users" }));

=> HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "classes/users" },
    "class": { "@ref": "classes" },
    "ts": 1520225699365905,
    "history_days": 30,
    "name": "users"
  }
}

=> {
  "ref": { "@ref": "classes/users" },
  "class": { "@ref": "classes" },
  "ts": 1520225699365905,
  "history_days": 30,
  "name": "users"
}

=> {
  "ref": { "@ref": "classes/users" },
  "class": { "@ref": "classes" },
  "ts": 1520225699365905,
  "history_days": 30,
  "name": "users"
}

=> {
  "ref": { "@ref": "classes/users" },
  "class": { "@ref": "classes" },
  "ts": 1520225699365905,
  "history_days": 30,
  "name": "users"
}

=> {
  "ref": { "@ref": "classes/users" },
  "class": { "@ref": "classes" },
  "ts": 1520225699365905,
  "history_days": 30,
  "name": "users"
}

=> {
  "ref": { "@ref": "classes/users" },
  "class": { "@ref": "classes" },
  "ts": 1520225699365905,
  "history_days": 30,
  "name": "users"
}

=> {
  "ref": { "@ref": "classes/users" },
  "class": { "@ref": "classes" },
  "ts": 1520225699365905,
  "history_days": 30,
  "name": "users"
}

=> {
  "ref": { "@ref": "classes/users" },
  "class": { "@ref": "classes" },
  "ts": 1520225699365905,
  "history_days": 30,
  "name": "users"
}

=> {
  "ref": { "@ref": "classes/users" },
  "class": { "@ref": "classes" },
  "ts": 1520225699365905,
  "history_days": 30,
  "name": "users"
}

Next, we create a unique index on the user’s email address. This ensures only one user record exists per email and provides a mechanism for us to find users via their email address. We can also use this index to authenticate users by email.

We set the index’s permissions to be publicly readable. That way we can use the client key that we embedded in our public client to log the user in.


curl https://db.fauna.com/ \
    -u fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq: \
    -d '{
          "create_index": {
            "object": {
              "name": "users_by_email",
              "permissions": { "object": { "read": "public" } },
              "source": { "class": "users" },
              "terms": [ { "object": { "field": [ "data", "email" ] } } ],
              "unique": true
            }
          }
        }'

client.Query(
  CreateIndex(
    Obj(
      "name", "users_by_email",
      "permissions", Obj("read", "public"),
      "source", Class("users"),
      "terms", Arr(Obj("field", Arr("data", "email"))),
      "unique", true
    )));

client.query(
  CreateIndex(
    Obj(
      "name", Value("users_by_email"),
      "permissions", Obj("read", Value("public")),
      "source", Class(Value("users")),
      "terms", Arr(Obj("field", Arr(Value("data"), Value("email")))),
      "unique", Value(true)
    )));

client.Query(
    f.CreateIndex(
        f.Obj{
            "name": "users_by_email",
            "permissions": f.Obj{"read": "public"},
            "source": f.Class("users"),
            "terms": f.Arr{f.Obj{"field": f.Arr{"data", "email"}}},
            "unique": true,
        },
    ),
)

client.query(
  CreateIndex(
    Obj(
      "name" -> "users_by_email",
      "permissions" -> Obj("read" -> "public"),
      "source" -> Class("users"),
      "terms" -> Arr(Obj("field" -> Arr("data", "email"))),
      "unique" -> true
    )))

client.query(
  q.create_index(
    {
      "name": "users_by_email",
      "permissions": {"read": "public"},
      "source": q.class_expr("users"),
      "terms": [{"field": ["data", "email"]}],
      "unique": True
    }
  ))

$client.query do
  create_index name: 'users_by_email',
               permissions: { read: 'public' },
               source: class_('users'),
               terms: [{ field: ['data', 'email'] }],
               unique: true
end

client.query(
    CreateIndex(
        Obj(
            "name" => "users_by_email",
            "permissions" => Obj("read" => "public"),
            "source" => Class("users"),
            "terms" => Arr(Obj("field" => Arr("data", "email"))),
            "unique" => true
        )
    )
)

client.query(
  q.CreateIndex(
    {
      name: "users_by_email",
      permissions: { read: "public" },
      source: q.Class("users"),
      terms: [{ field: ["data", "email"] }],
      unique: true
    }));

=> HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "indexes/users_by_email" },
    "class": { "@ref": "indexes" },
    "ts": 1520225699390477,
    "active": false,
    "partitions": 1,
    "name": "users_by_email",
    "permissions": { "read": "public" },
    "source": { "@ref": "classes/users" },
    "terms": [ { "field": [ "data", "email" ] } ],
    "unique": true
  }
}

=> {
  "ref": { "@ref": "indexes/users_by_email" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699390477,
  "active": false,
  "partitions": 1,
  "name": "users_by_email",
  "permissions": { "read": "public" },
  "source": { "@ref": "classes/users" },
  "terms": [ { "field": [ "data", "email" ] } ],
  "unique": true
}

=> {
  "ref": { "@ref": "indexes/users_by_email" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699390477,
  "active": false,
  "partitions": 1,
  "name": "users_by_email",
  "permissions": { "read": "public" },
  "source": { "@ref": "classes/users" },
  "terms": [ { "field": [ "data", "email" ] } ],
  "unique": true
}

=> {
  "ref": { "@ref": "indexes/users_by_email" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699390477,
  "active": false,
  "partitions": 1,
  "name": "users_by_email",
  "permissions": { "read": "public" },
  "source": { "@ref": "classes/users" },
  "terms": [ { "field": [ "data", "email" ] } ],
  "unique": true
}

=> {
  "ref": { "@ref": "indexes/users_by_email" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699390477,
  "active": false,
  "partitions": 1,
  "name": "users_by_email",
  "permissions": { "read": "public" },
  "source": { "@ref": "classes/users" },
  "terms": [ { "field": [ "data", "email" ] } ],
  "unique": true
}

=> {
  "ref": { "@ref": "indexes/users_by_email" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699390477,
  "active": false,
  "partitions": 1,
  "name": "users_by_email",
  "permissions": { "read": "public" },
  "source": { "@ref": "classes/users" },
  "terms": [ { "field": [ "data", "email" ] } ],
  "unique": true
}

=> {
  "ref": { "@ref": "indexes/users_by_email" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699390477,
  "active": false,
  "partitions": 1,
  "name": "users_by_email",
  "permissions": { "read": "public" },
  "source": { "@ref": "classes/users" },
  "terms": [ { "field": [ "data", "email" ] } ],
  "unique": true
}

=> {
  "ref": { "@ref": "indexes/users_by_email" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699390477,
  "active": false,
  "partitions": 1,
  "name": "users_by_email",
  "permissions": { "read": "public" },
  "source": { "@ref": "classes/users" },
  "terms": [ { "field": [ "data", "email" ] } ],
  "unique": true
}

=> {
  "ref": { "@ref": "indexes/users_by_email" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699390477,
  "active": false,
  "partitions": 1,
  "name": "users_by_email",
  "permissions": { "read": "public" },
  "source": { "@ref": "classes/users" },
  "terms": [ { "field": [ "data", "email" ] } ],
  "unique": true
}

Authenticating Users

Creating Users

When new users sign up, we can create a new user instance with their email address and password attached to it. A hash of the password is stored along with the user instance. Passwords are never stored in plain text. Creating user records is done with the server key.


curl https://db.fauna.com/ \
    -u fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq: \
    -d '{
          "create": { "class": "users" },
          "params": {
            "object": {
              "credentials": { "object": { "password": "secret password" } },
              "data": { "object": { "email": "alice@example.com" } }
            }
          }
        }'

client.Query(
  Create(
    Class("users"),
    Obj(
      "credentials", Obj("password", "secret password"),
      "data", Obj("email", "alice@example.com")
    )));

client.query(
  Create(
    Class(Value("users")),
    Obj(
      "credentials", Obj("password", Value("secret password")),
      "data", Obj("email", Value("alice@example.com"))
    )));

client.Query(
    f.Create(
        f.Class("users"),
        f.Obj{
            "credentials": f.Obj{"password": "secret password"},
            "data": f.Obj{"email": "alice@example.com"},
        },
    ),
)

client.query(
  Create(
    Class("users"),
    Obj(
      "credentials" -> Obj("password" -> "secret password"),
      "data" -> Obj("email" -> "alice@example.com")
    )))

client.query(
  q.create(
    q.class_expr("users"),
    {
      "credentials": {"password": "secret password"},
      "data": {"email": "alice@example.com"}
    }
  ))

$client.query do
  create class_('users'),
         credentials: { password: 'secret password' },
         data: { email: 'alice@example.com' }
end

client.query(
    Create(
        at: Class("users"),
        Obj(
            "credentials" => Obj("password" => "secret password"),
            "data" => Obj("email" => "alice@example.com")
        )
    )
)

client.query(
  q.Create(
    q.Class("users"),
    {
      credentials: { password: "secret password" },
      data: { email: "alice@example.com" }
    }));

=> HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "classes/users/192903223150903808" },
    "class": { "@ref": "classes/users" },
    "ts": 1520225699464870,
    "data": { "email": "alice@example.com" }
  }
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699464870,
  "data": { "email": "alice@example.com" }
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699464870,
  "data": { "email": "alice@example.com" }
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699464870,
  "data": { "email": "alice@example.com" }
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699464870,
  "data": { "email": "alice@example.com" }
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699464870,
  "data": { "email": "alice@example.com" }
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699464870,
  "data": { "email": "alice@example.com" }
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699464870,
  "data": { "email": "alice@example.com" }
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699464870,
  "data": { "email": "alice@example.com" }
}

Logging in Users

Now, if a user provides an email and password, we can generate a token, using the login function, that can be used to access resources.

Here, we find the user using the email index. We then call the login function with the user and password to create a new token.


curl https://db.fauna.com/ \
    -u fnACrVR1k5ACAEDMAXJyHLF4UiVpZBpqBYvpl3UO: \
    -d '{
          "login": {
            "match": { "index": "users_by_email" },
            "terms": "alice@example.com"
          },
          "params": { "object": { "password": "secret password" } }
        }'

client.Query(
  Login(
    Match(Index("users_by_email"), "alice@example.com"),
    Obj("password", "secret password")));

client.query(
  Login(
    Match(
      Index(Value("users_by_email")),
      Value("alice@example.com")),
    Obj("password", Value("secret password"))));

client.Query(
    f.Login(
        f.MatchTerm(
            f.Index("users_by_email"),
            "alice@example.com",
        ),
        f.Obj{"password": "secret password"},
    ),
)

client.query(
  Login(
    Match(Index("users_by_email"), "alice@example.com"),
    Obj("password" -> "secret password")))

client.query(
  q.login(
    q.match(q.index("users_by_email"), "alice@example.com"),
    {"password": "secret password"}
  ))

$client.query do
  login match(index('users_by_email'), 'alice@example.com'),
        password: 'secret password'
end

client.query(
    Login(
        for: Match(
            index: Index("users_by_email"),
            terms: "alice@example.com"
        ),
        Obj("password" => "secret password")
    )
)

client.query(
  q.Login(
    q.Match(q.Index("users_by_email"), "alice@example.com"),
    { password: "secret password" }));

=> HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "tokens/192903223187603968" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699485218,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k"
  }
}

=> {
  "ref": { "@ref": "tokens/192903223187603968" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699485218,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k"
}

=> {
  "ref": { "@ref": "tokens/192903223187603968" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699485218,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k"
}

=> {
  "ref": { "@ref": "tokens/192903223187603968" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699485218,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k"
}

=> {
  "ref": { "@ref": "tokens/192903223187603968" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699485218,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k"
}

=> {
  "ref": { "@ref": "tokens/192903223187603968" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699485218,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k"
}

=> {
  "ref": { "@ref": "tokens/192903223187603968" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699485218,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k"
}

=> {
  "ref": { "@ref": "tokens/192903223187603968" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699485218,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k"
}

=> {
  "ref": { "@ref": "tokens/192903223187603968" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699485218,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k"
}

The provided token can now be used to access resources. If you’re using HTTP requests to interact with the database, you can use the token as a username on the Basic-Auth header for every request made by that specific user.

If you’re using a native client, you should create a new client instance using the user’s token as a secret. Some drivers allow the creation of session clients in which the underlying http connection is shared across them.


curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223187603968" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699485218,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$8Fqa50upvT9gJHUjf6P6h.OuzAXqkmntOOGVV13VZv2HDgHN4fyDu"
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223187603968" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699485218,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$8Fqa50upvT9gJHUjf6P6h.OuzAXqkmntOOGVV13VZv2HDgHN4fyDu"
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223187603968" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699485218,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$8Fqa50upvT9gJHUjf6P6h.OuzAXqkmntOOGVV13VZv2HDgHN4fyDu"
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223187603968" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699485218,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$8Fqa50upvT9gJHUjf6P6h.OuzAXqkmntOOGVV13VZv2HDgHN4fyDu"
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223187603968" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699485218,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$8Fqa50upvT9gJHUjf6P6h.OuzAXqkmntOOGVV13VZv2HDgHN4fyDu"
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223187603968" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699485218,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$8Fqa50upvT9gJHUjf6P6h.OuzAXqkmntOOGVV13VZv2HDgHN4fyDu"
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223187603968" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699485218,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$8Fqa50upvT9gJHUjf6P6h.OuzAXqkmntOOGVV13VZv2HDgHN4fyDu"
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223187603968" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699485218,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$8Fqa50upvT9gJHUjf6P6h.OuzAXqkmntOOGVV13VZv2HDgHN4fyDu"
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223187603968" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699485218,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$8Fqa50upvT9gJHUjf6P6h.OuzAXqkmntOOGVV13VZv2HDgHN4fyDu"
  }
}

Multiple tokens can be created per instance. This allows a user to log in from multiple sources.

Let’s create another token for the user. In this example we use the user’s ref directly.


curl https://db.fauna.com/ \
    -u fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq: \
    -d '{
          "login": { "@ref": "classes/users/192903223150903808" },
          "params": { "object": { "password": "secret password" } }
        }'

client.Query(
  Login(
    Ref("classes/users/192903223150903808"),
    Obj("password", "secret password")));

client.query(
  Login(
    Ref("classes/users/192903223150903808"),
    Obj("password", Value("secret password"))));

client.Query(
    f.Login(
        f.Ref("classes/users/192903223150903808"),
        f.Obj{"password": "secret password"},
    ),
)

client.query(
  Login(
    Ref("classes/users/192903223150903808"),
    Obj("password" -> "secret password")))

client.query(
  q.login(
    Ref("classes/users/192903223150903808"),
    {"password": "secret password"}
  ))

$client.query do
  login ref('classes/users/192903223150903808'),
        password: 'secret password'
end

client.query(
    Login(
        for: Ref("classes/users/192903223150903808"),
        Obj("password" => "secret password")
    )
)

client.query(
  q.Login(
    Ref("classes/users/192903223150903808"),
    { password: "secret password" }));

=> HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "tokens/192903223274635776" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699581555,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54"
  }
}

=> {
  "ref": { "@ref": "tokens/192903223274635776" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699581555,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54"
}

=> {
  "ref": { "@ref": "tokens/192903223274635776" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699581555,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54"
}

=> {
  "ref": { "@ref": "tokens/192903223274635776" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699581555,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54"
}

=> {
  "ref": { "@ref": "tokens/192903223274635776" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699581555,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54"
}

=> {
  "ref": { "@ref": "tokens/192903223274635776" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699581555,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54"
}

=> {
  "ref": { "@ref": "tokens/192903223274635776" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699581555,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54"
}

=> {
  "ref": { "@ref": "tokens/192903223274635776" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699581555,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54"
}

=> {
  "ref": { "@ref": "tokens/192903223274635776" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699581555,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54"
}

Logging out Users

Logging out a token will invalidate it requiring a new token for future access. The logout function takes a single boolean argument: true invalidates all the tokens for the user, false invalidates only the current token.


curl https://db.fauna.com/ \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k: \
    -d '{ "logout": true }'

client.Query(Logout(true));

client.query(Logout(Value(true)));

client.Query(f.Logout(true))

client.query(Logout(true))

client.query(q.logout(True))

$client.query do
  logout true
end

client.query(Logout(all: true))

client.query(q.Logout(true));

=> HTTP/1.1 200 OK
{ "resource": true }

=> true

=> true

=> true

=> true

=> true

=> true

=> true

=> true

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1niACAAKtVHJrQAIAZpQQMziofMwxan0k8rVoWEODd5k:

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1o1ACAAKtVHJrQAIAme-sIiOeieVETYodh_MFzK8Uo54:

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

=> HTTP/1.1 401 Unauthorized
{
  "errors": [ { "code": "unauthorized", "description": "Unauthorized" } ]
}

Changing a User’s Password

Change a user’s password by calling the update or replace function with a new password in the credentials field. Any existing tokens will remain valid: If required, invalidate their previous sessions by calling the logout function with true as its argument.


curl https://db.fauna.com/ \
    -u fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq: \
    -d '{
          "login": { "@ref": "classes/users/192903223150903808" },
          "params": { "object": { "password": "secret password" } }
        }'

client.Query(
  Login(
    Ref("classes/users/192903223150903808"),
    Obj("password", "secret password")));

client.query(
  Login(
    Ref("classes/users/192903223150903808"),
    Obj("password", Value("secret password"))));

client.Query(
    f.Login(
        f.Ref("classes/users/192903223150903808"),
        f.Obj{"password": "secret password"},
    ),
)

client.query(
  Login(
    Ref("classes/users/192903223150903808"),
    Obj("password" -> "secret password")))

client.query(
  q.login(
    Ref("classes/users/192903223150903808"),
    {"password": "secret password"}
  ))

$client.query do
  login ref('classes/users/192903223150903808'),
        password: 'secret password'
end

client.query(
    Login(
        for: Ref("classes/users/192903223150903808"),
        Obj("password" => "secret password")
    )
)

client.query(
  q.Login(
    Ref("classes/users/192903223150903808"),
    { password: "secret password" }));

=> HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "tokens/192903223338598912" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699642035,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI"
  }
}

=> {
  "ref": { "@ref": "tokens/192903223338598912" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699642035,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI"
}

=> {
  "ref": { "@ref": "tokens/192903223338598912" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699642035,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI"
}

=> {
  "ref": { "@ref": "tokens/192903223338598912" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699642035,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI"
}

=> {
  "ref": { "@ref": "tokens/192903223338598912" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699642035,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI"
}

=> {
  "ref": { "@ref": "tokens/192903223338598912" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699642035,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI"
}

=> {
  "ref": { "@ref": "tokens/192903223338598912" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699642035,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI"
}

=> {
  "ref": { "@ref": "tokens/192903223338598912" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699642035,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI"
}

=> {
  "ref": { "@ref": "tokens/192903223338598912" },
  "class": { "@ref": "tokens" },
  "ts": 1520225699642035,
  "instance": { "@ref": "classes/users/192903223150903808" },
  "secret": "fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI"
}

Now a query authenticated with the user’s token can change the password.


curl https://db.fauna.com/ \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI: \
    -d '{
          "replace": { "@ref": "classes/users/192903223150903808" },
          "params": {
            "object": {
              "credentials": { "object": { "password": "new password" } }
            }
          }
        }'

client.Query(
  Replace(
    Ref("classes/users/192903223150903808"),
    Obj("credentials", Obj("password", "new password"))));

client.query(
  Replace(
    Ref("classes/users/192903223150903808"),
    Obj("credentials", Obj("password", Value("new password")))));

client.Query(
    f.Replace(
        f.Ref("classes/users/192903223150903808"),
        f.Obj{"credentials": f.Obj{"password": "new password"}},
    ),
)

client.query(
  Replace(
    Ref("classes/users/192903223150903808"),
    Obj("credentials" -> Obj("password" -> "new password"))))

client.query(
  q.replace(
    Ref("classes/users/192903223150903808"),
    {"credentials": {"password": "new password"}}
  ))

$client.query do
  replace ref('classes/users/192903223150903808'),
          credentials: { password: 'new password' }
end

client.query(
    Replace(
        ref: Ref("classes/users/192903223150903808"),
        with: Obj("credentials" => Obj("password" => "new password"))
    )
)

client.query(
  q.Replace(
    Ref("classes/users/192903223150903808"),
    { credentials: { password: "new password" } }));

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "classes/users/192903223150903808" },
    "class": { "@ref": "classes/users" },
    "ts": 1520225699657259
  }
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699657259
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699657259
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699657259
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699657259
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699657259
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699657259
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699657259
}

=> {
  "ref": { "@ref": "classes/users/192903223150903808" },
  "class": { "@ref": "classes/users" },
  "ts": 1520225699657259
}

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI:

curl https://db.fauna.com/tokens/self \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI:

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223338598912" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699642035,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$WImHA/pjKP7ZXAf.RJIEh.ZwLQCQ9Z/AzKUVKKZwq6P9ZI6SXq6k."
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223338598912" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699642035,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$WImHA/pjKP7ZXAf.RJIEh.ZwLQCQ9Z/AzKUVKKZwq6P9ZI6SXq6k."
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223338598912" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699642035,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$WImHA/pjKP7ZXAf.RJIEh.ZwLQCQ9Z/AzKUVKKZwq6P9ZI6SXq6k."
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223338598912" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699642035,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$WImHA/pjKP7ZXAf.RJIEh.ZwLQCQ9Z/AzKUVKKZwq6P9ZI6SXq6k."
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223338598912" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699642035,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$WImHA/pjKP7ZXAf.RJIEh.ZwLQCQ9Z/AzKUVKKZwq6P9ZI6SXq6k."
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223338598912" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699642035,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$WImHA/pjKP7ZXAf.RJIEh.ZwLQCQ9Z/AzKUVKKZwq6P9ZI6SXq6k."
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223338598912" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699642035,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$WImHA/pjKP7ZXAf.RJIEh.ZwLQCQ9Z/AzKUVKKZwq6P9ZI6SXq6k."
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223338598912" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699642035,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$WImHA/pjKP7ZXAf.RJIEh.ZwLQCQ9Z/AzKUVKKZwq6P9ZI6SXq6k."
  }
}

=> HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/192903223338598912" },
    "class": { "@ref": "tokens" },
    "ts": 1520225699642035,
    "instance": { "@ref": "classes/users/192903223150903808" },
    "hashed_secret": "$2a$05$WImHA/pjKP7ZXAf.RJIEh.ZwLQCQ9Z/AzKUVKKZwq6P9ZI6SXq6k."
  }
}

Identifying a User

A user’s password can be validated without creating a new token.


curl https://db.fauna.com/ \
    -u fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq: \
    -d '[
          {
            "identify": { "@ref": "classes/users/192903223150903808" },
            "password": "secret password"
          },
          {
            "identify": { "@ref": "classes/users/192903223150903808" },
            "password": "badpassword"
          }
        ]'

client.Query(
  Arr(
    Identify(
      Ref("classes/users/192903223150903808"),
      "secret password"),
    Identify(
      Ref("classes/users/192903223150903808"),
      "badpassword")
  ));

client.query(
  Arr(
    Identify(
      Ref("classes/users/192903223150903808"),
      Value("secret password")),
    Identify(
      Ref("classes/users/192903223150903808"),
      Value("badpassword"))
  ));

client.Query(
    f.Arr{
        f.Identify(
            f.Ref("classes/users/192903223150903808"),
            "secret password",
        ),
        f.Identify(
            f.Ref("classes/users/192903223150903808"),
            "badpassword",
        ),
    },
)

client.query(
  Arr(
    Identify(
      Ref("classes/users/192903223150903808"),
      "secret password"),
    Identify(
      Ref("classes/users/192903223150903808"),
      "badpassword")
  ))

client.query(
  [
    q.identify(
      Ref("classes/users/192903223150903808"),
      "secret password"
    ),
    q.identify(
      Ref("classes/users/192903223150903808"),
      "badpassword"
    )
  ])

$client.query do
  [
    identify(ref('classes/users/192903223150903808'),
             'secret password'),
    identify(ref('classes/users/192903223150903808'),
             'badpassword')
  ]
end

client.query(
    Arr(
        Identify(
            ref: Ref("classes/users/192903223150903808"),
            password: "secret password"
        ),
        Identify(
            ref: Ref("classes/users/192903223150903808"),
            password: "badpassword"
        )
    )
)

client.query(
  [
    q.Identify(
      Ref("classes/users/192903223150903808"),
      "secret password"),
    q.Identify(
      Ref("classes/users/192903223150903808"),
      "badpassword")
  ]);

=> HTTP/1.1 200 OK
{ "resource": [ false, false ] }

=> [ false, false ]

=> [ false, false ]

=> [ false, false ]

=> [ false, false ]

=> [ false, false ]

=> [ false, false ]

=> [ false, false ]

=> [ false, false ]

3rd Party Delegation

Our service provides an API that third party clients can use to provide their own interface. For security, we’d like to provide unique tokens for each third party client. This will allow them to access our service on our user’s behalf while providing a way for our user to easily revoke the client’s access.

First we’ll create an index that will enable us to list all of a user’s tokens. Login allows us to attach data to a token by adding extra fields. We’ll use this to name our tokens the same name as the third party service that will use them. We can then store the name as a covered value in the index making it easier to list all of a user’s tokens.


curl https://db.fauna.com/ \
    -u fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq: \
    -d '{
          "create_index": {
            "object": {
              "name": "tokens_by_instance",
              "permissions": { "object": { "read": "public" } },
              "source": { "@ref": "tokens" },
              "terms": [ { "object": { "field": "instance" } } ],
              "values": [ { "object": { "field": [ "data", "name" ] } } ]
            }
          }
        }'

client.Query(
  CreateIndex(
    Obj(
      "name", "tokens_by_instance",
      "permissions", Obj("read", "public"),
      "source", Ref("tokens"),
      "terms", Arr(Obj("field", "instance")),
      "values", Arr(Obj("field", Arr("data", "name")))
    )));

client.query(
  CreateIndex(
    Obj(
      "name", Value("tokens_by_instance"),
      "permissions", Obj("read", Value("public")),
      "source", Ref("tokens"),
      "terms", Arr(Obj("field", Value("instance"))),
      "values", Arr(Obj("field", Arr(Value("data"), Value("name"))))
    )));

client.Query(
    f.CreateIndex(
        f.Obj{
            "name": "tokens_by_instance",
            "permissions": f.Obj{"read": "public"},
            "source": f.Ref("tokens"),
            "terms": f.Arr{f.Obj{"field": "instance"}},
            "values": f.Arr{f.Obj{"field": f.Arr{"data", "name"}}},
        },
    ),
)

client.query(
  CreateIndex(
    Obj(
      "name" -> "tokens_by_instance",
      "permissions" -> Obj("read" -> "public"),
      "source" -> Ref("tokens"),
      "terms" -> Arr(Obj("field" -> "instance")),
      "values" -> Arr(Obj("field" -> Arr("data", "name")))
    )))

client.query(
  q.create_index(
    {
      "name": "tokens_by_instance",
      "permissions": {"read": "public"},
      "source": Ref("tokens"),
      "terms": [{"field": "instance"}],
      "values": [{"field": ["data", "name"]}]
    }
  ))

$client.query do
  create_index name: 'tokens_by_instance',
               permissions: { read: 'public' },
               source: ref('tokens'),
               terms: [{ field: 'instance' }],
               values: [{ field: ['data', 'name'] }]
end

client.query(
    CreateIndex(
        Obj(
            "name" => "tokens_by_instance",
            "permissions" => Obj("read" => "public"),
            "source" => Ref("tokens"),
            "terms" => Arr(Obj("field" => "instance")),
            "values" => Arr(Obj("field" => Arr("data", "name")))
        )
    )
)

client.query(
  q.CreateIndex(
    {
      name: "tokens_by_instance",
      permissions: { read: "public" },
      source: Ref("tokens"),
      terms: [{ field: "instance" }],
      values: [{ field: ["data", "name"] }]
    }));

=> HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "indexes/tokens_by_instance" },
    "class": { "@ref": "indexes" },
    "ts": 1520225699709315,
    "active": false,
    "partitions": 1,
    "name": "tokens_by_instance",
    "permissions": { "read": "public" },
    "source": { "@ref": "tokens" },
    "terms": [ { "field": [ "instance" ] } ],
    "values": [ { "field": [ "data", "name" ] } ]
  }
}

=> {
  "ref": { "@ref": "indexes/tokens_by_instance" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699709315,
  "active": false,
  "partitions": 1,
  "name": "tokens_by_instance",
  "permissions": { "read": "public" },
  "source": { "@ref": "tokens" },
  "terms": [ { "field": [ "instance" ] } ],
  "values": [ { "field": [ "data", "name" ] } ]
}

=> {
  "ref": { "@ref": "indexes/tokens_by_instance" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699709315,
  "active": false,
  "partitions": 1,
  "name": "tokens_by_instance",
  "permissions": { "read": "public" },
  "source": { "@ref": "tokens" },
  "terms": [ { "field": [ "instance" ] } ],
  "values": [ { "field": [ "data", "name" ] } ]
}

=> {
  "ref": { "@ref": "indexes/tokens_by_instance" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699709315,
  "active": false,
  "partitions": 1,
  "name": "tokens_by_instance",
  "permissions": { "read": "public" },
  "source": { "@ref": "tokens" },
  "terms": [ { "field": [ "instance" ] } ],
  "values": [ { "field": [ "data", "name" ] } ]
}

=> {
  "ref": { "@ref": "indexes/tokens_by_instance" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699709315,
  "active": false,
  "partitions": 1,
  "name": "tokens_by_instance",
  "permissions": { "read": "public" },
  "source": { "@ref": "tokens" },
  "terms": [ { "field": [ "instance" ] } ],
  "values": [ { "field": [ "data", "name" ] } ]
}

=> {
  "ref": { "@ref": "indexes/tokens_by_instance" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699709315,
  "active": false,
  "partitions": 1,
  "name": "tokens_by_instance",
  "permissions": { "read": "public" },
  "source": { "@ref": "tokens" },
  "terms": [ { "field": [ "instance" ] } ],
  "values": [ { "field": [ "data", "name" ] } ]
}

=> {
  "ref": { "@ref": "indexes/tokens_by_instance" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699709315,
  "active": false,
  "partitions": 1,
  "name": "tokens_by_instance",
  "permissions": { "read": "public" },
  "source": { "@ref": "tokens" },
  "terms": [ { "field": [ "instance" ] } ],
  "values": [ { "field": [ "data", "name" ] } ]
}

=> {
  "ref": { "@ref": "indexes/tokens_by_instance" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699709315,
  "active": false,
  "partitions": 1,
  "name": "tokens_by_instance",
  "permissions": { "read": "public" },
  "source": { "@ref": "tokens" },
  "terms": [ { "field": [ "instance" ] } ],
  "values": [ { "field": [ "data", "name" ] } ]
}

=> {
  "ref": { "@ref": "indexes/tokens_by_instance" },
  "class": { "@ref": "indexes" },
  "ts": 1520225699709315,
  "active": false,
  "partitions": 1,
  "name": "tokens_by_instance",
  "permissions": { "read": "public" },
  "source": { "@ref": "tokens" },
  "terms": [ { "field": [ "instance" ] } ],
  "values": [ { "field": [ "data", "name" ] } ]
}

Next we create a token for each third party service our user uses. This example creates a set of tokens all at once.


curl https://db.fauna.com/ \
    -u fnACrVRybLACAOytRXDMleFgdUZKXcJfMdzyjsRq: \
    -d '{
          "map": {
            "lambda": "name",
            "expr": {
              "login": { "@ref": "classes/users/192903223150903808" },
              "params": {
                "object": {
                  "password": "new password",
                  "data": { "object": { "name": { "var": "name" } } }
                }
              }
            }
          },
          "collection": [ "Desktop App", "Mobile App", "Web Service" ]
        }'

client.Query(
  Map(
    Arr("Desktop App", "Mobile App", "Web Service"),
    name => Login(
      Ref("classes/users/192903223150903808"),
      Obj("password", "new password", "data", Obj("name", name)))));

client.query(
  Map(
    Arr(
      Value("Desktop App"),
      Value("Mobile App"),
      Value("Web Service")
    ),
    Lambda(
      Value("name"),
      Login(
        Ref("classes/users/192903223150903808"),
        Obj(
          "password", Value("new password"),
          "data", Obj("name", Var("name"))
        )))));

client.Query(
    f.Map(
        f.Arr{"Desktop App", "Mobile App", "Web Service"},
        f.Lambda(
            "name",
            f.Login(
                f.Ref("classes/users/192903223150903808"),
                f.Obj{
                    "password": "new password",
                    "data": f.Obj{"name": f.Var("name")},
                },
            ),
        ),
    ),
)

client.query(
  Map(
    Arr("Desktop App", "Mobile App", "Web Service"),
    Lambda { name =>
      Login(
        Ref("classes/users/192903223150903808"),
        Obj(
          "password" -> "new password",
          "data" -> Obj("name" -> name)
        ))
    }))

client.query(
  q.map_expr(
    lambda name: q.login(
      Ref("classes/users/192903223150903808"),
      {"password": "new password", "data": {"name": name}}
    ),
    ["Desktop App", "Mobile App", "Web Service"]
  ))

$client.query do
  map ['Desktop App', 'Mobile App', 'Web Service'] do |name|
    login(ref('classes/users/192903223150903808'),
          password: 'new password', data: { name: name })
  end
end

client.query(
    Map(
        collection: Arr("Desktop App", "Mobile App", "Web Service"),
        to: { name in
            Login(
                for: Ref("classes/users/192903223150903808"),
                Obj(
                    "password" => "new password",
                    "data" => Obj("name" => name)
                )
            )
        }
    )
)

client.query(
  q.Map(
    ["Desktop App", "Mobile App", "Web Service"],
    function(name) {
      return q.Login(
        Ref("classes/users/192903223150903808"),
        { password: "new password", data: { name: name } });
    }));

=> HTTP/1.1 200 OK
{
  "resource": [
    {
      "ref": { "@ref": "tokens/192903235239936512" },
      "class": { "@ref": "tokens" },
      "ts": 1520225710981259,
      "data": { "name": "Desktop App" },
      "instance": { "@ref": "classes/users/192903223150903808" },
      "secret": "fnECrVR4bIACAAKtVHJrQAIAiZstNH0U4yvvIHs1MqdbR4ZOwKk"
    },
    {
      "ref": { "@ref": "tokens/192903235238887936" },
      "class": { "@ref": "tokens" },
      "ts": 1520225710981259,
      "data": { "name": "Mobile App" },
      "instance": { "@ref": "classes/users/192903223150903808" },
      "secret": "fnECrVR4bHACAAKtVHJrQAIActD0lCTdGiO7az40R30mMPj3030"
    },
    {
      "ref": { "@ref": "tokens/192903235238888960" },
      "class": { "@ref": "tokens" },
      "ts": 1520225710981259,
      "data": { "name": "Web Service" },
      "instance": { "@ref": "classes/users/192903223150903808" },
      "secret": "fnECrVR4bHAGAAKtVHJrQAIA5390cQwruJgR4dVG3Tlgk4dB6e8"
    }
  ]
}

=> [
  {
    "ref": { "@ref": "tokens/192903235239936512" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bIACAAKtVHJrQAIAiZstNH0U4yvvIHs1MqdbR4ZOwKk"
  },
  {
    "ref": { "@ref": "tokens/192903235238887936" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHACAAKtVHJrQAIActD0lCTdGiO7az40R30mMPj3030"
  },
  {
    "ref": { "@ref": "tokens/192903235238888960" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHAGAAKtVHJrQAIA5390cQwruJgR4dVG3Tlgk4dB6e8"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/192903235239936512" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bIACAAKtVHJrQAIAiZstNH0U4yvvIHs1MqdbR4ZOwKk"
  },
  {
    "ref": { "@ref": "tokens/192903235238887936" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHACAAKtVHJrQAIActD0lCTdGiO7az40R30mMPj3030"
  },
  {
    "ref": { "@ref": "tokens/192903235238888960" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHAGAAKtVHJrQAIA5390cQwruJgR4dVG3Tlgk4dB6e8"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/192903235239936512" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bIACAAKtVHJrQAIAiZstNH0U4yvvIHs1MqdbR4ZOwKk"
  },
  {
    "ref": { "@ref": "tokens/192903235238887936" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHACAAKtVHJrQAIActD0lCTdGiO7az40R30mMPj3030"
  },
  {
    "ref": { "@ref": "tokens/192903235238888960" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHAGAAKtVHJrQAIA5390cQwruJgR4dVG3Tlgk4dB6e8"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/192903235239936512" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bIACAAKtVHJrQAIAiZstNH0U4yvvIHs1MqdbR4ZOwKk"
  },
  {
    "ref": { "@ref": "tokens/192903235238887936" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHACAAKtVHJrQAIActD0lCTdGiO7az40R30mMPj3030"
  },
  {
    "ref": { "@ref": "tokens/192903235238888960" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHAGAAKtVHJrQAIA5390cQwruJgR4dVG3Tlgk4dB6e8"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/192903235239936512" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bIACAAKtVHJrQAIAiZstNH0U4yvvIHs1MqdbR4ZOwKk"
  },
  {
    "ref": { "@ref": "tokens/192903235238887936" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHACAAKtVHJrQAIActD0lCTdGiO7az40R30mMPj3030"
  },
  {
    "ref": { "@ref": "tokens/192903235238888960" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHAGAAKtVHJrQAIA5390cQwruJgR4dVG3Tlgk4dB6e8"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/192903235239936512" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bIACAAKtVHJrQAIAiZstNH0U4yvvIHs1MqdbR4ZOwKk"
  },
  {
    "ref": { "@ref": "tokens/192903235238887936" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHACAAKtVHJrQAIActD0lCTdGiO7az40R30mMPj3030"
  },
  {
    "ref": { "@ref": "tokens/192903235238888960" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHAGAAKtVHJrQAIA5390cQwruJgR4dVG3Tlgk4dB6e8"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/192903235239936512" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bIACAAKtVHJrQAIAiZstNH0U4yvvIHs1MqdbR4ZOwKk"
  },
  {
    "ref": { "@ref": "tokens/192903235238887936" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHACAAKtVHJrQAIActD0lCTdGiO7az40R30mMPj3030"
  },
  {
    "ref": { "@ref": "tokens/192903235238888960" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHAGAAKtVHJrQAIA5390cQwruJgR4dVG3Tlgk4dB6e8"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/192903235239936512" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bIACAAKtVHJrQAIAiZstNH0U4yvvIHs1MqdbR4ZOwKk"
  },
  {
    "ref": { "@ref": "tokens/192903235238887936" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHACAAKtVHJrQAIActD0lCTdGiO7az40R30mMPj3030"
  },
  {
    "ref": { "@ref": "tokens/192903235238888960" },
    "class": { "@ref": "tokens" },
    "ts": 1520225710981259,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/192903223150903808" },
    "secret": "fnECrVR4bHAGAAKtVHJrQAIA5390cQwruJgR4dVG3Tlgk4dB6e8"
  }
]

Finally we can list all of a user’s tokens by querying the index we built.


curl https://db.fauna.com/ \
    -u fnECrVR1pyACAAKtVHJrQAIAUW3r7mdLKjBV-u1EwjbtFW203gI: \
    -d '{
          "paginate": {
            "match": { "index": "tokens_by_instance" },
            "terms": {
              "select": "instance",
              "from": { "get": { "@ref": "tokens/self" } }
            }
          }
        }'

client.Query(
  Paginate(
    Match(
      Index("tokens_by_instance"),
      Select("instance", Get(Ref("tokens/self"))))));

client.query(
  Paginate(
      Match(
        Index(Value("tokens_by_instance")),
        Select(Value("instance"), Get(Ref("tokens/self"))))));

client.Query(
    f.Paginate(
        f.MatchTerm(
            f.Index("tokens_by_instance"),
            f.Select("instance", f.Get(f.Ref("tokens/self"))),
        ),
    ),
)

client.query(
  Paginate(
    Match(
      Index("tokens_by_instance"),
      Select("instance", Get(Ref("tokens/self"))))))

client.query(
  q.paginate(
    q.match(
      q.index("tokens_by_instance"),
      q.select("instance", q.get(Ref("tokens/self")))
    )
  ))

$client.query do
  paginate match(index('tokens_by_instance'),
                 select('instance', get(ref('tokens/self'))))
end

client.query(
    Paginate(
        Match(
            index: Index("tokens_by_instance"),
            terms: Select(
                path: "instance",
                from: Get(Ref("tokens/self"))
            )
        )
    )
)

client.query(
  q.Paginate(
    q.Match(
      q.Index("tokens_by_instance"),
      q.Select("instance", q.Get(Ref("tokens/self"))))));

=> HTTP/1.1 200 OK
{
  "resource": { "data": [ "Desktop App", "Mobile App", "Web Service" ] }
}

=> { "data": [ "Desktop App", "Mobile App", "Web Service" ] }

=> { "data": [ "Desktop App", "Mobile App", "Web Service" ] }

=> { "data": [ "Desktop App", "Mobile App", "Web Service" ] }

=> { "data": [ "Desktop App", "Mobile App", "Web Service" ] }

=> { "data": [ "Desktop App", "Mobile App", "Web Service" ] }

=> { "data": [ "Desktop App", "Mobile App", "Web Service" ] }

=> { "data": [ "Desktop App", "Mobile App", "Web Service" ] }

=> { "data": [ "Desktop App", "Mobile App", "Web Service" ] }

Further Reading

In this tutorial, we’ve modeled a simple user authentication service based on FaunaDB’s built-in authentication features. We’ve also covered how to create tokens for specific 3rd party applications that may access users’ data on their behalf.

In the next tutorial, we’ll demonstrate how to view and edit both the current and historical data of your database.