Skip to content

cherry-pick authorization docs to 6.x #245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions modules/ROOT/pages/security/authentication.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Authentication can be configured for an entire type:

[source, graphql, indent=0]
----
type User @authentication {
type User @authentication @node {
id: ID!
name: String!
password: String!
Expand All @@ -89,7 +89,7 @@ Authentication can be configured on a per-field basis, for example:

[source, graphql, indent=0]
----
type User {
type User @node {
id: ID!
name: String!
password: String! @authentication
Expand Down
127 changes: 124 additions & 3 deletions modules/ROOT/pages/security/authorization.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ For instance, to only require filtering for the reading and aggregating posts:
[source, graphql, indent=0]
----
type Post @node @authorization(filter: [
{ operations: [READ, AGGREGATE] where: { node: { author: { id_EQ: "$jwt.sub" } } } }
{ operations: [READ, AGGREGATE], where: { node: { author: { id_EQ: "$jwt.sub" } } } }
]) {
title: String!
content: String!
Expand Down Expand Up @@ -106,11 +106,10 @@ Validation can be configured to only be performed on certain operations:

For instance, to only require validation for the update or deletion of a post:


[source, graphql, indent=0]
----
type Post @node @authorization(validate: [
{ operations: [UPDATE, DELETE] where: { node: { author: { id_EQ: "$jwt.sub" } } } }
{ operations: [UPDATE, DELETE], where: { node: { author: { id_EQ: "$jwt.sub" } } } }
]) {
title: String!
content: String!
Expand All @@ -123,6 +122,98 @@ type Post @node @authorization(validate: [
In case there is no `operations` argument with a list of operations, the GraphQL Library treats the authorization configuration as if the full list of operations had been provided.
====

=== When

Validation can be configured to only be performed before or after an operation is executed.
This is done using the `when` argument which accepts an array of the following values:

* `BEFORE`
* `AFTER`

Additionally, some operations only support validation either before or after them, which is summarised in this table:

[cols="2,5"]
|===
| `operation` | `when`

| `READ`
| `BEFORE`

| `AGGREGATE`
| `BEFORE`

| `CREATE`
| `AFTER`

| `UPDATE`
| `BEFORE`, `AFTER`

| `DELETE`
| `BEFORE`

| `CREATE_RELATIONSHIP`
| `BEFORE`, `AFTER`

| `DELETE_RELATIONSHIP`
| `BEFORE`, `AFTER`

|===

As an example, let's say you want someone to be able to update a post.
If you want to check that after the update the author of the post is still the current user, do the following:

[source, graphql, indent=0]
----
type Post @node @authorization(validate: [
{ operations: [UPDATE], when: [AFTER], where: { node: { author: { id: "$jwt.sub" } } } }
]) {
title: String!
content: String!
author: User! @relationship(type: "AUTHORED", direction: IN)
}
----

== Authorization on fields

The `@authorization` directive can be used either on object types or their fields, with the former being used in examples for the most part on this page.
When applied to a field, the authorization rules are only evaluated if the matching operations are performed on that field.
For example, consider a `User` type with a `password` field:

[source, graphql, indent=0]
----
type User @node {
id: ID!
username: String!
password: String! @authorization(where: [{ operations: [READ, UPDATE], where: { node: { id: "$jwt.sub" } } }])
}
----

When executing the following query, a valid identity is not needed:

[source, graphql, indent=0]
----
{
users {
username
}
}
----

However, consider the following query:

[source, graphql, indent=0]
----
{
users {
username
password
}
}
----

This will require a valid JWT to have been provided with the request, and the matching users will be filtered down according to the JWT subject.
The same applies for attempting to update the `password` field, the update will only apply to the user matching the JWT.


== Authorization without authentication

Expand All @@ -147,3 +238,33 @@ type Post @node @authorization(filter: [
author: User! @relationship(type: "AUTHORED", direction: IN)
}
----

== Ordering of rules

In each ruleset (`filter` and `validate`), rules are joined with an `OR`.
The two rulesets are joined with an `AND`.

For example, the following would allow for the update of a `User` node if the JWT roles claim includes `admin` _or_ if the `locked` property on the node is `false`:

[source, graphql, indent=0]
----
type User @node @authorization(validate: [
{ operations: [UPDATE], where: { jwt: { roles_INCLUDES: "admin" } } }
{ operations: [UPDATE], where: { node: { locked: false } } }
]) {
id: ID!
locked: Boolean!
}
----

If you want to combine the rule that a user must be an admin with the rule that the `locked` property must be `false` in order to update a `User` node, add them both to the `where` field using `AND` in a single rule:

[source, graphql, indent=0]
----
type User @node @authorization(validate: [
{ operations: [UPDATE], where: { AND: [{ jwt: { roles_INCLUDES: "admin" } }, { node: { locked: false } }] } }
]) {
id: ID!
locked: Boolean!
}
----
66 changes: 66 additions & 0 deletions modules/ROOT/pages/security/operations.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,70 @@ query {
}
----

=== Connection

For a connection query, rules with `READ` in the operations are evaluated for any type being read:

[source, graphql, indent=0]
----
query {
moviesConnection {
edges {
node { # READ ON OBJECT Movie
title # READ ON FIELD_DEFINITION Movie.title
actorsConnection {
edges {
node { # READ ON OBJECT Actor
name # READ ON FIELD_DEFINITION Actor.name
}
}
}
}
}
}
}
----

=== Aggregation

For an aggregation query, rules with `AGGREGATE` in the operations are evaluated for any type being aggregated:

[source, graphql, indent=0]
----
query {
moviesConnection {
aggregate { # AGGREGATE ON OBJECT Movie
title { # AGGREGATE ON FIELD_DEFINITION Movie.title
longest
}
}
}
}
----

The same logic applies to aggregations of nested nodes:

[source, graphql, indent=0]
----
query {
movies {
actorsConnection {
aggregate { # AGGREGATE ON OBJECT Actor
node {
name { # AGGREGATE ON FIELD_DEFINITION Actor.name
longest
}
}
}
}
}
}
----

== Mutation

=== Create

For `create` mutations, `CREATE` rules on the object are evaluated for each node created, as well as field definition rules:

[source, graphql, indent=0]
Expand All @@ -56,6 +118,8 @@ mutation {
}
----

=== Delete

For single `delete` mutations, rules with `DELETE` on the object are evaluated:

[source, graphql, indent=0]
Expand All @@ -81,6 +145,8 @@ mutation {
}
----

=== Update

For a complex `update` mutation with many effects, a variety of rules is evaluated, as well as `READ` rules for the selection set:

[source, graphql, indent=0]
Expand Down
3 changes: 2 additions & 1 deletion modules/ROOT/pages/security/subscriptions-authorization.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
= Subscriptions authorization

Subscriptions require their own authorization rules, which are configured with the `@subscriptionsAuthorization` directive.
These rules are different to authorization rules for queries and mutations because they use filtering rules available for subscriptions events.
These rules are different to authorization rules for queries and mutations because they use filtering rules available for subscriptions events.
These filtering rules can only be used to filter against the properties of the nodes impacted by the events.

All subscriptions authorization rules have an implied requirement for authentication, given that the rules are normally evaluated against values in the JWT payload.

Expand Down
3 changes: 3 additions & 0 deletions modules/ROOT/pages/types/relationships.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ You can add relationship properties to the example in two steps:
. Add a type definition decorated with the `@relationshipProperties` directive, containing the desired relationship properties.
. Add a `properties` argument to both "sides" (or just one side, if you prefer) of the `@relationship` directive which points to the newly defined interface.

Relationship properties fields can only be primitive types or their list variants.
You cannot map complex types such as object types into the types modelling relationship properties.

For example, suppose you want to distinguish which roles an actor played in a movie:

[source, graphql, indent=0]
Expand Down
Loading