Bind

Use bind to ensure that on creating or updating nodes, a connection exists between a value on the JWT vs a property on a matched node. This validation is done after the operation but inside a transaction. Taking a closer look, let’s put a user in our database;

CREATE (:User {id:"user1", name: "one"})
type User {
    id: ID!
    name: String!
}

Given the above GraphQL type definitions - How can we restrict user1 from changing their ID?

type User {
    id: ID!
    name: String!
}

extend type User @auth(
    rules: [
        {
            operations: [UPDATE],
            bind: { id: "$jwt.sub" }
        }
    ]
)

After we update or create the node we validate that the property on the node is equal to the jwt.sub property. This validation is done in Cypher with function apoc.util.validate

Given user1 has the decoded JWT;

{
  "sub": "user1",
  "iat": 1516239022
}

With this JWT makes a GraphQL mutation to update their ID to someone else;

mutation {
    updateUsers(where: { id: "user1" }, update: { id: "user2" }) {
        users {
            name
        }
    }
}

The generated cypher for this query would look like the below, Throwing us out of the operation because the ids do not match.

MATCH (u:User {id: "user1"})
SET u.id = "user2"
CALL apoc.util.validate(NOT(u.id = "user1"), "Forbidden")
RETURN u

Bind is used on the following operations;

  1. create

  2. update

  3. connect

  4. disconnect

  5. delete

1. bind Across Relationships

There may be a reason where you need to traverse across relationships to satisfy your Auth implementation. One example of this could be "Ensure that users only create Posts related to themselves";

type User {
    id: ID
    name: String
}

type Post {
    content: String
    creator: User @relationship(type: "HAS_POST", direction: IN)
}

extend type Post @auth(rules: [
    { operations: [CREATE], bind: { creator: { id: "$jwt.sub" } } }
])

When you specify bind on a relationship you can select fields on the referenced node. It’s worth pointing out that allow on a relationship will perform an ALL on the matched nodes; to see if there is a match. This means you can only use bind to enforce a single relationship to a single node.

1.1. Field Level bind

You can use bind on a field. The root is still considered the node. Taking the example at the start of this bind section; you could do the following;

type User {
    id: ID! @auth(rules: [{ operations: [UPDATE], bind: { id: "$jwt.sub" } }])
    name: String!
}