Auth Rule for CheckPassword Query

Motivation

The @secret directive

Dgraph GraphQL has support for @secret directive to have password fields. Adding @secret field for a type, generates the check<Type>Password query The query returns an instance of the Type if the correct ID and password field is passed. The query was designed to be used for authentication by privileged (admin) users.

Example:

type Author @secret(field: "pwd") {
	name: String! @id
	token: String
}

generates the query,

query {
	checkAuthorPassword(name: String!, pwd: String!): Author
}

Combining @secret and @auth directive

We also support Authorization using the @auth directive. With @auth directive, rules can be provided for query, add, delete, update queries / mutations which are used for the respective queries / mutations.

Eventhough, checkPassword is a separate query, it internally is treated like a different form of get query and also uses auth rules specified by query field.

The recent discuss posts Combining @secret and @auth directives and Auth rule with other directives show that this is proving to be problematic. The checkPassword query does not work in cases in which it is used for authenticating newly created Users. The usecase here is to use checkPassword at the time of Sign In of Users.

Proposed Solution

To solve this problem, there are 2 possible solutions.

  1. Have a separate field in @auth directive, say pwd. This field could be used for specifying the auth rule to be applied for checkPassword query. In case no rule has been specified for pwd, the query rule could be used.
  2. Disable applying of auth rules for checkPassword query.

Possible Drawbacks

  1. Making the checkPassword query not follow any auth rules could lead to its misuse and brute force attacks to guess password.

@spinelsun , @gregerolsson , @amaster507, @maaft , We will like to know more about your use case and which solution fits your needs.

2 Likes

Hi,

In our case, the user generates API keys, a combination of access key and secret key that serves as the PWD.
Then this key combination is sent to a proxy server, which in turn sends requests to slash.
The proxy has a unique JWT claim, and we want to check if it exists to create/update/delete a key for a user. We want to check it because we want only the proxy server to send requests to the endpoint that Slash produces. We use Slash authorization as another layer of security, so if someone gets our /graphql endpoint, he will be blocked cause he doesn’t have the server’s JWT claim.

I believe that option #1 will suite us best

1 Like

Our use case is fairly straightforward; we need a way for a user to exchange login credentials for a JWT token that can be used for accessing @auth-protected types.

The external service that is responsible for this will need to call the auto-generated check<Type><Field> to verify the credentials and then create and return a token that can be used on the /graphql endpoint to satisfy the @auth rules.

Running any @auth rules during check<Type><Field> (in our scenario) will fail, since no JWT is available yet. So in our case, option 2 would work just fine. If the caller tries to fetch anything other than __typename the @auth rules will kick in and reject the request?

Option 1 would work as well, since @spinelsun has a legitimate use case for that – we could just use a no-op rule that always returns true in our case.

We don’t see the possible drawback you mention as anything unique for Dgraph – wherever you have a service that can exchange credentials for a token you are open for brute-force attacks. Since you are using bcrypt, at least the brute-force attack will take some time. Granted, in a solution like mentioned by @spinelsun the external proxy server could further mitigate brute-force attacks by implementing a tar-pit or similar.

Thanks for looking into this!

1 Like

For us option 2 would also be sufficient.

1 Like

Thanks for your replies and explaining your use cases. To suit everyone’s needs, we have decided to go with the following option.

There will be a separate field for password inside @auth directive, to provide rules for check<Type>Password query.
If no password rule is passed, no rule would be applied to check<Type>Password.

This does break backward compatibility to some degree, as the query rule was applied by default to the check<Type>Password query. The behaviour of @secret with @auth is broken currently, and it seems unlikely that anyone is using it successfully. So, it makes sense to make this change.

After making the change, @maaft and @gregerolsson could then simply not pass any password rule to @auth while @spinelsun could use the password rule.

2 Likes

May I suggest you name the rule secret rather than password? The field it will protect may not be named password.

@rajas sounds good!

What if there are multiple secret fields? Would you need to (or could) specify an @auth rule for every single one?

@gregerolsson is very right. This could not only be useful for passwords, but rather for any secrets.

The rule name is following the query name. That makes sense to me.

Wait, is that even possible? Isn’t it a single secret field per type?

Oh, okay! I assumed that multiple fields are allowed, my bad!

But allowing for multiple secret fields would also be a very valid use-case, wouldn’t it? Otherwise you would have to define a whole new type for every secret you want to store.

The fix for supporting this has been merged into master. This will also be part of 20.11 release.
Related PR: Fix(GraphQL): Add support for using auth with secret directive by vmrajas · Pull Request #6907 · dgraph-io/dgraph · GitHub .

Now, a password rule can be supplied for @auth directive which then applies to the check<Type>Password query.

@gregerolsson , we decided against keeping secret as the field for the rule to be applied for check<Type>Password query. The reason is to avoid any confusion with @secret directive. Also, the corresponding field name for check<Type>Password query in @generate directive is also password. We want the field name to be consistent with it.

5 Likes