Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vet and use custom directives for authorization #36

Open
3 tasks done
rosshadden opened this issue Mar 24, 2020 · 2 comments
Open
3 tasks done

Vet and use custom directives for authorization #36

rosshadden opened this issue Mar 24, 2020 · 2 comments
Labels
enhancement For issues that describe a feature that needs to be added, changed, or removed, but is not a bug

Comments

@rosshadden
Copy link
Contributor

rosshadden commented Mar 24, 2020

What

GraphQL as a spec supports custom directives. This is how Apollo is able to add things like @key and @external.

We can leverage our own custom directive(s) to better support authorization (for example). The linked docs have an example section on authorization, but we can do something even better since we have our authorization already abstracted into its own service.

Inside our schemas we can annotate types, queries, and mutations with things like their resource path, which would let us implement gateway-level authorization.

For example:

@entity("reaction:pricing:pricebooks")
type Pricebook @key(fields: "id") {
  id: ID!
  name: String!
}

@entity("reaction:pricing:pricebook-entries")
type PricebookEntry @key(fields: "id") {
  id: String!

  @entity("reaction:pricing:pricebook")
  pricebook: Pricebook!
}

type Query {
  @ref(resource: "reation:pricing:pricebooks:*", action: "read")
  getPricebook(id: ID!): Pricebook

  @ref(resource: "reation:pricing:pricebook-entries:*", action: "read")
  @ref(resource: "reation:pricing:pricebooks:*", action: "read")
  getPricebookEntry(id: ID!): Pricebook
}

Some notes on the above:

  • These are pseudo-examples from pricing. These are simplified and not the exact types or names used in pricing, but I wanted to use something more grounded in reality than metasyntactic variables.
  • The directive we really care about here is what I called ref.
  • Name not set in stone. @kieckhafer and I brainstormed a few more:
    • @affects
    • @concerns
    • @hook
    • @involves
    • @policy
    • @ref
    • @reference
    • @uses
    • @bind
    • @connect
  • @entity isn't really well thought-out or an official idea, I was just spitballing some other things we could annotate.
  • @key is a directive added and used by Apollo Federation itself.
  • The first query is an example of a query (getPricebook) that works with one resource. This will be our most common case.
  • The second query (getPricebookEntry) is an example that works with two resources, as clients are able to request fields on pricebooks as well.
  • This plan does not support referencing conditional or optional resources. For example, if getPricebookEntry only enforced rules on pricebook-entries unless clients request fields within pricebooks, with this plan I suppose the workflow would be to only annotate with the former, and optionally enforce the latter in the pricing service itself.

How

The work required for this issue:

  • vet custom directives for our use, figure out how to make them an how best to fit them into our ecosystem
  • figure out how to cleanly add them to all services
    • I noticed that when you startup with a custom directive buildFederatedSchemas complains that you are using an unregistered directive. We still want individual services to be able to run independently of the federation, so we need to either figure out why, or perhaps define the directives in each service's schema.
  • implement @ref (in concept---can be called anything that makes sense)

Why

This lets us do some really cool things. It will let us go from have 0% of our authorization checks at the gateway level and 100% at the app level to having most of them (by far) at the gateway level. This means we will be able to determine whether users are authorized to perform certain actions before we even call out to the app in question.

@focusaurus
Copy link

focusaurus commented Mar 24, 2020

Do we want to define some default mapping or a directive that maps queries and mutations to actions? Like do we want by default to map the name of the query/mutation to the action name directly? For example: graphql query primaryShopId maps to authz action 'primaryShopId`.

Nevermind I see there's an explicit `action:' annotation parameter

@aldeed
Copy link
Contributor

aldeed commented Mar 26, 2020

Here are a smattering of thoughts:

  • @ticean and I discussed directives like this early on (I wrote a couple that did all the ID encode/decode automatically) but we opted not to use them because we weren't sure how/if they would work across microservices (which at the time meant schema stitching but now means federation support). Assuming you've tested and verified that this all works transparently with federation, then yipee! Specifically, my concern would be whether both the service and the gateway would need to install the directives, and potential mismatches with that.
  • We may want to prefix the directives to avoid clashes, unless this will be a generic solution that isn't tied to Reaction.
  • Do we need a way to support either AND or OR when they specify multiple checks?
  • Have you considered moving all non-mutation checks to decorate the types rather than the queries? I don't know if it's feasible, but I can think of at least three clear benefits:
    • Guarantee that the visibility of a type is the same everywhere, without the same check in multiple places.
    • We could omit/error a type at any level if there are children of children of children, etc. requested
    • Query rules would be applied automatically to the responses of mutations as well, ensuring that you can't see something simply because you can mutate it (up for debate whether this is good or bad).
  • What about some of the special cases like owner access?

@aldeed aldeed added the enhancement For issues that describe a feature that needs to be added, changed, or removed, but is not a bug label May 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement For issues that describe a feature that needs to be added, changed, or removed, but is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants