# @auth directive status and security

**URL:** <https://discuss.dgraph.io/t/auth-directive-status-and-security/7432>\
**Category:** GraphQL\
**Tags:** graphql\
**Created:** [June 30, 2020, 12:12pm UTC](https://discuss.dgraph.io/t/auth-directive-status-and-security/7432 "2020-06-30T12:12:35Z")\
**Posts on this page:** 6\
**Page:** 2

<div class="post-metadata">

**Author:** ![forstmeier](https://yyz1.discourse-cdn.com/flex007/user_avatar/discuss.dgraph.io/forstmeier/32/2611_2.png) [@forstmeier](https://discuss.dgraph.io/u/forstmeier)\
**Post date:** [August 7, 2020, 1:04pm UTC](https://discuss.dgraph.io/t/auth-directive-status-and-security/7432/21 "2020-08-07T13:04:37Z")

</div>

That’s so weird, @arijit, because my resources look _identical_ to what you have here. Here’s a list of everything (some may be duplicated from earlier in the conversation but I’m including it here for a collected reference).

- I’m using the JSON-formatted config for the bottom of my `schema.graph`l file
- I’m building off of the `master` branch for the Dgraph repo (`git pull` and then running the `make install` command)
- I’m running Dgraph using `dgraph zero --my=localhost:5080` and `dgraph alpha --lru_mb=2048 --my=localhost:7080 --zero=localhost:5080` on two separate tabs of my command line and then uploading the schema using `curl -X POST localhost:8080/admin/schema -d '@schema.graphql'`
- the mutation I’m execution is identical (excepting different variable values)
- I’m _not_ evaluating the `"Audience"` parameter but that was marked as optional in the documentation
- I’m following [this documentation](https://graphql.dgraph.io/tutorials/todo-app-tutorial/todo-auth0-jwt/) to generate the private key in the schema authorization configuration
- below is a collection of all of the resources I have in their current configuration

Schema (simplified):

```auto
type OwnerOrg implements Org & Location @auth(
	add: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"true\" } }" },
		{ rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
	]},
	query: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"true\" } }" },
		{ or: [
			{ rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
			{ rule: "{ $role: { eq: \"USER_INTERNAL\" } }"},
		]},
		{ rule: """query($orgID: ID!) {
			queryOwnerOrg( filter: { id: [$orgID] } ) {
				id
			}
		}"""},
	]},
	update: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"true\" } }" },
		{ rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
		{ rule: """query($orgID: ID!) {
			queryOwnerOrg( filter: { id: [$orgID] } ) {
				id
			}
		}"""},
	]},
	delete: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"true\" } }" },
		{ rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
		{ rule: """query($orgID: ID!) {
			queryOwnerOrg( filter: { id: [$orgID] } ) {
				id
			}
		}"""},
	]},
) {
	labs: [LabOrg!]! @hasInverse(field: owner)
	storages: [StorageOrg!]! @hasInverse(field: owner)
}

interface Org {
	id: ID!
	name: String! @search(by: [hash])
	users: [User!]! @hasInverse(field: org)
	createdOn: DateTime!
	updatedOn: DateTime!
}

type User @auth(
	add: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"true\" } }" },
		{ rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
		{ rule: """query($orgID: ID!) {
			queryUser {
				owner( filter: { id: [$orgID] } ) {
					id
				}
			}
		}"""},
	]},
	query: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"true\" } }" },
		{ rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
		{ rule: """query($orgID: ID!) {
			queryUser {
				owner( filter: { id: [$orgID] } ) {
					id
				}
			}
		}"""},
	]},
	update: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"true\" } }" },
		{ rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
		{ rule: """query($orgID: ID!) {
			queryUser {
				owner( filter: { id: [$orgID] } ) {
					id
				}
			}
		}"""},
	]},
	delete: { and: [
		{ rule: "{ $isAuthenticated: { eq: \"true\" } }" },
		{ rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
		{ rule: """query($orgID: ID!) {
			queryUser {
				owner( filter: { id: [$orgID] } ) {
					id
				}
			}
		}"""},
	]},
) {
	owner: OwnerOrg!
	email: String! @id
	firstName: String! @search(by: [exact])
	lastName: String! @search(by: [exact])
	org: Org! @hasInverse(field: users)
	user_id: String!
}

interface Location {
	street: String! @search(by: [fulltext])
	city: String! @search(by: [fulltext])
	county: String! @search(by: [exact])
	state: String! @search(by: [exact])
	country: String! @search(by: [exact])
	zip: Int! @search
}

# Dgraph.Authorization {"VerificationKey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt50KaKOwI1/r9yEojzVW\ncOwGTZbL7sjlUaSI25icLPF8eK1R2dbVaKTdZNtq6LAxFe+NDt2AuU7Vtqzv8GGv\nb2RP5KEgUcJyy75Yw0hT4TP3SrzDB2paCfcKHxQlTQ0pFP0SJMk4YCfq+gDqPnXQ\nCfzw+Zff29zZh5bs1lOxvAIgsu9LtH/zX6f5ASMdHV8EPWdZq6nq8KoOiMcAizDj\nrbm/qcAJP6k+ztbgtN6HdD8v6+7uIKStrYRa0BLXdJAra2uaLI4z2H22RHuzhkIu\nytxpYnxDlYTXzroSiRs/vs/dyHixT8smbEQmLoPTpflnoEZcNDXkhf0v9yVtG6NV\n1QIDAQAB\n-----END PUBLIC KEY-----","Header":"X-Auth0-Token","Namespace":"https://folivora.io/jwt/claims","Algo":"RS256"}

```

Auth0 Rule code:

```auto
function addAttributes(user, context, callback) {
  const claims = {
    "isAuthenticated": "true", // string because of dgraph requirement
    "role": user.app_metadata.role,
    "orgID": user.app_metadata.orgID
  };
  
  context.idToken["https://folivora.io/jwt/claims"] = claims;
  callback(null, user, context);
}

```

JWT contents:

```auto
{
  "https://folivora.io/jwt/claims": {
    "isAuthenticated": "true",
    "role": "USER_ADMIN",
    "orgID": "42"
  },
  "nickname": "john.forstmeier",
  ...
}

```

Mutation:

```auto
{
	"query": "mutation AddOwnerOrgs($input: [AddOwnerOrgInput!]!) { addOwnerOrg(input: $input) { ownerOrg { id } } }",
	"variables": {
		"input": [
			{
				"street": "street",
				"city": "city",
				"county": "county",
				"state": "state",
				"country": "country",
				"zip": 12345,
				"name": "name",
				"users": [],
				"createdOn": "2006-01-02T15:04:05",
				"updatedOn": "2006-01-02T15:04:05",
				"labs": [],
				"storages": []
			}
		]
	}
}

```

Header (token header is the token retrieved from the login process):

 ![Screenshot from 2020-08-07 08-59-34](https://canada1.discourse-cdn.com/flex007/uploads/dgraph/original/2X/d/d9898727e7bc41f84762f90cfd2a71cc40b854eb.png)

Sorry to keep harping on this particular issue - it’s just now become a full blocker on my end with being unable to use Dgraph `@auth` fully. The documentation and support here has been amazing I’m just not sure what I’m doing wrong. 😬

---

<div class="post-metadata">

**Author:** ![arijit](https://yyz1.discourse-cdn.com/flex007/user_avatar/discuss.dgraph.io/arijit/32/2116_2.png) [@arijit](https://discuss.dgraph.io/u/arijit)\
**Post date:** [August 10, 2020, 6:01am UTC](https://discuss.dgraph.io/t/auth-directive-status-and-security/7432/22 "2020-08-10T06:01:33Z")

</div>

I tried the exact same schema. I just changed the field for OwnerOrg. It seems to be working fine for me.

```auto
type OwnerOrg implements Org & Location @auth(
    add: { and: [
        { rule: "{ $isAuthenticated: { eq: \"true\" } }" },
        { rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
    ]},
    query: { and: [
        { rule: "{ $isAuthenticated: { eq: \"true\" } }" },
        { or: [
            { rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
            { rule: "{ $role: { eq: \"USER_INTERNAL\" } }"},
        ]},
        { rule: """query($orgID: ID!) {
        queryOwnerOrg( filter: { id: [$orgID] } ) {
        id
        }
        }"""},
    ]},
    update: { and: [
        { rule: "{ $isAuthenticated: { eq: \"true\" } }" },
        { rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
        { rule: """query($orgID: ID!) {
        queryOwnerOrg( filter: { id: [$orgID] } ) {
        id
        }
        }"""},
    ]},
    delete: { and: [
        { rule: "{ $isAuthenticated: { eq: \"true\" } }" },
        { rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
        { rule: """query($orgID: ID!) {
        queryOwnerOrg( filter: { id: [$orgID] } ) {
        id
        }
        }"""},
    ]},
) {
    random: String! @search(by: [hash])
}

interface Org {
    id: ID!
    name: String! @search(by: [hash])
    users: [User!]! @hasInverse(field: org)
    createdOn: DateTime!
    updatedOn: DateTime!
}

type User @auth(
    add: { and: [
        { rule: "{ $isAuthenticated: { eq: \"true\" } }" },
        { rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
        { rule: """query($orgID: ID!) {
        queryUser {
        owner( filter: { id: [$orgID] } ) {
        id
        }
        }
        }"""},
    ]},
    query: { and: [
        { rule: "{ $isAuthenticated: { eq: \"true\" } }" },
        { rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
        { rule: """query($orgID: ID!) {
        queryUser {
        owner( filter: { id: [$orgID] } ) {
        id
        }
        }
        }"""},
    ]},
    update: { and: [
        { rule: "{ $isAuthenticated: { eq: \"true\" } }" },
        { rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
        { rule: """query($orgID: ID!) {
        queryUser {
        owner( filter: { id: [$orgID] } ) {
        id
        }
        }
        }"""},
    ]},
    delete: { and: [
        { rule: "{ $isAuthenticated: { eq: \"true\" } }" },
        { rule: "{ $role: { eq: \"USER_ADMIN\" } }"},
        { rule: """query($orgID: ID!) {
        queryUser {
        owner( filter: { id: [$orgID] } ) {
        id
        }
        }
        }"""},
    ]},
) {
    owner: OwnerOrg!
    email: String! @id
    firstName: String! @search(by: [exact])
    lastName: String! @search(by: [exact])
    org: Org! @hasInverse(field: users)
    user_id: String!
}

interface Location {
    street: String! @search(by: [fulltext])
    city: String! @search(by: [fulltext])
    county: String! @search(by: [exact])
    state: String! @search(by: [exact])
    country: String! @search(by: [exact])
    zip: Int! @search
}
# Dgraph.Authorization {"VerificationKey":"-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIdHCD6UCPADXXefvmamEdGKoB2NKV+E\nsqDO1H6MpoQED0QjL4iZHulVMjlFt+lwjU/ty+GG9ev/pqyk9pMzIlkCAwEAAQ==\n-----END PUBLIC KEY-----","Header":"X-Test-Auth","Namespace":"https://xyz.io/jwt/claims","Algo":"RS256","Audience":["aud1","63do0q16n6ebjgkumu05kkeian","aud5"]}

```

JWT

```auto
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1626239022,
  "https://xyz.io/jwt/claims": {
    "isAuthenticated": "true",
    "role": "USER_ADMIN",
    "orgID": "42"
  },
  "nickname": "john.forstmeier"
}

```

Mutation

```auto
mutation AddOwnerOrgs($input: [AddOwnerOrgInput!]!) {
  addOwnerOrg(input: $input) {
    ownerOrg {
      id
    }
  }
}

```

Variables:

```auto
{
		"input": [{
				"street": "street",
				"city": "city",
				"county": "county",
				"state": "state",
				"country": "country",
				"zip": 12345,
				"name": "name",
				"users": [],
				"createdOn": "2006-01-02T15:04:05",
				"updatedOn": "2006-01-02T15:04:05",
				"random": "random"
      }
}

```

HTTP Header:

```auto
X-Test-Auth

```

JWT Token:

```auto
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTYyNjIzOTAyMiwiaHR0cHM6Ly94eXouaW8vand0L2NsYWltcyI6eyJpc0F1dGhlbnRpY2F0ZWQiOiJ0cnVlIiwicm9sZSI6IlVTRVJfQURNSU4iLCJvcmdJRCI6IjQyIn0sIm5pY2tuYW1lIjoiam9obi5mb3JzdG1laWVyIn0.H_L6MpDDtCDfdnKBqLilv3wuk9zRwLpGIwxu9_1t2gkcKCEZ7v6d_KuHtV_miEowcpFT-jAP-pAwElieYhgXKQ

```

I suggest you do the following steps and let me know if you are still getting the error.

1. `curl -X POST localhost:8180/admin/schema --data-binary '@schema.graphql`
2. Send the mutation request from either GraphQL client like insomnia or GQL playground. While sending the mutation request set the variables and HTTP header as mentioned above.

Also, you can print and see whether auth meta is set or not in `ParseAuthMeta()` in `auth.go` and use `--data-binary` flag in curl instead of `-d`.

---

<div class="post-metadata">

**Author:** ![forstmeier](https://yyz1.discourse-cdn.com/flex007/user_avatar/discuss.dgraph.io/forstmeier/32/2611_2.png) [@forstmeier](https://discuss.dgraph.io/u/forstmeier)\
**Post date:** [August 11, 2020, 12:49am UTC](https://discuss.dgraph.io/t/auth-directive-status-and-security/7432/23 "2020-08-11T00:49:58Z")

</div>

Hey, @arijit circling back to this and so far things appear to be working.

- I kept my schema unchanged (tried it with your new `random` field on the `OwnerOrg` and also switched it back to my original fields)
- hitting the API endpoint worked from Postman with the same payload

I did change two things:

1. I added in the `Audience` field and included the expected value that I was able to pull from the JWT for the `aud` key
2. I used the public key that was made available on [https://jwt.io/](https://jwt.io/) in the “VERIFY SIGNATURE” box instead of the one that was generated using the documentation in the Dgraph [documentation](https://graphql.dgraph.io/tutorials/todo-app-tutorial/todo-auth0-jwt/)

I’m not sure why the different public key seemed to work?

---

<div class="post-metadata">

**Author:** ![arijit](https://yyz1.discourse-cdn.com/flex007/user_avatar/discuss.dgraph.io/arijit/32/2116_2.png) [@arijit](https://discuss.dgraph.io/u/arijit)\
**Post date:** [August 11, 2020, 5:27am UTC](https://discuss.dgraph.io/t/auth-directive-status-and-security/7432/24 "2020-08-11T05:27:41Z")

</div>

@forstmeier You need to use the same public-private key pair for JWT verification. Your public key is similar to your identity and if you sign the JWT with any other public-key then the JWT verification will fail. This allows for verification of the source of JWT and prevents tampering of JWT tokens.

You need to sign the JWT with the public key that was generated using documentation as you were using its private key for verification. But since you used a different public key from `jwt.io` authorization failed.

---

<div class="post-metadata">

**Author:** ![forstmeier](https://yyz1.discourse-cdn.com/flex007/user_avatar/discuss.dgraph.io/forstmeier/32/2611_2.png) [@forstmeier](https://discuss.dgraph.io/u/forstmeier)\
**Post date:** [August 11, 2020, 1:21pm UTC](https://discuss.dgraph.io/t/auth-directive-status-and-security/7432/25 "2020-08-11T13:21:07Z")

</div>

@arijit okay, cool! Thank you for your help! I don’t know what to mark as the “answer” to the original question because this thread has been super valuable and I think answers several questions other users might have so feel free to mark it up however you see fit!

---

<div class="post-metadata">

**Author:** ![timcash](https://yyz1.discourse-cdn.com/flex007/user_avatar/discuss.dgraph.io/timcash/32/4450_2.png) [@timcash](https://discuss.dgraph.io/u/timcash)\
**Post date:** [August 25, 2020, 1:26am UTC](https://discuss.dgraph.io/t/auth-directive-status-and-security/7432/27 "2020-08-25T01:26:59Z")

</div>

Yes this thread is helpful for those of us who do not know much about JWTs and claims. Also if you are using Auth0 and want to do something similar for node.js you will need to use Auth0 “hooks”

[Previous page](https://discuss.dgraph.io/t/auth-directive-status-and-security/7432.md?page=1)
