-
Notifications
You must be signed in to change notification settings - Fork 15
Updates for the security section #168
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
Changes from 3 commits
b57242c
b34ea6a
5ddefe4
381f006
c1c90c1
4cd684e
9658364
c6fdc97
4c283fc
127569c
c51c458
12648b2
4a408be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,28 @@ | ||
| = Configuration | ||
| :description: This page describes how to configure authentication and authorization features in the Neo4j GraphQL Library. | ||
|
|
||
| The Neo4j GraphQL Library uses JSON Web Token (JWT) authentication. | ||
| JWTs are tokens containing claims or statements about the user or client making the request. | ||
| These claims can include information such as the user's ID or roles. | ||
|
|
||
| When a user or client logs in to the API, the API generates a JWT and returns it to the client. | ||
| The client then includes the JWT with each subsequent request to the API. | ||
| The API verifies the JWT and returns the requested data if the JWT is valid. | ||
|
|
||
| // ^ is this paragraph accurate? | ||
|
|
||
| == Instantiation | ||
|
|
||
| The Neo4j GraphQL Library can accept JSON Web Tokens via two mechanisms: | ||
| The Neo4j GraphQL Library can accept two types of JWTs: | ||
|
|
||
| * Encoded JWTs in the `token` field of the request context. | ||
| * Decoded JWTs in the `jwt` field of the request context. | ||
|
|
||
| If using the former, the library will need to be configured with a key to decode and verify the token. | ||
| === Encoded JWTs | ||
|
|
||
| The following code block demonstrates using Apollo Server, extracting the `Authorization` header from the request, and putting it into the appropriate context field: | ||
| To use encoded JWTs, the library must to be configured with a key to decode and verify the tokens. | ||
|
||
|
|
||
| The following code block uses Apollo Server, extracts the `Authorization` header from the request and puts it in the appropriate context field: | ||
rsill-neo4j marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source, typescript, indent=0] | ||
| ---- | ||
|
|
@@ -28,10 +40,14 @@ const { url } = await startStandaloneServer(server, { | |
|
|
||
| Optionally, if a custom decoding mechanism is required, that same header can be decoded and the resulting JWT payload put into the `jwt` field of the context. | ||
|
|
||
| === Symmetric secret | ||
| // ^ Can we show the above in a code listing? | ||
rsill-neo4j marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ==== Symmetric secret | ||
|
|
||
| To configure the library with a symmetric secret (e.g. "secret"), the following instantiation is required: | ||
|
|
||
| // ^ What is a symmetric secret? What is its purpose? | ||
rsill-neo4j marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source, typescript, indent=0] | ||
| ---- | ||
| new Neo4jGraphQL({ | ||
|
|
@@ -44,9 +60,11 @@ new Neo4jGraphQL({ | |
| }); | ||
| ---- | ||
|
|
||
| === JWKS endpoint | ||
| ==== JWKS endpoint | ||
|
|
||
| To configure the library to verify tokens against a JWKS endpoint, "https://www.myapplication.com/.well-known/jwks.json", the following instantiation is required: | ||
| To configure the library to verify tokens against a JWKS endpoint, for example "https://www.myapplication.com/.well-known/jwks.json", the following instantiation is required: | ||
|
|
||
| // ^ What is the purpose? | ||
rsill-neo4j marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [source, typescript, indent=0] | ||
| ---- | ||
|
|
@@ -62,14 +80,66 @@ new Neo4jGraphQL({ | |
| }); | ||
| ---- | ||
|
|
||
| [[authentication-and-authorization-jwt]] | ||
| == JWT | ||
| ==== Passing in encoded JWTs | ||
|
|
||
| // This was at the end of the file, I thought it could be moved here instead. What about decoded JWTs? | ||
rsill-neo4j marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| To pass in an encoded JWT, use the token field of the context. | ||
| When using Apollo Server, extract the authorization header into the token property of the context: | ||
|
|
||
| [source, javascript, indent=0] | ||
| ---- | ||
| const server = new ApolloServer({ | ||
| schema, | ||
| }); | ||
|
|
||
| await startStandaloneServer(server, { | ||
| context: async ({ req }) => ({ token: req.headers.authorization }), | ||
| }); | ||
| ---- | ||
|
|
||
| For example, a HTTP request with the following `authorization` header should look like this: | ||
|
|
||
| [source] | ||
| ---- | ||
| POST / HTTP/1.1 | ||
| authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJ1c2VyX2FkbWluIiwicG9zdF9hZG1pbiIsImdyb3VwX2FkbWluIl19.IY0LWqgHcjEtOsOw60mqKazhuRFKroSXFQkpCtWpgQI | ||
| content-type: application/json | ||
| ---- | ||
|
|
||
| Alternatively, you can pass a key `jwt` of type `JwtPayload` into the context, which has the following definition: | ||
|
|
||
| [source, typescript, indent=0] | ||
| ---- | ||
| // standard claims https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 | ||
| interface JwtPayload { | ||
| [key: string]: any; | ||
| iss?: string | undefined; | ||
| sub?: string | undefined; | ||
| aud?: string | string[] | undefined; | ||
| exp?: number | undefined; | ||
| nbf?: number | undefined; | ||
| iat?: number | undefined; | ||
| jti?: string | undefined; | ||
| } | ||
| ---- | ||
|
|
||
| [WARNING] | ||
| Do not pass in the header or the signature. | ||
rsill-neo4j marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| === Decoded JWTs | ||
|
|
||
| // What could be added here? | ||
|
||
|
|
||
rsill-neo4j marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| == Adding JWT claims | ||
|
|
||
| By default, filtering is available on https://www.rfc-editor.org/rfc/rfc7519#section-4.1[the registered claim names] in the JWT specification. | ||
|
|
||
| Filtering can be configured for additional JWT claims using the `@jwt` directive and, in some circumstances, the `@jwtClaim` directive. | ||
|
|
||
| If you configure an additional `roles` claim, which is an array of strings located at the root of the JWT payload, the following must be added to the type definitions: | ||
| === The `@jwt` directive | ||
|
|
||
| If you configure an additional `roles` claim, which is an array of strings located at the root of the JWT payload, add the following to the type definitions: | ||
|
|
||
| [source, graphql, indent=0] | ||
| ---- | ||
|
|
@@ -78,11 +148,12 @@ type JWT @jwt { | |
| } | ||
| ---- | ||
|
|
||
| Note that the type name here, `JWT`, is not required, and this can have any name as long as it is decorated with the `@jwt` directive. | ||
| Note that the type name `JWT` is not required. | ||
rsill-neo4j marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| You can use any name as long as it is decorated with the `@jwt` directive. | ||
rsill-neo4j marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| === Nested claims | ||
| === The `@jwtClaim` directive | ||
|
|
||
| If the previous `roles` claim is not located at the JWT payload root, but instead in a nested location, for example: | ||
| A `roles` claim is not necessarily located at the JWT payload root, but can instead be in a nested location, for example: | ||
|
|
||
| [source, json, indent=0] | ||
| ---- | ||
|
|
@@ -94,7 +165,9 @@ If the previous `roles` claim is not located at the JWT payload root, but instea | |
| } | ||
| ---- | ||
|
|
||
| This needs to be configured using the `@jwtClaim` directive: | ||
| // ^ why is this a nested location? can we show the nesting? | ||
rsill-neo4j marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| In this case, use the `@jwtClaim` directive: | ||
|
|
||
| [source, graphql, indent=0] | ||
| ---- | ||
|
|
@@ -103,7 +176,7 @@ type JWT @jwt { | |
| } | ||
| ---- | ||
|
|
||
| Additionally, if this nested location contains any `.` characters in the path, for example: | ||
| Additionally, the nested location may contain `.` characters in the path, for example: | ||
|
|
||
| [source, json, indent=0] | ||
| ---- | ||
|
|
@@ -115,7 +188,7 @@ Additionally, if this nested location contains any `.` characters in the path, f | |
| } | ||
| ---- | ||
|
|
||
| These characters need to be escaped: | ||
| Escape these characters: | ||
|
||
|
|
||
| [source, graphql, indent=0] | ||
| ---- | ||
|
|
@@ -126,50 +199,6 @@ type JWT @jwt { | |
|
|
||
| [NOTE] | ||
| ==== | ||
| The seemingly excessive escaping is required to doubly escape: once for GraphQL and once for `dot-prop`, which is used under the hood to resolve the path. | ||
| This way of escaping is necessary to escape twice: once for GraphQL and once for `dot-prop`, which is used under the hood to resolve the path. | ||
| ==== | ||
|
|
||
| == Passing in JWTs | ||
|
|
||
| To pass in an encoded JWT, you must use the token field of the context. | ||
| When using Apollo Server, extract the authorization header into the token property of the context as follows: | ||
|
|
||
| [source, javascript, indent=0] | ||
| ---- | ||
| const server = new ApolloServer({ | ||
| schema, | ||
| }); | ||
|
|
||
| await startStandaloneServer(server, { | ||
| context: async ({ req }) => ({ token: req.headers.authorization }), | ||
| }); | ||
| ---- | ||
|
|
||
| For example, a HTTP request with the following `authorization` header should look like this: | ||
|
|
||
| [source] | ||
| ---- | ||
| POST / HTTP/1.1 | ||
| authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJ1c2VyX2FkbWluIiwicG9zdF9hZG1pbiIsImdyb3VwX2FkbWluIl19.IY0LWqgHcjEtOsOw60mqKazhuRFKroSXFQkpCtWpgQI | ||
| content-type: application/json | ||
| ---- | ||
|
|
||
| Alternatively, you can pass a key `jwt` of type `JwtPayload` into the context, which has the following definition: | ||
|
|
||
| [source, typescript, indent=0] | ||
| ---- | ||
| // standard claims https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 | ||
| interface JwtPayload { | ||
| [key: string]: any; | ||
| iss?: string | undefined; | ||
| sub?: string | undefined; | ||
| aud?: string | string[] | undefined; | ||
| exp?: number | undefined; | ||
| nbf?: number | undefined; | ||
| iat?: number | undefined; | ||
| jti?: string | undefined; | ||
| } | ||
| ---- | ||
|
|
||
| [WARNING] | ||
| _Do not_ pass in the header or the signature. | ||
Uh oh!
There was an error while loading. Please reload this page.