Combining @secret and @auth directives

Hi,

We’re having some problems with combining the @secret and @auth directives which may very well be our lack of understanding.

We have the following type in a GraphQL schema to reproduce the problem:

type User @secret(field: "password") @auth(
  query: {
    rule: """
      query ($user: String!) {
        queryUser(filter: { username: { eq: $user } }) {
          __typename
        }
      }
    """
  }
)
{
  username: String! @id
  name: String!
}

(The actual @auth query-rule is not that important here but in the example above should allow only a caller with the “user” JWT claim set to the username of the node to see the information in it).

Our application uses self-registration so any user should be able to register a User without a JWT token. This works fine since there is no rule for the add case in the @auth-directive:

POST /graphql (without JWT in request)

mutation {
  addUser(input: [{
    username: "emily",
    password: "password",
    name: "Emily Blunt"
  }]) {
    numUids
  }
}

gives:

{
  "data": {
    "addUser": {
      "numUids": 1
    }
  }
}

Now, since the @secret password is not included during querying, and since the username is marked as the @id of the node we expected that we would be able to invoke the auto-generated checkUserPassword like so:

POST /graphql (without JWT in request)

{
  checkUserPassword (username: "emily", password: "password") {
    __typename
  }
}

without getting blocked by the @auth query-rule. But instead we get the following server error (also displayed in the Alpha log):

  "errors": [
    {
      "message": "Dgraph query failed because Dgraph execution failed because line 3 column 11: Expected Left round brackets. Got: lex.Item [6] \"{\" at 3:11"
    }
  ],
  "data": {
    "checkUserPassword": null
  }

Removing the @auth directive allows checkUserPassword to return. With a proper JWT token in the request we can also invoke checkUserPassword.

Since adding a new User and logging in is done without a JWT we don’t quite see how we can protect the User type with an @auth directive while at the same time allow our external @remote service to call checkUserPassword in order to generate a JWT for further GraphQL queries?

Thanks for your time.

1 Like

Looks like something goes wrong in the query rewriter when converting the GraphQL query to DQL.

Bumping up the logging level shows the following converted query in the Alpha log when an @auth directive is present alongside the @secret directive:

query {
  @filter((eq(val(pwd), 1))) checkUserPassword(func: uid(UserRoot)) {
    dgraph.uid : uid
  }
  UserRoot as var(func: uid(User1)) @filter((uid(UserAuth2)))
  User1 as var(func: eq(User.username, "charlize")) @filter(type(User))
  UserAuth2 as var(func: uid(User1)) @filter(eq(User.username, "charlize")) @cascade
  checkPwd {
    pwd as checkpwd(User.password, "password")
  }
}

The initial @filter appears out of place.

It wouldn’t matter in our case anyway since the filter generated from the @auth directive will be executed anyway, preventing a checkUserPassword without passing along a JWT.

So I guess our @remote query which is responsible for generating a JWT token from a username+password could just access the /query endpoint instead of the /graphql endpoint and pass the following DQL for the desired result:

POST /query (without JWT)

query {
  checkUserPassword(func: eq(User.username, "charlize")) {
    checkpwd(User.password, "password")
  }
}

Which is perfectly fine with us since this is an internal exchange between our API and Dgraph anyway. But the rewritten GraphQL query looks like a bug, no?

That definitely looks like a bug to me. That is invalid DQL. The filter directive should never be at the root like that.

Thanks for replying. Before I go on and raise this as a bug; would you also agree that the auto-generated check<Type><Field> query (e.g. checkUserPassword) should not include the @auth-filters when rewritten to DQL?

I saw in some of your other posts that the application(s) you’re working on has a lot of @auth directives and rules and was wondering if the check<Type><Field> queries really should execute them – I mean, the sole use case for a checkUserPassword would be to verify a password before generating a JWT with claims that the @auth rules could actually inspect.

I personally would be acceptable with that proposal. I am not sure of how anyone is using it right now though. That would be a good thing to poll somehow on who is using the @secret directive and if they are using it with @auth rules. Obviously if this :bug: is affecting anyone using the two together it would error out. :man_shrugging:t2:

Hi, I can confirm that I also have a similar error on latest master:
column 1: Expected some name. Got: lex.Item [14] \"@\" at 2:1"

I think that @auth and @secret definitly should work together.

My workaround for now would look like this:

  • @custom login mutation pointing to my custom GQL server
  • my custom resolver will fetch the user from Dgraph using an ADMIN jwt token
  • it will check if the hashed password matches and if yes, return a valid jwt token

FYI,

1 Like