Feature request: An @id directive option that quarantines @ids after usage for security

The docs state that:

Identities created with @id are reusable. If you delete an existing user, you can reuse the username.

This, I think, creates a security issue if you are authorizing JWTs with usernames (I don’t want to put the user’s email address in a JWT because that goes against best practices):

Reason is that it’s technically possible for someone to change their username, keep the JWT that authorizes their prior username, someone else takes their old username, they use the old JWT to authorize themselves as that user who happens to have their old username. This would seem like an unlikely event, however, an effective attack strategy could be to sign up, then using a script, change the username 100,000 times to generate 100k JWTs authorized against 100k popular usernames, wait until someone signs up using one of the usernames that correlates with one of the 100k JWTs and login as that user. Assuming that your JWTs have 30 day expiries on them, the attacker would just need someone to sign up using one of those 100k usernames in the 30 day window. If they had this script running constantly they would inevitably gain access to Dgraph as that user.

This is not really a problem that can be solved on the client, because I don’t control access to the Dgraph GraphQL API.

Ideal solution from my point of view would be an ‘quarantine’ option that can be specified for @id values. If a value has a quarantine period, that means that if an @id value is deleted/changed, that original value would not be available for the duration of the quarantine period. For example, if there was a user with username ‘jack’, and they later change the username to ‘mark’, then the value ‘jack’ should not be available until n days later.

What the syntax might look like:

type User {
  id: ID!
  username: String! @id(quarantine: 30d)
}

This way I just make sure that the quarantine value is the same as my JWT expiry amount, and the attack vector is neutralised.

Hmm, I think this can be handled alternatively by putting the ID (DQL’s UID field) in the JWT. Then it is secure to non-reusable and also does not reveal a valid username for even more security best practices. This would then allow the user to login with a username but use the id in auth rules.

And just for others information, it is possible to have a username and id in the same type and work with both of them as needed.

type {
  id: ID
  username: String! @id
  email: String! @id
  name: String!
  isActive: Boolean @search
  # etc.
}
2 Likes

Thanks once again @amaster507! This works. :+1: