Moving GraphQL Authorization to admin API

Experience Report for Feature Request

Note: Feature requests are judged based on user experience and modeled on Go Experience Reports. These reports should focus on the problems: they should not focus on and need not propose solutions.

What you wanted to do

Manage Authorization configuration and rules outside of the schema

What you actually did

Put Authorization configuration and rules inside of the schema

Why that wasn’t great, with examples

Any external references to support your case

^ Thanks for this link @jdgamble555


they should not focus on and need not propose solutions.

Sorry, can’t really help but to provide this also.

It was talked about moving the Authorization line from the schema to the /admin GraphQL API.

This would open the door for more advanced control options

  • The input schema could be made public without also exposing all authorization rules
  • A single rule could be applied to multiple places without rewriting the same rule redundently
  • Easier iteration on schema without auth rules taking up so much schema realty
  • Admin management (Dgraph Cloud or custom Dgraph management solutions) endpoints could be made to work with these rules independently of schema alterations.

This also paves the way forward for auth rules at the field level as well.

Here is schema I propose to handle all of the above

enum Algo {
  HS256
  RS256
}
type Auth {
  header: String
  namespace: String
  algo: Algo
  verificationKey: String
  JWKurl: String
  audience: [String]
  closedByDefault: Boolean
}
type AuthRule {
  id: ID
  rule: String
  and: [AuthRule]
  or: [AuthRule]
  not: AuthRule
}
type AuthType {
  type: String! @id
  password: AuthRule
  query: AuthRule
  add: AuthRule
  update: AuthRule
  delete: AuthRule
  fields: [AuthField]
}
type AuthField {
  id: ID
  field: String! @search(by: [hash])
  query: AuthRule
  mutate: AuthRule
}

This schema proposed should work with the same kind of GraphQL generation that provides the following queries and mutations that are unique to the admin API.

type Query {
  getAuth: Auth
  getAuthRule(id: ID!): AuthRule
  queryAuthRule: [AuthRule]
  getAuthType(type: String!): AuthType
  queryAuthType(filter: AuthTypeFilter, first: Int, offset: Int): [AuthType]
  getAuthField(id: ID!): AuthField
  queryAuthField(filter: AuthFieldFilter)
}
type Mutation {
  updateAuth(input: AuthPatch): Auth
  deleteAuth: Auth
  addAuthRule(input: [AddAuthRuleInput!]!): AddAuthRulePayload
  updateAuthRule(input: UpdateAuthRuleInput!): UpdateAuthRulePayload
  deleteAuthRule(filter: AuthRuleFilter!): DeleteAuthRulePayload
  addAuthType(input: [AddAuthTypeInput!]!): AddAuthTypePayload
  updateAuthType(input: UpdateAuthTypeInput!): UpdateAuthTypePayload
  deleteAuthType(filter: AuthTypeFilter!): DeleteAuthTypePayload
  addAuthField(input: [AddAuthFieldInput!]!): AddAuthFieldPayload
  updateAuthField(input: UpdateAuthFieldInput!): UpdateAuthFieldPayload
  deleteAuthField(filter: AuthFieldFilter!): DeleteAuthFieldPayload
}
input AuthTypeFilter {
  type: StringHashFilter
  has: AuthRuleHasFilter
  and: [AuthRuleFilter]
  or: [AuthRuleFilter]
  not: AuthRuleFilter
}
enum AuthRuleHasFilter {
  password
  query
  add
  update
  delete
  fields
}
input AuthFieldFilter {
  id: [ID!]
  field: StringHashFilter
  has: AuthFieldHasFilter
  and: [AuthFieldFilter]
  or: [AuthFieldFilter]
  not: AuthFieldFilter
}
enum AuthFieldHasFilter {
  query
  mutate
}
input AuthPatch {
  header: String
  namespace: String
  algo: Algo
  verificationKey: String
  JWKurl: String
  audience: [String]
  closedByDefault: Boolean
}
input AddAuthRuleInput { ... } 
type AddAuthRulePayload { ... }
input UpdateAuthRuleInput { ... }
type UpdateAuthRulePayload { ... }
type DeleteAuthRulePayload { ... }
input AddAuthTypeInput { ... } 
type AddAuthTypePayload { ... }
input UpdateAuthTypeInput { ... }
type UpdateAuthTypePayload { ... }
type DeleteAuthTypePayload { ... }
input AddAuthFieldInput { ... } 
type AddAuthFieldPayload { ... }
input UpdateAuthFieldInput { ... }
type UpdateAuthFieldPayload { ... }
type DeleteAuthFieldPayload { ... }

This got me thinking maybe the entire schema could be held piece by piece in a management graph and then queried and iterated upon in pieces and the functions that generate a more static schema file, could read from this schema graph.

This tangent idea would take the form of AuthType above, but have information about specific fields. Adding a single field to a type would be then making a mutation to update that one type which would trigger the full API schema rebuild process. :bulb:

3 Likes

@verneleem Thanks for posting this comprehensive proposal.

I just would like to point out (since my comments were used as an example) that for me, having just the meta information (# Dgraph.Authorization…) controlled by the admin API is the most pressing need. At the moment, we’re forced to inject or remove that line via file munging depending on the deployment scenario (local testing, staging, production, etc) and it just feels dirty.

1 Like