Skip to content

Commit 9932cda

Browse files
committed
WiP new type defs
1 parent efd8267 commit 9932cda

File tree

1 file changed

+182
-12
lines changed

1 file changed

+182
-12
lines changed

modules/ROOT/pages/security/securing-a-graphql-api.adoc

Lines changed: 182 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,149 @@
22
:description: This page is a tutorial on how to secure your API created with the Neo4j GraphQL Library.
33
= Securing your GraphQL API
44

5-
Lorem ipsum.
5+
This page is a tutorial on how to secure your API created with the Neo4j GraphQL Library.
66

77

88
== Prerequisites
99

10-
Set up a new AuraDB instance.
10+
. Set up a new AuraDB instance.
1111
Refer to link:https://neo4j.com/docs/aura/getting-started/create-instance/[Creating a Neo4j Aura instance].
12+
. Populate the instance with the Northwind data set.
13+
14+
[NOTE]
15+
====
16+
If you have completed the GraphQL and Aura Console getting started guide and would like to get rid of the example nodes you have created there, run the following in **Query** before populating your data base with the Northwind set:
17+
[source,cypher]
18+
----
19+
MATCH (n) DETACH DELETE n;
20+
----
21+
====
1222

23+
This tutorial is built on top of the xref:northwind-api.adoc[Northwind API tutorial].
24+
Specifically, it will extend the following type definitions:
25+
26+
[source, graphql, indent=0]
27+
----
28+
type Customer @node {
29+
contactName: String!
30+
customerID: ID! @id
31+
orders: [Order!]! @relationship(type: "PURCHASED", direction: OUT)
32+
}
33+
type Order @node {
34+
orderID: ID! @id
35+
customer: [Customer!]! @relationship(type: "PURCHASED", direction: IN)
36+
products: [Product!]! @relationship(type: "ORDERS", direction: OUT, properties: "ordersProperties")
37+
}
38+
type Product @node {
39+
productName: String!
40+
category: [Category!]! @relationship(type: "PART_OF", direction: OUT)
41+
orders: [Product!]! @relationship(type: "ORDERS", direction: IN, properties: "ordersProperties")
42+
supplier: [Supplier!]! @relationship(type: "SUPPLIES", direction: IN)
43+
}
44+
type Category @node {
45+
categoryName: String!
46+
products: [Product!]! @relationship(type: "PART_OF", direction: IN)
47+
}
48+
type Supplier @node {
49+
supplierID: ID! @id
50+
companyName: String!
51+
products: [Product!]! @relationship(type: "SUPPLIES", direction: OUT)
52+
}
53+
type ordersProperties @relationshipProperties {
54+
unitPrice: Float!
55+
quantity: Int!
56+
}
57+
----
1358

1459
== Security-related directives
1560

16-
Lorem ipsum.
61+
The GraphQL Library has several directives dedicated to security: `@authentication` and `@authorization`, as well as `@jwt` and `@jwtClaim`.
1762

1863

1964
=== Authentication
2065

2166
The xref:security/authentication.adoc[`@authentication` directive] can be applied globally, only to certain fields or only to certain types, and only for certain operations.
2267

68+
Add admin authorization for operations on customers, orders, products, categories and suppliers:
69+
70+
* `DELETE` for customers,
71+
* `UPDATE` and `DELETE` for orders,
72+
* `CREATE`, `UPDATE` and `DELETE` for products, categories and suppliers.
73+
74+
[source, graphql, indent=0]
75+
----
76+
type Customer
77+
@node
78+
@authentication(operations: [DELETE], jwt: { roles: { includes: "admin" } })
79+
@authorization(
80+
filter: [
81+
{ operations: [READ], where: { node: { customerID: { eq: "$jwt.customerID" } } } }
82+
{ where: { jwt: { roles: { includes: "admin" } } } }
83+
]
84+
) {
85+
contactName: String!
86+
sensitiveData: String! @selectable(onRead: false, onAggregate: false)
87+
createdAt: DateTime! @settable(onCreate: true, onUpdate: false)
88+
adminNotes: [String!]! @authorization(validate: [{ where: { jwt: { roles: { includes: "admin" } } } }])
89+
customerID: ID! @id
90+
orders: [Order!]! @relationship(type: "PURCHASED", direction: OUT)
91+
}
92+
93+
type Order
94+
@node
95+
@authentication(operations: [UPDATE, DELETE], jwt: { roles: { includes: "admin" } })
96+
@authorization(
97+
filter: [
98+
{ where: { node: { customer: { all: { customerID: { eq: "$jwt.customerID" } } } } } }
99+
{ where: { jwt: { roles: { includes: "admin" } } } }
100+
]
101+
) {
102+
orderID: ID! @id
103+
customer: [Customer!]! @relationship(type: "PURCHASED", direction: IN)
104+
products: [Product!]! @relationship(type: "ORDERS", direction: OUT, properties: "ordersProperties")
105+
}
106+
107+
type Product @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
108+
productName: String!
109+
category: [Category!]! @relationship(type: "PART_OF", direction: OUT)
110+
orders: [Product!]! @relationship(type: "ORDERS", direction: IN, properties: "ordersProperties")
111+
supplier: [Supplier!]! @relationship(type: "SUPPLIES", direction: IN)
112+
}
113+
114+
type Category @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
115+
categoryName: String!
116+
products: [Product!]! @relationship(type: "PART_OF", direction: IN)
117+
}
118+
119+
type Supplier @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
120+
supplierID: ID! @id
121+
companyName: String!
122+
products: [Product!]! @relationship(type: "SUPPLIES", direction: OUT)
123+
}
124+
----
23125

24126
==== JSON Web Token (JWT) authentication
25127

26128
JWT authentication is a popular method for token-based authentication.
27129
It allows clients to obtain and use tokens to authenticate subsequent requests.
28130

29-
JWT are represent encoded JSON data.
131+
JWT are represented by encoded JSON data.
30132
These data can have arbitrary fields - which ones they should contain depends on the application preferences.
31-
For instance, if the server side is trying to parse a field `roles_INCLUDES`, then the JWT should contain that.
133+
For instance, if the server side is trying to parse a field `roles`, then the JWT should contain that.
134+
With `@jwtClaim`, you can specify a path to a customer ID in a nested location.
135+
Here is an example:
32136

33137
[source, graphql, indent=0]
34138
----
35-
//Example
139+
type JWT @jwt {
140+
roles: [String!]!
141+
customerID: String! @jwtClaim(path: "sub")
142+
}
36143
----
37144

38145
You can encode and decode JWT using a site like link:https://www.jwt.io/[https://www.jwt.io/].
39146

40147

41-
//==== Other methods?
42-
43-
//Lorem ipsum.
44-
45-
46148
=== Authorization
47149

48150
The xref:security/authorization.adoc[`@authorization` directive] can either be used to filter out data which users should not have access to or throw an error if a query is executed against such data.
@@ -258,4 +360,72 @@ Follow the input validation methods summarized in the link:https://cheatsheetser
258360

259361
== Further reading
260362

261-
Neo4j has a link:https://neo4j.com/docs/operations-manual/current/authentication-authorization/manage-privileges/[Role-based access control] mechanism that can be leveraged to increase security even further.
363+
Neo4j has a link:https://neo4j.com/docs/operations-manual/current/authentication-authorization/manage-privileges/[Role-based access control] mechanism that can be leveraged to increase security even further.
364+
365+
366+
367+
368+
369+
370+
371+
[source, graphql, indent=0]
372+
----
373+
type JWT @jwt {
374+
roles: [String!]!
375+
customerID: String! @jwtClaim(path: "sub")
376+
}
377+
378+
type Customer
379+
@node
380+
@authentication(operations: [DELETE], jwt: { roles: { includes: "admin" } })
381+
@authorization(
382+
filter: [
383+
{ operations: [READ], where: { node: { customerID: { eq: "$jwt.customerID" } } } }
384+
{ where: { jwt: { roles: { includes: "admin" } } } }
385+
]
386+
) {
387+
contactName: String!
388+
sensitiveData: String! @selectable(onRead: false, onAggregate: false)
389+
createdAt: DateTime! @settable(onCreate: true, onUpdate: false)
390+
adminNotes: [String!]! @authorization(validate: [{ where: { jwt: { roles: { includes: "admin" } } } }])
391+
customerID: ID! @id
392+
orders: [Order!]! @relationship(type: "PURCHASED", direction: OUT)
393+
}
394+
395+
type Order
396+
@node
397+
@authentication(operations: [UPDATE, DELETE], jwt: { roles: { includes: "admin" } })
398+
@authorization(
399+
filter: [
400+
{ where: { node: { customer: { all: { customerID: { eq: "$jwt.customerID" } } } } } }
401+
{ where: { jwt: { roles: { includes: "admin" } } } }
402+
]
403+
) {
404+
orderID: ID! @id
405+
customer: [Customer!]! @relationship(type: "PURCHASED", direction: IN)
406+
products: [Product!]! @relationship(type: "ORDERS", direction: OUT, properties: "ordersProperties")
407+
}
408+
409+
type Product @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
410+
productName: String!
411+
category: [Category!]! @relationship(type: "PART_OF", direction: OUT)
412+
orders: [Product!]! @relationship(type: "ORDERS", direction: IN, properties: "ordersProperties")
413+
supplier: [Supplier!]! @relationship(type: "SUPPLIES", direction: IN)
414+
}
415+
416+
type Category @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
417+
categoryName: String!
418+
products: [Product!]! @relationship(type: "PART_OF", direction: IN)
419+
}
420+
421+
type Supplier @node @authentication(operations: [CREATE, UPDATE, DELETE], jwt: { roles: { includes: "admin" } }) {
422+
supplierID: ID! @id
423+
companyName: String!
424+
products: [Product!]! @relationship(type: "SUPPLIES", direction: OUT)
425+
}
426+
427+
type ordersProperties @relationshipProperties {
428+
unitPrice: Float!
429+
quantity: Int!
430+
}
431+
----

0 commit comments

Comments
 (0)