Configuration
Instantiation
The Neo4j GraphQL Library can accept JSON Web Tokens via two mechanisms:
-
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.
The following code block demonstrates using Apollo Server, extracting the Authorization
header from the request, and putting it into the appropriate context field:
const server = new ApolloServer({
schema, // schema from Neo4jGraphQL.getSchema()
});
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
context: async ({ req }) => ({
token: req.headers.authorization,
}),
});
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
To configure the library with a symmetric secret (e.g. "secret"), the following instantiation is required:
new Neo4jGraphQL({
typeDefs,
features: {
authorization: {
key: "secret",
},
},
});
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:
new Neo4jGraphQL({
typeDefs,
features: {
authorization: {
key: {
url: "https://www.myapplication.com/.well-known/jwks.json"
},
},
},
});
JWT
By default, filtering is available on 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:
type JWT @jwt {
roles: [String!]!
}
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.
Nested claims
If the previous roles
claim is not located at the JWT payload root, but instead in a nested location, for example:
{
"sub": "user1234",
"myApplication": {
"roles": ["user", "admin"]
}
}
This needs to be configured using the @jwtClaim
directive:
type JWT @jwt {
roles: [String!]! @jwtClaim(path: "myApplication.roles")
}
Additionally, if this nested location contains any .
characters in the path, for example:
{
"sub": "user1234",
"http://www.myapplication.com": {
"roles": ["user", "admin"]
}
}
These characters need to be escaped:
type JWT @jwt {
roles: [String!]! @jwtClaim(path: "http://www\\\\.myapplication\\\\.com.roles")
}
The seemingly excessive escaping is required to doubly escape: once for GraphQL and once for |
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:
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:
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:
// 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;
}
Do not pass in the header or the signature. |