Graphql web examples with user authorization?

Are there any examples out there that implement full user authorization with graphql mutations?

I’m having a hard time setting up a single collection to allow an authenticated user to:

  1. insert a new document
  2. see only their own documents

I would expect this to be fairly straightforward and would love to see how someone else has done it.

So far what I’ve managed is to:

insert a document when every permission is allowed
  • but I need authorization
insert a document when “insert” is true and the user_id field is ‘write = true’
    {
      "name": "insert",
      "apply_when": {},
      "insert": true,
      "delete": false,
      "search": false,
      "fields": {
        "_partition": {},
        "address": {},
        "description": {},
        "user_id": {
          "write": true
        },
      },
      "additional_fields": {}
    },

Adding the user_id field as write: true was necessary to get past an error: does not have insert permission for document with _id: ObjectID(\"5fc....\"): user_id cannot be written to"; code="ArgumentsNotAllowed";

But even with this I can’t seem to filter to just these records.

query {
  listings(
    query: {
      user_id: {
        user_id: "5f......"
      }
    }
    
  ){
    _id
  }
}

returns []; as does:

query {
  listings(
    query: {
      user_id: {
        _id: "5f..."
      }
    }
    
  ){
    _id
  }
}

I definitely see a matching record in the collection.
I’ve enabled “Search Documents” and marked all fields as Read: true (which I don’t want).

Insecurely, this setup also allows me to insert a document with any random made-up user-id.

In addition, despite checking Write for All additional fields, fields are not writable unless I specifically name them. You can see that here with address.

Before adding address to this I received: address cannot be written to"; code="ArgumentsNotAllowed"

Hey Travis,

Just noticed you have a “_partition” field in your schema. Do you happen to be using Sync simultaneously? In that case your Rules will actually have to be set in the Sync section of the UI https://docs.mongodb.com/realm/mongodb/define-roles-and-permissions/

If that’s not the case, do you mind linking your app url here so I can take a look at your permissions. Generally for your use-case you would set the “apply when” to something like this:

https://docs.mongodb.com/realm/mongodb/query-roles/#the-user-is-the-document-owner

and check “insert” and “read”

1 Like

Hi Sumedha, I’m not using sync and I don’t think I’m correctly using the _partition field. Its just something I added based on some of the docs. I am always sending _partition: "ff" to keep it happy, but haven’t made any further use of it. I am only using authentication and Graphql. Do you think I should remove it?

I was using "<Owner ID Field>": "%%user.id" initially but changed to using a function based on this thread.
I had it setup as you describe.

Is it safe to post the app-id here - isn’t that the only secret for a web app? Wouldn’t posting it make it usable by any passers-by?

A few things:

  • . _partition is a field that is required for Sync. If any part of the documentation made this unclear or hard to understand, let us know so we can fix it

  • I responded to the user.id issue in another thread. Let me know if that worked (permissions for allowing users to only see their document).

  • for insert a document, you can use the same isOwner role and check “insert” (as you already have). The other insert role may be unnecessary unless you add a separate “apply when” expression.

1 Like

Thanks for your help Sumeda.

I responded to the user.id issue in another thread. Let me know if that worked (permissions for allowing users to only see their document).

That did help. I have a field of user_id that is an ObjectId. In the other thread the meaning of root was unclear to me. Now I know that it is the top of the document being evaluated, regardless of whether that document is being read from the backend or coming from a graphql mutation and being evaluated for potential insert.

For anyone else following along please check the other thread if trying to get object authorization working.

Since resolving that I was able to remove all the other permissions (as you suggested) which were just there to allow me to keep going on the front-end until this could be resolved.

. _partition is a field that is required for Sync. If any part of the documentation made this unclear or hard to understand, let us know so we can fix it

I have removed the _partition field. I put this in very early on - not sure which document I was reading at that time.

It would be really helpful if there were a working sample app that implemented authorization using any major web framework.

So I thought I had this working, but as soon as I wanted to allow some fields to be public I ran back into these issues.

I added a new role that has “read” for some fields and put it to the right of the existing role. This worked and I could now retrieve all records from any user using graphql.
However, I don’t want to always get all of them, I want an individual user to see their own records only most of the time. To that end I modified my graphql query to filter by the user’s own ID:

query MyListings($userId: ObjectId!) {
  listings(query: {
    user_id: {
      _id: $userId
    }
  }) {
    _id
    description
  }
}

This unfortunately returns no results:

{
  "data": {
    "listings": []
  }
}

And yes, I’m sure the userID is correct and on the records. I copy-pasted it and even altered some records to have their user_id set to ffffffffffffffffffffffff and ran it with that - no luck.

To validate that I’m using the query-input types correctly I also ran this query which did return results:

query ListingsWithDescription($description: String!) {
  listings(query: {
    description: $description
  }) {
    _id
    description
  }
}

Not sure where to go from here, this seems like a continuation of the initial problem. I’ve been unable to resolve it.

ObjectId clearly behaves differently as indicated by the other thread - but its not clear how to filter on an ObjectId field.
How can I filter to a specific user’s listings?