About Dgraph.Authorization and @auth

I’m reading https://dgraph.io/docs/graphql/authorization/authorization-overview/ and have some questions.

I’m using keycloak oidc server, I’m sure you’re familiar with it or with oidc in general.

https://connect.icod.de/auth/realms/testrealm/.well-known/openid-configuration
is the autoconfiguration url, it has jwk url

  1. Can this # Dgraph.Authorization be on multiple lines?

  2. In audience the returned id token has [“account”, “the-client-id”], is it enough to set [“the-client-id”]?

  3. Namespace, I don’t understand this. Do I have to provide an example id token payload there in JSON format?

  4. Header, what should the header provide to the endpoint? “The claims relevant to dgraph authorization”.
    I’d use https://github.com/gogatekeeper/gatekeeper/blob/master/docs/user-guide.md#upstream-headers
    As you can see there are some options which can be passed. Personally I’d check by “sub” if someone has access to a resource.

Right now this is my

# Dgraph.Authorization {"VerificationKey":"","Header":"X-Auth-Subject", "jwkurl":"https://connect.icod.de/auth/realms/testrealm/protocol/openid-connect/certs", "Namespace":"https://xyz.io/jwt/claims","Algo":"RS256","Audience":["devel"]}

Let’s move on to @auth.

The examples don’t make any sense at all.
Where do I write criteria? How does this all work?
Is there a real life example?

Queries and Mutations are autogenerated, how does this work in detail in context of @auth()

Seems like no one knows, the documentation is lacking and if you’re not a paying customer you won’t receive a response.

Thanks so much.

No

Just the audience should be enough.

All it is really for is a unique key to look for in the claims of the JWT to get values from. Originally only claims from this custom key were used, now you can use any of the root claims too.

This is the key to look for in the clients request for the actual JWT key.

By your truly :smiley:

1 Like

Come join the (unofficial) Discord server if you haven’t already @dalu. We have quite an active community of Dgraphers helping each other out:

1 Like

Thanks I joined, BUT, altough I did, I find discord/slack are very bad for the internet and information as a whole. You can no longer search HTML or content sites, but everything is hidden behind proprietary, hidden services. Did you know discord ptraces everything on a Linux system?
google: “discord ptrace linux”

Ok, back on topic.

Thanks for you response,

The Namespace is still a mystery.
What is the role of

"https://xyz.io/jwt/claims"

Is this a literal string, some hardcoded string dgraph recognizes? It’s not self-explanatory.
Here’s a default keycloak id token

  "iat": 1635250590,
  "auth_time": 1635250589,
  "jti": "e7f11506-04b5-470a-b546-5365bea7dc74",
  "iss": "https://redacted/auth/realms/redacted",
  "aud": [
    "devel",
    "account"
  ],
  "sub": "4d1debda-2a29-4caf-9b7f-a8474051f6b6",
  "typ": "Bearer",
  "azp": "devel",
  "nonce": "ec137176-9c23-4375-849e-74bb20c7fbea",
  "session_state": "5618efe5-2bcf-4c35-8730-ae17b8258404",
  "acr": "1",
  "allowed-origins": [
    "*"
  ],
  "realm_access": {
    "roles": [
      "offline_access",
      "default-roles-redacted",
      "uma_authorization",
      "user"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid email profile",
  "sid": "5618efe5-2bcf-4c35-8730-ae17b8258404",
  "email_verified": true,
  "name": "redacted",
  "preferred_username": "redacted",
  "given_name": "redacted",
  "family_name": "redacted",
  "email": "redacted"
}

how does that fit into the Namespace field?

So with a default header of
Authorization: Bearer <access token>
it would be "Authorization"

Still, the Namespace field is not making sense
https://dgraph.io/docs/graphql/authorization/authorization-overview/#authorization-with-standard-claims
where is this written / where do I provide this info? In the schema? But it can’t be on multiple lines, so how can this be on multiple lines?

https://dgraph.io/docs/graphql/authorization/authorization-overview/#authorization-with-custom-claims
says we’re supposed to pass them “the JWT is expected to contain a custom claims object with the claims used in authorization rules.”
So this huge unmarshalled keycloak token as json should be fit on 1 line under which namespace name exactly?

The @auth is more clear now, in the morning with a fresh mind :slight_smile:

continued:

if I understood that correctly

# Dgraph.Authorization {"VerificationKey":"","Header":"Authorization", "jwkurl":"https://connect.icod.de/auth/realms/testrealm/protocol/openid-connect/certs", "Namespace":"resource_access": { "account": { "roles": [ "manage-account", "manage-account-links", "view-profile" ] } }","Algo":"RS256","Audience":["devel"]}

is what that line should look like.
Can this be shortened?

Yes, exactly. But you can take out the word “Bearer” and the space afterwards and send just the access token.

It is just a hard coded string like

 # Dgraph.Authorization {"VerificationKey":"","Header":"Authorization", "jwkurl":"https://connect.icod.de/auth/realms/testrealm/protocol/openid-connect/certs", "Namespace":"yourCustomNamespaceFoo","Algo":"RS256","Audience":["devel"]}

And then your JWT payload would look something like:

  "iat": 1635250590,
  "auth_time": 1635250589,
  "jti": "e7f11506-04b5-470a-b546-5365bea7dc74",
  "iss": "https://redacted/auth/realms/redacted",
  "aud": [
    "devel",
    "account"
  ],
  "sub": "4d1debda-2a29-4caf-9b7f-a8474051f6b6",
  "typ": "Bearer",
  "azp": "devel",
  "nonce": "ec137176-9c23-4375-849e-74bb20c7fbea",
  "session_state": "5618efe5-2bcf-4c35-8730-ae17b8258404",
  "acr": "1",
  "allowed-origins": [
    "*"
  ],
  "realm_access": {
    "roles": [
      "offline_access",
      "default-roles-redacted",
      "uma_authorization",
      "user"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid email profile",
  "sid": "5618efe5-2bcf-4c35-8730-ae17b8258404",
  "email_verified": true,
  "name": "redacted",
  "preferred_username": "redacted",
  "given_name": "redacted",
  "family_name": "redacted",
  "email": "redacted",
  "yourCustomNamespaceFoo": {
    "roles": [
      "manage-account",
      "manage-account-links",
      "view-profile"
    ],
    "USERNAME": "redacted"
  }
}

Then in auth rules you could use these variables as
$roles or $USERNAME and since [I think 21.03?] You can also use standard claims variables like $email or $email_verified too.

1 Like

Ok so far so good.

But we’re not done yet :smiley:

Regarding @auth
It’s now clear that when the presented claim has a field named sub and the entity has a field named sub for convenience sake, I can write a query that checks if claims.sub == Person.sub .

However, take this schema

type Person @auth() {
  id: ID!
  sub: String!
  name: String
  posts: [Post]
}

type Post @auth() {
  id: ID!
  author: Person!
  content: String
}

Here Person has the sub field, I can check against.
However in subsequent checks I need to check if Post.author == (Person.sub == claims.sub), aka is the same Person that the claims subject is associated with.

Is that possible, it should be, right?
What would the @auth() rule look like? If it’s not too much trouble.

I see that it’s explained at https://dgraph.io/docs/graphql/authorization/directive/#graph-traversal-in-auth-rules , so never mind.

What I have is

type Post  @auth(
  query: { rule: """
    query ($sub: String!) {
      queryPost () {
        author(filter: {sub: {eq: $sub}}) {
          sub
        }
      }
    }
  """}
) {
  id: ID!
  author: Person!
  content: String
}

If I understood it correctly.

1 Like

You know, the more I think about it, complex authn/z rules have no place in a schema definition.

They should belong in a separate middleware/proxy.

1 Like

I would be happy just by having auth directives at field level :thinking:

1 Like