01 Tutorials

Languages:

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

We recommend you first take a look at the FaunaDB Basics tutorial in order to get familiar with the basic concepts around FaunaDB.

To follow along 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 kqnPAi3Kj3ZgAAC0Hu51Ng2dtn0JcgP7Fb-Q_uzLGZE: \
    -d '{ "create_database": { "object": { "name": "my_app" } } }'

FaunaClient adminClient = FaunaClient.builder()
  .withSecret(adminKey)
  .build();

adminClient.query(CreateDatabase(Obj("name", Value("my_app"))));

var adminClient = new faunadb.Client({
  secret: adminKey
});

adminClient.query(q.CreateDatabase({ name: "my_app" }));

val adminClient = FaunaClient(secret = adminKey)

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

$admin_client = Fauna::Client.new(secret: adminKey)

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

var adminClient = new FaunaClient(secret: adminKey);

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

adminClient = FaunaClient(secret=adminKey)

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

adminClient = f.NewFaunaClient(adminKey)

adminClient.Query(f.CreateDatabase(f.Obj{"name": "my_app"}))

let adminClient = FaunaDB.Client(secret: adminKey)

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

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

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

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

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

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

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

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

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

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

You’ll use the my_app database across all of the tutorials, so this query may return an error if 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 kqnPAi3Kj3ZgAAC0Hu51Ng2dtn0JcgP7Fb-Q_uzLGZE: \
    -d '{
          "create_key": {
            "object": { "database": { "database": "my_app" }, "role": "server" }
          }
        }'

Value key = adminClient.query(
  CreateKey(
    Obj(
      "database", Database(Value("my_app")),
      "role", Value("server")
    ))).get(1, TimeUnit.SECONDS); // block thread until key gets created

String serverKey = key.get(Field.at("secret").to(Codec.STRING));

var serverKey;
adminClient.query(
  q.CreateKey(
    { database: q.Database("my_app"), role: "server" }))
  .then(function(key) { serverKey = key.secret; });

val keyF = adminClient.query(
  CreateKey(
    Obj("database" -> Database("my_app"), "role" -> "server")))

val key = Await.result(keyF, 1.second) // block thread until key gets created
val serverKey = key(Field("secret").to[String]).get

key = $admin_client.query do
  create_key database: database('my_app'), role: 'server'
end

serverKey = key[:secret]

var key = await adminClient.Query(
  CreateKey(
    Obj("database", Database("my_app"), "role", "server")));

var serverKey = key.Get(Field.At("secret").To(Codec.STRING));

key = adminClient.query(
  q.create_key(
    {"database": q.database("my_app"), "role": "server"}
  ))

serverKey = key["secret"]

var serverKey string

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

_ = key.At(f.ObjKey("secret")).Get(&serverKey)

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

HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "keys/104979509692858368" },
    "class": { "@ref": "keys" },
    "ts": 1436375112199704,
    "database": { "@ref": "databases/my_app" },
    "role": "server",
    "secret": "kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI",
    "hashed_secret": "$2a$05$.saZ/E/JSuEEkbIfTU/GN.7iKEBBpBGePtWe/mZyL/rmf4uXeZ5oi"
  }
}

=> {
  "ref": { "@ref": "keys/104979509692858368" },
  "class": { "@ref": "keys" },
  "ts": 1436375112199704,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI",
  "hashed_secret": "$2a$05$.saZ/E/JSuEEkbIfTU/GN.7iKEBBpBGePtWe/mZyL/rmf4uXeZ5oi"
}

=> {
  "ref": { "@ref": "keys/104979509692858368" },
  "class": { "@ref": "keys" },
  "ts": 1436375112199704,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI",
  "hashed_secret": "$2a$05$.saZ/E/JSuEEkbIfTU/GN.7iKEBBpBGePtWe/mZyL/rmf4uXeZ5oi"
}

=> {
  "ref": { "@ref": "keys/104979509692858368" },
  "class": { "@ref": "keys" },
  "ts": 1436375112199704,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI",
  "hashed_secret": "$2a$05$.saZ/E/JSuEEkbIfTU/GN.7iKEBBpBGePtWe/mZyL/rmf4uXeZ5oi"
}

=> {
  "ref": { "@ref": "keys/104979509692858368" },
  "class": { "@ref": "keys" },
  "ts": 1436375112199704,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI",
  "hashed_secret": "$2a$05$.saZ/E/JSuEEkbIfTU/GN.7iKEBBpBGePtWe/mZyL/rmf4uXeZ5oi"
}

=> {
  "ref": { "@ref": "keys/104979509692858368" },
  "class": { "@ref": "keys" },
  "ts": 1436375112199704,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI",
  "hashed_secret": "$2a$05$.saZ/E/JSuEEkbIfTU/GN.7iKEBBpBGePtWe/mZyL/rmf4uXeZ5oi"
}

=> {
  "ref": { "@ref": "keys/104979509692858368" },
  "class": { "@ref": "keys" },
  "ts": 1436375112199704,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI",
  "hashed_secret": "$2a$05$.saZ/E/JSuEEkbIfTU/GN.7iKEBBpBGePtWe/mZyL/rmf4uXeZ5oi"
}

=> {
  "ref": { "@ref": "keys/104979509692858368" },
  "class": { "@ref": "keys" },
  "ts": 1436375112199704,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI",
  "hashed_secret": "$2a$05$.saZ/E/JSuEEkbIfTU/GN.7iKEBBpBGePtWe/mZyL/rmf4uXeZ5oi"
}

=> {
  "ref": { "@ref": "keys/104979509692858368" },
  "class": { "@ref": "keys" },
  "ts": 1436375112199704,
  "database": { "@ref": "databases/my_app" },
  "role": "server",
  "secret": "kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI",
  "hashed_secret": "$2a$05$.saZ/E/JSuEEkbIfTU/GN.7iKEBBpBGePtWe/mZyL/rmf4uXeZ5oi"
}

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 kqnPAi3Kj3ZgAAC0Hu51Ng2dtn0JcgP7Fb-Q_uzLGZE: \
    -d '{
          "create_key": {
            "object": { "database": { "database": "my_app" }, "role": "client" }
          }
        }'

Value key = adminClient.query(
  CreateKey(
    Obj(
      "database", Database(Value("my_app")),
      "role", Value("client")
    ))).get(1, TimeUnit.SECONDS); // block thread until key gets created

String clientKey = key.get(Field.at("secret").to(Codec.STRING));

var clientKey;
adminClient.query(
  q.CreateKey(
    { database: q.Database("my_app"), role: "client" }))
  .then(function(key) { clientKey = key.secret; });

val keyF = adminClient.query(
  CreateKey(
    Obj("database" -> Database("my_app"), "role" -> "client")))

val key = Await.result(keyF, 1.second) // block thread until key gets created
val clientKey = key(Field("secret").to[String]).get

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

clientKey = key[:secret]

var key = await adminClient.Query(
  CreateKey(
    Obj("database", Database("my_app"), "role", "client")));

var clientKey = key.Get(Field.At("secret").To(Codec.STRING));

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

clientKey = key["secret"]

var clientKey string

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

_ = key.At(f.ObjKey("secret")).Get(&clientKey)

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

HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "keys/104979509693618791" },
    "class": { "@ref": "keys" },
    "ts": 1436375112257866,
    "database": { "@ref": "databases/my_app" },
    "role": "client",
    "secret": "kqnPAi3KlXEgAAC0OHSIyrIITuOMRfwre9owibw4nPU",
    "hashed_secret": "$2a$05$/FgxoctNLXMBjYAcea/dwOeyYLJtQLGiEdf2S/Lu6ZN7e6Gps6WF6"
  }
}

=> {
  "ref": { "@ref": "keys/104979509693618791" },
  "class": { "@ref": "keys" },
  "ts": 1436375112257866,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "kqnPAi3KlXEgAAC0OHSIyrIITuOMRfwre9owibw4nPU",
  "hashed_secret": "$2a$05$/FgxoctNLXMBjYAcea/dwOeyYLJtQLGiEdf2S/Lu6ZN7e6Gps6WF6"
}

=> {
  "ref": { "@ref": "keys/104979509693618791" },
  "class": { "@ref": "keys" },
  "ts": 1436375112257866,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "kqnPAi3KlXEgAAC0OHSIyrIITuOMRfwre9owibw4nPU",
  "hashed_secret": "$2a$05$/FgxoctNLXMBjYAcea/dwOeyYLJtQLGiEdf2S/Lu6ZN7e6Gps6WF6"
}

=> {
  "ref": { "@ref": "keys/104979509693618791" },
  "class": { "@ref": "keys" },
  "ts": 1436375112257866,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "kqnPAi3KlXEgAAC0OHSIyrIITuOMRfwre9owibw4nPU",
  "hashed_secret": "$2a$05$/FgxoctNLXMBjYAcea/dwOeyYLJtQLGiEdf2S/Lu6ZN7e6Gps6WF6"
}

=> {
  "ref": { "@ref": "keys/104979509693618791" },
  "class": { "@ref": "keys" },
  "ts": 1436375112257866,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "kqnPAi3KlXEgAAC0OHSIyrIITuOMRfwre9owibw4nPU",
  "hashed_secret": "$2a$05$/FgxoctNLXMBjYAcea/dwOeyYLJtQLGiEdf2S/Lu6ZN7e6Gps6WF6"
}

=> {
  "ref": { "@ref": "keys/104979509693618791" },
  "class": { "@ref": "keys" },
  "ts": 1436375112257866,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "kqnPAi3KlXEgAAC0OHSIyrIITuOMRfwre9owibw4nPU",
  "hashed_secret": "$2a$05$/FgxoctNLXMBjYAcea/dwOeyYLJtQLGiEdf2S/Lu6ZN7e6Gps6WF6"
}

=> {
  "ref": { "@ref": "keys/104979509693618791" },
  "class": { "@ref": "keys" },
  "ts": 1436375112257866,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "kqnPAi3KlXEgAAC0OHSIyrIITuOMRfwre9owibw4nPU",
  "hashed_secret": "$2a$05$/FgxoctNLXMBjYAcea/dwOeyYLJtQLGiEdf2S/Lu6ZN7e6Gps6WF6"
}

=> {
  "ref": { "@ref": "keys/104979509693618791" },
  "class": { "@ref": "keys" },
  "ts": 1436375112257866,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "kqnPAi3KlXEgAAC0OHSIyrIITuOMRfwre9owibw4nPU",
  "hashed_secret": "$2a$05$/FgxoctNLXMBjYAcea/dwOeyYLJtQLGiEdf2S/Lu6ZN7e6Gps6WF6"
}

=> {
  "ref": { "@ref": "keys/104979509693618791" },
  "class": { "@ref": "keys" },
  "ts": 1436375112257866,
  "database": { "@ref": "databases/my_app" },
  "role": "client",
  "secret": "kqnPAi3KlXEgAAC0OHSIyrIITuOMRfwre9owibw4nPU",
  "hashed_secret": "$2a$05$/FgxoctNLXMBjYAcea/dwOeyYLJtQLGiEdf2S/Lu6ZN7e6Gps6WF6"
}

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 kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI: \
    -d '{ "create_class": { "object": { "name": "users" } } }'

FaunaClient serverClient = FaunaClient.builder()
  .withSecret(serverKey)
  .build();

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

var serverClient = new faunadb.Client({
  secret: serverKey
});

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

val serverClient = FaunaClient(secret = serverKey)

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

$serverClient = Fauna::Client.new(secret: serverKey)

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

var serverClient = new FaunaClient(secret: serverKey);

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

serverClient = FaunaClient(secret=serverKey)

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

serverClient = f.NewFaunaClient(serverKey)

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

let serverClient = FaunaDB.Client(secret: serverKey)

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

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

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

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

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

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

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

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

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

=> {
  "ref": { "@ref": "classes/users" },
  "class": { "@ref": "classes" },
  "ts": 1436375112316028,
  "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 kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI: \
    -d '{
          "create_index": {
            "object": {
              "name": "users_by_email",
              "permissions": { "object": { "read": "public" } },
              "source": { "class": "users" },
              "terms": [ { "object": { "field": [ "data", "email" ] } } ],
              "unique": true
            }
          }
        }'

serverClient.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)
    )));

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

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

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

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

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

serverClient.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,
        },
    ),
)

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

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

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

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

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

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

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

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

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

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

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 kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI: \
    -d '{
          "create": { "class": "users" },
          "params": {
            "object": {
              "credentials": { "object": { "password": "secret password" } },
              "data": { "object": { "email": "alice@example.com" } }
            }
          }
        }'

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

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

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

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

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

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

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

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

HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "classes/users/104979509694379214" },
    "class": { "@ref": "classes/users" },
    "ts": 1436375112432352,
    "data": { "email": "alice@example.com" }
  }
}

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

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

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

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

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

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

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

=> {
  "ref": { "@ref": "classes/users/104979509694379214" },
  "class": { "@ref": "classes/users" },
  "ts": 1436375112432352,
  "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 kqnPAi3KlXEgAAC0OHSIyrIITuOMRfwre9owibw4nPU: \
    -d '{
          "login": {
            "match": { "index": "users_by_email" },
            "terms": "alice@example.com"
          },
          "params": { "object": { "password": "secret password" } }
        }'

FaunaClient clientClient = FaunaClient.builder()
  .withSecret(clientKey)
  .build();

Value key = clientClient.query(
  Login(
    Match(
      Index(Value("users_by_email")),
      Value("alice@example.com")),
    Obj("password", Value("secret password")))).get(1, TimeUnit.SECONDS); // block thread until key gets created

String token = key.get(Field.at("secret").to(Codec.STRING));

var clientClient = new faunadb.Client({
  secret: clientKey
});

var token;
clientClient.query(
  q.Login(
    q.Match(q.Index("users_by_email"), "alice@example.com"),
    { password: "secret password" }))
  .then(function(key) { token = key.secret; });

val clientClient = FaunaClient(secret = clientKey)

val keyF = clientClient.query(
  Login(
    Match(Index("users_by_email"), "alice@example.com"),
    Obj("password" -> "secret password")))

val key = Await.result(keyF, 1.second) // block thread until key gets created
val token = key(Field("secret").to[String]).get

$clientClient = Fauna::Client.new(secret: clientKey)

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

token = key[:secret]

var clientClient = new FaunaClient(secret: clientKey);

var key = await clientClient.Query(
  Login(
    Match(Index("users_by_email"), "alice@example.com"),
    Obj("password", "secret password")));

var token = key.Get(Field.At("secret").To(Codec.STRING));

clientClient = FaunaClient(secret=clientKey)

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

token = key["secret"]

clientClient = f.NewFaunaClient(clientKey)

var token string

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

_ = key.At(f.ObjKey("secret")).Get(&token)

let clientClient = FaunaDB.Client(secret: clientKey)

clientClient.query(
    Login(
        for: Match(
            index: Index("users_by_email"),
            terms: "alice@example.com"
        ),
        Obj("password" => "secret password")
    )
).await(timeout: .now() + 5)

let token: String = key.get("secret")


HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "tokens/104979509695139637" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112490514,
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA"
  }
}

=> {
  "ref": { "@ref": "tokens/104979509695139637" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112490514,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA"
}

=> {
  "ref": { "@ref": "tokens/104979509695139637" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112490514,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA"
}

=> {
  "ref": { "@ref": "tokens/104979509695139637" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112490514,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA"
}

=> {
  "ref": { "@ref": "tokens/104979509695139637" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112490514,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA"
}

=> {
  "ref": { "@ref": "tokens/104979509695139637" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112490514,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA"
}

=> {
  "ref": { "@ref": "tokens/104979509695139637" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112490514,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA"
}

=> {
  "ref": { "@ref": "tokens/104979509695139637" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112490514,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA"
}

=> {
  "ref": { "@ref": "tokens/104979509695139637" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112490514,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA"
}

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 k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA:

HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/104979509695139637" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112490514,
    "instance": { "@ref": "classes/users/104979509694379214" },
    "hashed_secret": "$2a$05$wEQ1p82hrSGAQUVH6uzLEuCVqQCrhqOjvRkNx/P78TGm9rhA4qS9a"
  }
}

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 kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI: \
    -d '{
          "login": { "@ref": "classes/users/104979509694379214" },
          "params": { "object": { "password": "secret password" } }
        }'

Value key = serverClient.query(
  Login(
    Ref("classes/users/104979509694379214"),
    Obj("password", Value("secret password")))).get(1, TimeUnit.SECONDS); // block thread until key gets created

String token2 = key.get(Field.at("secret").to(Codec.STRING));

var token2;
serverClient.query(
  q.Login(
    Ref("classes/users/104979509694379214"),
    { password: "secret password" }))
  .then(function(key) { token2 = key.secret; });

val keyF = serverClient.query(
  Login(
    Ref("classes/users/104979509694379214"),
    Obj("password" -> "secret password")))

val key = Await.result(keyF, 1.second) // block thread until key gets created
val token2 = key(Field("secret").to[String]).get

key = $serverClient.query do
  login ref('classes/users/104979509694379214'),
        password: 'secret password'
end

token2 = key[:secret]

var key = await serverClient.Query(
  Login(
    Ref("classes/users/104979509694379214"),
    Obj("password", "secret password")));

var token2 = key.Get(Field.At("secret").To(Codec.STRING));

key = serverClient.query(
  q.login(
    Ref("classes/users/104979509694379214"),
    {"password": "secret password"}
  ))

token2 = key["secret"]

var token2 string

key, _ := serverClient.Query(
    f.Login(
        f.Ref("classes/users/104979509694379214"),
        f.Obj{"password": "secret password"},
    ),
)

_ = key.At(f.ObjKey("secret")).Get(&token2)

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

HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "tokens/104979509695900060" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112548676,
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiAdK7AAAAADqc8CLiAT4QAAALQPYu04T76pXDvid-jCafW4hoqUYw"
  }
}

=> {
  "ref": { "@ref": "tokens/104979509695900060" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112548676,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiAdK7AAAAADqc8CLiAT4QAAALQPYu04T76pXDvid-jCafW4hoqUYw"
}

=> {
  "ref": { "@ref": "tokens/104979509695900060" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112548676,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiAdK7AAAAADqc8CLiAT4QAAALQPYu04T76pXDvid-jCafW4hoqUYw"
}

=> {
  "ref": { "@ref": "tokens/104979509695900060" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112548676,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiAdK7AAAAADqc8CLiAT4QAAALQPYu04T76pXDvid-jCafW4hoqUYw"
}

=> {
  "ref": { "@ref": "tokens/104979509695900060" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112548676,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiAdK7AAAAADqc8CLiAT4QAAALQPYu04T76pXDvid-jCafW4hoqUYw"
}

=> {
  "ref": { "@ref": "tokens/104979509695900060" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112548676,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiAdK7AAAAADqc8CLiAT4QAAALQPYu04T76pXDvid-jCafW4hoqUYw"
}

=> {
  "ref": { "@ref": "tokens/104979509695900060" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112548676,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiAdK7AAAAADqc8CLiAT4QAAALQPYu04T76pXDvid-jCafW4hoqUYw"
}

=> {
  "ref": { "@ref": "tokens/104979509695900060" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112548676,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiAdK7AAAAADqc8CLiAT4QAAALQPYu04T76pXDvid-jCafW4hoqUYw"
}

=> {
  "ref": { "@ref": "tokens/104979509695900060" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112548676,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiAdK7AAAAADqc8CLiAT4QAAALQPYu04T76pXDvid-jCafW4hoqUYw"
}

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 k6oCLcqe9TAAAAADqc8CLcqVdZAAALQaz43VUOEOvexWVMNr3vjvZfLVCA: \
    -d '{ "logout": true }'

FaunaClient tokenClient = FaunaClient.builder()
  .withSecret(token)
  .build();

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

var tokenClient = new faunadb.Client({
  secret: token
});

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

val tokenClient = FaunaClient(secret = token)

tokenClient.query(Logout(true))

$tokenClient = Fauna::Client.new(secret: token)

$tokenClient.query do
  logout true
end

var tokenClient = new FaunaClient(secret: token);

tokenClient.Query(Logout(true));

tokenClient = FaunaClient(secret=token)

tokenClient.query(q.logout(True))

tokenClient = f.NewFaunaClient(token)

tokenClient.Query(f.Logout(true))

let tokenClient = FaunaDB.Client(secret: token)

tokenClient.query(Logout(all: true))

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

=> true

=> true

=> true

=> true

=> true

=> true

=> true

=> true

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

HTTP/1.1 403 Forbidden
{
  "errors": [
    {
      "code": "permission denied",
      "description": "Insufficient privileges to perform the request."
    }
  ]
}

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

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 kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI: \
    -d '{
          "login": { "@ref": "classes/users/104979509694379214" },
          "params": { "object": { "password": "secret password" } }
        }'

Value key = serverClient.query(
  Login(
    Ref("classes/users/104979509694379214"),
    Obj("password", Value("secret password")))).get(1, TimeUnit.SECONDS); // block thread until key gets created

String newToken = key.get(Field.at("secret").to(Codec.STRING));

var newToken;
serverClient.query(
  q.Login(
    Ref("classes/users/104979509694379214"),
    { password: "secret password" }))
  .then(function(key) { newToken = key.secret; });

val keyF = serverClient.query(
  Login(
    Ref("classes/users/104979509694379214"),
    Obj("password" -> "secret password")))

val key = Await.result(keyF, 1.second) // block thread until key gets created
val newToken = key(Field("secret").to[String]).get

key = $serverClient.query do
  login ref('classes/users/104979509694379214'),
        password: 'secret password'
end

newToken = key[:secret]

var key = await serverClient.Query(
  Login(
    Ref("classes/users/104979509694379214"),
    Obj("password", "secret password")));

var newToken = key.Get(Field.At("secret").To(Codec.STRING));

key = serverClient.query(
  q.login(
    Ref("classes/users/104979509694379214"),
    {"password": "secret password"}
  ))

newToken = key["secret"]

var newToken string

key, _ := serverClient.Query(
    f.Login(
        f.Ref("classes/users/104979509694379214"),
        f.Obj{"password": "secret password"},
    ),
)

_ = key.At(f.ObjKey("secret")).Get(&newToken)

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

HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "tokens/104979509696660483" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112606838,
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiCvCgAAAAADqc8CLiCvAAAAALS4Nyc9l0ALFOY8Crx3Lgnn9UbFQQ"
  }
}

=> {
  "ref": { "@ref": "tokens/104979509696660483" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112606838,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiCvCgAAAAADqc8CLiCvAAAAALS4Nyc9l0ALFOY8Crx3Lgnn9UbFQQ"
}

=> {
  "ref": { "@ref": "tokens/104979509696660483" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112606838,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiCvCgAAAAADqc8CLiCvAAAAALS4Nyc9l0ALFOY8Crx3Lgnn9UbFQQ"
}

=> {
  "ref": { "@ref": "tokens/104979509696660483" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112606838,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiCvCgAAAAADqc8CLiCvAAAAALS4Nyc9l0ALFOY8Crx3Lgnn9UbFQQ"
}

=> {
  "ref": { "@ref": "tokens/104979509696660483" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112606838,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiCvCgAAAAADqc8CLiCvAAAAALS4Nyc9l0ALFOY8Crx3Lgnn9UbFQQ"
}

=> {
  "ref": { "@ref": "tokens/104979509696660483" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112606838,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiCvCgAAAAADqc8CLiCvAAAAALS4Nyc9l0ALFOY8Crx3Lgnn9UbFQQ"
}

=> {
  "ref": { "@ref": "tokens/104979509696660483" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112606838,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiCvCgAAAAADqc8CLiCvAAAAALS4Nyc9l0ALFOY8Crx3Lgnn9UbFQQ"
}

=> {
  "ref": { "@ref": "tokens/104979509696660483" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112606838,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiCvCgAAAAADqc8CLiCvAAAAALS4Nyc9l0ALFOY8Crx3Lgnn9UbFQQ"
}

=> {
  "ref": { "@ref": "tokens/104979509696660483" },
  "class": { "@ref": "tokens" },
  "ts": 1436375112606838,
  "instance": { "@ref": "classes/users/104979509694379214" },
  "secret": "k6oCLiCvCgAAAAADqc8CLiCvAAAAALS4Nyc9l0ALFOY8Crx3Lgnn9UbFQQ"
}

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


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

FaunaClient newTokenClient = FaunaClient.builder()
  .withSecret(newToken)
  .build();

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

var newTokenClient = new faunadb.Client({
  secret: newToken
});

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

val newTokenClient = FaunaClient(secret = newToken)

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

$newTokenClient = Fauna::Client.new(secret: newToken)

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

var newTokenClient = new FaunaClient(secret: newToken);

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

newTokenClient = FaunaClient(secret=newToken)

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

newTokenClient = f.NewFaunaClient(newToken)

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

let newTokenClient = FaunaDB.Client(secret: newToken)

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

HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "classes/users/104979509694379214" },
    "class": { "@ref": "classes/users" },
    "ts": 1436375112665000
  }
}

=> {
  "ref": { "@ref": "classes/users/104979509694379214" },
  "class": { "@ref": "classes/users" },
  "ts": 1436375112665000
}

=> {
  "ref": { "@ref": "classes/users/104979509694379214" },
  "class": { "@ref": "classes/users" },
  "ts": 1436375112665000
}

=> {
  "ref": { "@ref": "classes/users/104979509694379214" },
  "class": { "@ref": "classes/users" },
  "ts": 1436375112665000
}

=> {
  "ref": { "@ref": "classes/users/104979509694379214" },
  "class": { "@ref": "classes/users" },
  "ts": 1436375112665000
}

=> {
  "ref": { "@ref": "classes/users/104979509694379214" },
  "class": { "@ref": "classes/users" },
  "ts": 1436375112665000
}

=> {
  "ref": { "@ref": "classes/users/104979509694379214" },
  "class": { "@ref": "classes/users" },
  "ts": 1436375112665000
}

=> {
  "ref": { "@ref": "classes/users/104979509694379214" },
  "class": { "@ref": "classes/users" },
  "ts": 1436375112665000
}

=> {
  "ref": { "@ref": "classes/users/104979509694379214" },
  "class": { "@ref": "classes/users" },
  "ts": 1436375112665000
}

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

HTTP/1.1 200 OK
{
  "resource": {
    "ref": { "@ref": "tokens/104979509696660483" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112606838,
    "instance": { "@ref": "classes/users/104979509694379214" },
    "hashed_secret": "$2a$05$YchPKx7CPvbL8Z9QgXkXG.cBRXo.ROmaHalJd4ryEEIvGaKgQQRpC"
  }
}

Identifying a User

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


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

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

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

serverClient.query(
  Arr(
    Identify(
      Ref("classes/users/104979509694379214"),
      "secret password"),
    Identify(
      Ref("classes/users/104979509694379214"),
      "badpassword")
  ))

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

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

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

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

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

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

=> [ true, false ]

=> [ true, false ]

=> [ true, false ]

=> [ true, false ]

=> [ true, false ]

=> [ true, false ]

=> [ true, false ]

=> [ true, 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 kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI: \
    -d '{
          "create_index": {
            "object": {
              "name": "tokens_by_instance",
              "permissions": { "object": { "read": "public" } },
              "source": { "@ref": "tokens" },
              "terms": [ { "object": { "field": "instance" } } ],
              "values": [ { "object": { "field": [ "data", "name" ] } } ]
            }
          }
        }'

serverClient.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"))))
    )));

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

serverClient.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")))
    )))

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

serverClient.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")))
    )));

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

serverClient.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"}}},
        },
    ),
)

serverClient.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")))
        )
    )
)

HTTP/1.1 201 Created
{
  "resource": {
    "ref": { "@ref": "indexes/tokens_by_instance" },
    "class": { "@ref": "indexes" },
    "ts": 1436375112723162,
    "unique": false,
    "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": 1436375112723162,
  "unique": false,
  "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": 1436375112723162,
  "unique": false,
  "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": 1436375112723162,
  "unique": false,
  "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": 1436375112723162,
  "unique": false,
  "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": 1436375112723162,
  "unique": false,
  "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": 1436375112723162,
  "unique": false,
  "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": 1436375112723162,
  "unique": false,
  "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": 1436375112723162,
  "unique": false,
  "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 kqnPAi4gDivAAAC0i5t-a9YCcRnljtU2SfMJ-kWSRYI: \
    -d '{
          "map": {
            "lambda": "name",
            "expr": {
              "login": { "@ref": "classes/users/104979509694379214" },
              "params": {
                "object": {
                  "password": "secret password",
                  "data": { "object": { "name": { "var": "name" } } }
                }
              }
            }
          },
          "collection": [ "Desktop App", "Mobile App", "Web Service" ]
        }'

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

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

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

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

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

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

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

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

HTTP/1.1 200 OK
{
  "resource": [
    {
      "ref": { "@ref": "tokens/104979509697420906" },
      "class": { "@ref": "tokens" },
      "ts": 1436375112781324,
      "data": { "name": "Desktop App" },
      "instance": { "@ref": "classes/users/104979509694379214" },
      "secret": "k6oCLiDis-AAAAADqc8CLiDiqhAAALRDiEbDaeKAxIhO6R76wjxrRClZsw"
    },
    {
      "ref": { "@ref": "tokens/104979509698181329" },
      "class": { "@ref": "tokens" },
      "ts": 1436375112781324,
      "data": { "name": "Mobile App" },
      "instance": { "@ref": "classes/users/104979509694379214" },
      "secret": "k6oCLiCwvcAEAAADqc8CLiCvAAAAALRHgBEuAppNDiag0n-lHEnbkSVIWw"
    },
    {
      "ref": { "@ref": "tokens/104979509698941752" },
      "class": { "@ref": "tokens" },
      "ts": 1436375112781324,
      "data": { "name": "Web Service" },
      "instance": { "@ref": "classes/users/104979509694379214" },
      "secret": "k6oCLiDkm3AEAAADqc8CLiDiqhAAALTVc9bUtBvuf-vhmoJxWkbLoreyBg"
    }
  ]
}

=> [
  {
    "ref": { "@ref": "tokens/104979509697420906" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDis-AAAAADqc8CLiDiqhAAALRDiEbDaeKAxIhO6R76wjxrRClZsw"
  },
  {
    "ref": { "@ref": "tokens/104979509698181329" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiCwvcAEAAADqc8CLiCvAAAAALRHgBEuAppNDiag0n-lHEnbkSVIWw"
  },
  {
    "ref": { "@ref": "tokens/104979509698941752" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDkm3AEAAADqc8CLiDiqhAAALTVc9bUtBvuf-vhmoJxWkbLoreyBg"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/104979509697420906" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDis-AAAAADqc8CLiDiqhAAALRDiEbDaeKAxIhO6R76wjxrRClZsw"
  },
  {
    "ref": { "@ref": "tokens/104979509698181329" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiCwvcAEAAADqc8CLiCvAAAAALRHgBEuAppNDiag0n-lHEnbkSVIWw"
  },
  {
    "ref": { "@ref": "tokens/104979509698941752" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDkm3AEAAADqc8CLiDiqhAAALTVc9bUtBvuf-vhmoJxWkbLoreyBg"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/104979509697420906" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDis-AAAAADqc8CLiDiqhAAALRDiEbDaeKAxIhO6R76wjxrRClZsw"
  },
  {
    "ref": { "@ref": "tokens/104979509698181329" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiCwvcAEAAADqc8CLiCvAAAAALRHgBEuAppNDiag0n-lHEnbkSVIWw"
  },
  {
    "ref": { "@ref": "tokens/104979509698941752" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDkm3AEAAADqc8CLiDiqhAAALTVc9bUtBvuf-vhmoJxWkbLoreyBg"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/104979509697420906" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDis-AAAAADqc8CLiDiqhAAALRDiEbDaeKAxIhO6R76wjxrRClZsw"
  },
  {
    "ref": { "@ref": "tokens/104979509698181329" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiCwvcAEAAADqc8CLiCvAAAAALRHgBEuAppNDiag0n-lHEnbkSVIWw"
  },
  {
    "ref": { "@ref": "tokens/104979509698941752" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDkm3AEAAADqc8CLiDiqhAAALTVc9bUtBvuf-vhmoJxWkbLoreyBg"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/104979509697420906" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDis-AAAAADqc8CLiDiqhAAALRDiEbDaeKAxIhO6R76wjxrRClZsw"
  },
  {
    "ref": { "@ref": "tokens/104979509698181329" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiCwvcAEAAADqc8CLiCvAAAAALRHgBEuAppNDiag0n-lHEnbkSVIWw"
  },
  {
    "ref": { "@ref": "tokens/104979509698941752" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDkm3AEAAADqc8CLiDiqhAAALTVc9bUtBvuf-vhmoJxWkbLoreyBg"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/104979509697420906" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDis-AAAAADqc8CLiDiqhAAALRDiEbDaeKAxIhO6R76wjxrRClZsw"
  },
  {
    "ref": { "@ref": "tokens/104979509698181329" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiCwvcAEAAADqc8CLiCvAAAAALRHgBEuAppNDiag0n-lHEnbkSVIWw"
  },
  {
    "ref": { "@ref": "tokens/104979509698941752" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDkm3AEAAADqc8CLiDiqhAAALTVc9bUtBvuf-vhmoJxWkbLoreyBg"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/104979509697420906" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDis-AAAAADqc8CLiDiqhAAALRDiEbDaeKAxIhO6R76wjxrRClZsw"
  },
  {
    "ref": { "@ref": "tokens/104979509698181329" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiCwvcAEAAADqc8CLiCvAAAAALRHgBEuAppNDiag0n-lHEnbkSVIWw"
  },
  {
    "ref": { "@ref": "tokens/104979509698941752" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDkm3AEAAADqc8CLiDiqhAAALTVc9bUtBvuf-vhmoJxWkbLoreyBg"
  }
]

=> [
  {
    "ref": { "@ref": "tokens/104979509697420906" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Desktop App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDis-AAAAADqc8CLiDiqhAAALRDiEbDaeKAxIhO6R76wjxrRClZsw"
  },
  {
    "ref": { "@ref": "tokens/104979509698181329" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Mobile App" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiCwvcAEAAADqc8CLiCvAAAAALRHgBEuAppNDiag0n-lHEnbkSVIWw"
  },
  {
    "ref": { "@ref": "tokens/104979509698941752" },
    "class": { "@ref": "tokens" },
    "ts": 1436375112781324,
    "data": { "name": "Web Service" },
    "instance": { "@ref": "classes/users/104979509694379214" },
    "secret": "k6oCLiDkm3AEAAADqc8CLiDiqhAAALTVc9bUtBvuf-vhmoJxWkbLoreyBg"
  }
]

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


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

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

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

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

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

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

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

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

newTokenClient.query(
    Paginate(
        Match(
            index: Index("tokens_by_instance"),
            terms: Select(
                path: "instance",
                from: 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.

You may want to take a look at Authentication Requests for a deeper explanation, as well as the Developer Guide overall.