Impersonate database access
You can write applications that impersonate database access using a key that you already have. For example, you might want to impersonate database access when:
-
You have a reporting tool that needs to gather information from all of your child databases.
-
Your application makes queries on behalf of users, and you want to test that features or access controls are working correctly but you don’t have access to user passwords.
-
Your application has different capabilities for users with differing roles, and you want to test the capabilities and access controls.
For these use cases, Fauna accepts a scoped key. A scoped key uses a secret that you already have to impersonate access to Fauna. A scoped key can only impersonate the same or lesser level of privileges. You cannot use a scoped key to gain more privileges.
If you completed the Child database access, you have already used scope keys when you used the Dashboard Shell run menu. You can use this feature to run a query using different built-in roles:
Menu | Description |
---|---|
Admin |
Equivalent to using a key with the |
Server |
Equivalent to using a key with the |
Secret |
Use an existing key secret |
You can also use run a query as a user-defined Role or an identity by choosing the Document option. More about the last two options in another tutorial. This tutorial focuses on key secrets.
After you select a run menu option, that selection applies to the current database. Subsequent queries that you execute in the Dashboard Shell run under your selected option. When you change to a new child database or database, you return to the default, which is to run as Admin.
Investigate with key scopes
If you worked through from the beginning of the Access control cookbook cookbook, you should have a list of key secrets for the following:
Key name | Role | Created on | Access to |
---|---|---|---|
|
|
CoffeeStore |
Full hierarchy |
|
|
CoffeeStore |
Full hierarchy |
None. |
|
CoffeeStore/internal |
internal hierarchy |
None. |
|
CoffeeStore/production |
production hierarchy |
None. |
|
CoffeeStore/internal |
internal/bulletinBoard |
None. |
|
CoffeeStore/internal |
internal/personnel |
In a database, Fauna lists only the immediate resources available to that
caller regardless of the credentials. A parent admin
secret can connect to
and manage any child database resources. These same rules apply for any
connected application.
Investigate the practical implications of this in the Shell. Using the
Database.all()
do the following
-
In the dashboard, make sure you are still in the CoffeeStore/internal database.
-
Switch to the CoffeeStore/internal/personnel database
-
Use the run menu Secret option to run the
Database.all()
query with each of these secrets:-
coffee-admin
-
coffee-server
-
admin
secret for the CoffeeStore/internal database -
admin
secret for the CoffeeStore/production database
-
-
Switch to another database in the hierarchy and try these same secrets.
Add a scoped key an application
You already have two databases, production
and internal
, from the Child
database access tutorial. If not,
go to that section
and create them. In this procedure, you change the simple coffeestore.mjs
javascript program to use a scoped key.
-
Open the
coffeestore.mjs
file you used in Keys and built-in roles. -
Add a scoped secret after the first
envSecret
definition.const envSecret = process.env.FAUNA_SECRET; const scoped_secret = `${envSecret}:production:admin`;
The format of this scoped key is
secret[:child_db]:role
where:Field Description secret
A key secret. An
admin
key is required to access a child database.`child_db
Name of a child database. This is optional. When included, the
secret
for anadmin
key must be used. When not included, thesecret
from anadmin
orserver
key can be used.role
The name of a built-in system role to use, one of
admin
,server
, orserver-readonly
.This kind of scoped key is typically used to impersonate access to a child database. Given that
scoped_secret
uses theadmin
key, it allows full access to theproduction
child database. -
Update the
client
configuration with the newscoped_secret
.// Client that uses scoped key const client = new Client({ secret: scoped_secret, endpoint: new URL("https://db.fauna.com")});
Your finished source should look like the following:
import { Client, fql } from "fauna"; const envSecret = process.env.FAUNA_SECRET; const scoped_secret = `${envSecret}:production:admin`; // const scopedClient = new Client({ secret: scoped_secret }); const client = new Client({ secret: scoped_secret, endpoint: new URL("https://db.fauna.com")}); try { // build queries using the fql function const collection_query = fql`Collection.create({ name: "Post" })`; // Execute the query, return more informative decorated format // The decorated format is not suitable for complex objects in production systems const collection_result = await client.query(collection_query, {format: "decorated"}); console.log(collection_result); // define some data in your app const post = { headline: "Lost Doggo" }; // query using your application local variables const document_query = fql` Post.create(${post}) { id, ts, headline } `; const document_result = await client.query(document_query); console.log(document_result); } catch (error) { console.log(error) }
-
Save and close the application.
-
Set the
FAUNA_SECRET
in your environment to thecoffee-admin
secret.export FAUNA_SECRET=“fnAFKPwOAtAAIYrsap-yiSh51Kc0g0AA6XQ4inqL”
-
Run the application.
node coffeestore.mjs
You should see the
Post
andLost Doggo
document responses:{ data: NamedDocument { coll: Module { name: 'Collection' }, name: 'Post', ts: TimeStub { isoString: '2023-08-10T00:44:02.270Z' }, data: {}, indexes: {}, constraints: [] }, static_type: 'CollectionDef', summary: '', txn_ts: 1691628242270000, stats: { compute_ops: 1, read_ops: 0, write_ops: 1, query_time_ms: 90, contention_retries: 0, storage_bytes_read: 1443, storage_bytes_write: 353, rate_limits_hit: [] }, schema_version: 0 } { data: { id: '372631816002076705', ts: TimeStub { isoString: '2023-08-10T00:44:02.430Z' }, headline: 'Lost Doggo' }, static_type: '{ headline: Any, ts: Any, id: Any }', summary: '', txn_ts: 1691628242430000, stats: { compute_ops: 1, read_ops: 0, write_ops: 1, query_time_ms: 72, contention_retries: 0, storage_bytes_read: 221, storage_bytes_write: 213, rate_limits_hit: [] }, schema_version: 1691628242270000 }
-
Open the Fauna Dashboard and navigate to the
production
database. -
Confirm the
Post
exists with one document calledLost Doggo
.
Other scope key formats
There are more scope key formats you can use in your applications. Familiarize yourself with them now. The cookbook uses one of them in a later tutorial.
Impersonate authenticated user
Format:
secret[:child_db]:@doc/collection/id
Field | Description |
---|---|
|
A key secret. An |
|
Name of a child database. This is optional. When included, the |
|
Name of a collection in the current database or if |
|
The document ID for the document to authorize as. Document IDs are string-encoded 64-bit integers. |
This kind of scoped key is used to impersonate a user because the key has the same privileges as the authenticated user document, for example:
fnACysRJGIACAHiL_5f0UxHlPFIZgq876ptMNJ72:@doc/users/1234
Impersonate user by role
Format:
secret[:child_db]:@role/name
This kind of scoped key is used to impersonate a user with the privileges of the role.
Field | Description |
---|---|
|
A key secret. An |
|
Name of a child database. This is optional. When included, the |
|
The name of a role to authorize as. |
For example, this key has the same privileges as a member document
with the developers
role:
fnACysRJGIACAHiL_5f0UxHlPFIZgq876ptMNJ72:@role/developers
Is this article helpful?
Tell Fauna how the article can be improved:
Visit Fauna's forums
or email docs@fauna.com
Thank you for your feedback!