Bind
Use bind to ensure that on creating or updating nodes, there is equality between a value on the JWT and a property on a matched node. This validation is done after the operation but inside a transaction. Taking a closer look, create a user in your database:
CREATE (:User { id:"user1", name: "one" })
For the label and properties of the node created above, the corresponding GraphQL type definitions would be:
type User {
id: ID!
name: String!
}
Given the above GraphQL type definition - you could restrict user1
from changing their own ID:
type User {
id: ID!
name: String!
}
extend type User @auth(
rules: [
{
operations: [UPDATE],
bind: { id: "$jwt.sub" }
}
]
)
After the update or creation of the node, it is validated that the property id
on the node is equal to the jwt.sub
property.
Given user1
has the following decoded JWT:
{
"sub": "user1",
"iat": 1516239022
}
When the user makes a request using this JWT to change their ID:
mutation {
updateUsers(where: { id: "user1" }, update: { id: "user2" }) {
users {
name
}
}
}
The generated cypher for this query would look like the below, throwing you out of the operation because the id
property no longer matches.
MATCH (u:User { id: "user1" })
SET u.id = "user2"
CALL apoc.util.validate(NOT (u.id = "user1"), "Forbidden")
RETURN u
Bind is available for the following operations;
-
READ
-
UPDATE
-
CONNECT
-
DISCONNECT
-
DELETE
bind
across relationships
There may be a reason where you need to traverse across relationships to satisfy your authorization implementation. One use case 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 related node. It’s worth pointing out that bind
on a relationship field will perform an all
on the matched nodes to see if there is a match, or any
if the bindPredicate
option of the plugin has been set to "any".
Field-level bind
You can use bind
on a field, and the root is still considered the node itself. Taking the example at the start of this chapter, you could do the following to implement the same behaviour:
type User {
id: ID! @auth(rules: [{ operations: [UPDATE], bind: { id: "$jwt.sub" } }])
name: String!
}
Was this page helpful?