neo4jjwtneo4j-graphql-js

Can't manage to use JWT authentication/authorization with Neo4J-GraphQL


I'm trying to get Neo4J + graphQL lib 4.0.0 + authentication/authorization via JWT. I managed to do it with graphQL 3.0.0 but can't now update to 4.0.0.

My base code relates on the documentation OGM/Examples/Custom Resolvers

I get the following error:

"message": "Type mismatch for parameter 'jwt': expected Map, Node, Relationship, Point, Duration, Date, Time, LocalTime, LocalDateTime or DateTime but was String (line 3, column 87 (offset: 113))\n\"WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND this.id = coalesce($jwt.sub, $jwtDefault)), \"@neo4j/graphql/FORBIDDEN\", [0])\"\

This seems to be related to APOC translation.

I have the following in my schema:

type JWT @jwt {
    roles: [String!]!
}

My base type for User is:

type User {
  id: ID @id
  username: String!
  password: String! @private
  roles: [String!]
}

I tried different simple things in the schema:

type User @authorization(validate: [{ when: [BEFORE], where: {node: {id: "$jwt.sub" } } }]){
# or
type User @authorization(validate: [
    { where: { node: { id: "$jwt.sub" } } }
    { where: { jwt: { roles_INCLUDES: "admin" } } }
]) {
# or
type User @authentication(operations: [DELETE], jwt: { roles_INCLUDES: "admin" }){

When querying

query Users {
  users {
    username
    roles
  }
}

with header

{
  "Authorization" : "Bearer <jwt>"
}

I get the following error:

"message": "Type mismatch for parameter 'jwt': expected Map, Node, Relationship, Point, Duration, Date, Time, LocalTime, LocalDateTime or DateTime but was String (line 3, column 87 (offset: 113))\n\"WHERE apoc.util.validatePredicate(NOT ($isAuthenticated = true AND this.id = coalesce($jwt.sub, $jwtDefault)), \"@neo4j/graphql/FORBIDDEN\", [0])\"\

This seems to be related to APOC translation.

My server is as follows:

const neoSchema = new Neo4jGraphQL({
    typeDefs,
    driver,
    resolvers,
    features: {
        authorization: {
            key: signingKey,
        }
     }
});

Promise.all([neoSchema.getSchema(), ogm.init()]).then(([schema]) => {
    const server = new ApolloServer({
        schema,
        });

        startStandaloneServer(server, {
            context: async ({ req }) => {
                //console.log(req.headers);
                return { jwt: req.headers.authorization }},
        }).then(({ url }) =>  {
            console.log(`🚀 Server ready at ${url}`);
        });
});

Any help would be greatly appreciated!


Solution

  • I looks like you're trying to decode the JWT with the GraphQL library since you have the below in the library config.

    features: {
        authorization: {
            key: signingKey,
        }
    }
    

    Also, if the token you're setting to the jwt field on the request context is encoded, that would explain why the error gets thrown about it being a string.

    The documentation here https://neo4j.com/docs/graphql-manual/current/authentication-and-authorization/configuration/ says

    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.

    You should pass the encoded token into the request context on the token field.

    Try setting the request context with return { token: req.headers.authorization }} instead.