Use Singleton using User secret

Hello !

I’ve been trying to use a simple query with a user secret (this query is made simple, I’m actually not using Get/Singleton to get a Ref data !

const response = await fauna.query(
  q.Get(
    q.Singleton(q.Ref(q.Collection('lessons'), '259055206731350528')),
  ),
  { secret },
);

And end up with {"message":"Insufficient privileges to perform the action."}.

If I remove the secret & use the server or admin, it is working fine. Is there any privileges I need to give to the user to Get | Paginate over Singleton ? Thanks :wink:

Tokens do not provide any privileges by default, whereas an admin or server key would. Likely, you need to create a role that grants the appropriate privileges to the token being used.

For the query example that you included, the Singleton function isn’t doing anything for you. A specific ref doesn’t need to be turned into a set before Get can be used to fetch the ref’s contents. If you do have a set with multiple items, Get can be used to fetch the first item from the set (first is based on the set’s lexical ordering). For example, Get(Documents(Collection("users"))).

Thanks for your answer @ewan, the user already has privileges to the lessons collection & the used Indexes. The issue is really about Singleton, I will try to show the full example.

const match = req.query.id
 ? q.Singleton(q.Ref(q.Collection('lessons'), req.query.id))
 : q.Match(q.Index('lessonsFilterByPublished'), true);

const data = await fauna.query(
  Paginate(
    Join(
      match,
      lesson => Match(Index("itemsByLesson"), [q.CurrentIdentity(), lesson])
    )
  ),
  { secret }
)

The match changes depending on giving an id or not.
So I’m using Singleton to transform the Ref into a SetRef that Join can consume.

The query itself works if I’m giving the userRef instead of secret + CurrentIdentity().
I ended up showing that even a simple query like :

q.Get(
  q.Singleton(q.Ref(q.Collection('lessons'), '259055206731350528')),
)

Was not working with a secret. Even if the user has the privilege to read the lessons Collection.

Any idea to proceed ? Thanks

1 Like

I’ve been able to reproduce the problem, which seems like it might be a bug, or limitation, of Roles interacting with Singleton sets. I’m asking our engineering team about this, and hope to be able to report something useful soon.

1 Like

Hey @ewan, any news on this situation ?

I found out also that a simple query like : Get(CurrentToken()) called with a token that comes from user membership doesn’t work. (Which is normal I guess)

  • I need to put it inside an UDF
  • Assign admin or role server to the UDF
  • Add privilege to user Role to enable the UDF to be called

Is there an other way ? Do we have to create an UDF for the Get(CurrentToken()) + server role to be run using user Token ? I feel like it’s related to the above issue. Queries that need privilege escalation must be inside an UDF with a role that can run it.

Thank you !

Sorry for the delay.

Internally, the Role membership determination uses a document reference (for a document, index, etc.). Using Singleton adds a layer of abstraction that the Role logic is unable to penetrate. The conclusion is that you’re not going to be able to make this work with tokens. To continue with your approach, you’d have to use a key with the admin or server role.

That all seems correct.

When using a token to run a query, even if no Role provides any other access, CurrentToken returns the reference to the current token. Fetching the document associated with that reference requires a granted privilege.

Role privileges are not transitive: you could grant read permissions to Tokens() which is the internal collection containing Token document, but that doesn’t provide read access to all Token documents; it would be a potential security issue if it did.

So, a UDF that has higher privileges than the identity making the call, and granting permission to call that UDF, are required steps.

That is distinct from the Singleton problem, where the identity document is, essentially, disconnected from Role membership.

Note that CurrentIdentity returns the reference to the current identity document, which might be more straightforward than using CurrentToken, depending on your use case.

This is what I came up with indeed ! I Just need a second query to get the CurrentIdentity => Ref first but well, nothing’s perfect.

I didn’t fully understand why there was roles on UDF but now I get it :slight_smile:

Thanks again for your answer, this confirms what I thought !

1 Like