Coder Social home page Coder Social logo

Comments (2)

hayes avatar hayes commented on August 15, 2024

Auth is a complicated topic, lots of opinions, strategies and tradeoffs that can be made.

  1. All three of these are things that can be used and combined in different ways, so there isn't a concrete answer here.
    1. scope loaders (eg customPerm): This pattern is useful for integrating with other auth libraries or permissions systems. It allows you to have a place on fields and types where you can define a parameters that will be passed to another library. This is what I would recommend for something like CASL (I'll add more detail below), but it is very flexable and can be used in a number of different ways.
    2. authScopes as a function on a field: This is useful if the scopes needed to resolve a field are dependent on the parent, or args for that field, but could also be used to add auth checks that fall outside your normal auth scopes. I would expect this mostly only to be used for weird edge cases.
    3. authScopes as a function on a type: Similar to authScopes on fields, this is useful if your checks require access to the parent object. I'll include an example of this in my suggestion for CASL below.
    4. grantScopes: This is useful for contextual auth stuff. It's not the most common thing, but for example, if you have some PII like a phone number that only the owner of that data should be able to read, but need to make it available to a support agent working on a ticket for that user.
      With a query like query { ticket(id: 123) { customer: { phoneNumber } } }, the customer field could grant a scope that is checked by the phoneNumber on the User type, so that in the context of a ticket an agent can access the phoneNumber of a customer (as long as they can load the ticket). This allows the User type not to need to worry about support tickets, or add any ticket related auth checks. This is probably not very common, but can be quite powerful.
  2. authScopes only receives context because it is run at the start of the beginning of the operation, so that scopes are initialized before resolving fields. One of the big design considerations with the auth API is performance, and efficiency. scope checks are designed to be cachable and are shared across fields. This is why scope loaders are NOT passed the parent, args, or info objects, and will likely not change. There is a workaround, which is to pass data through the scope parameter (see CASL example below).
  3. Support for accessing return values is not currently planned, but I am open to suggestions on how to handle this if you have specific use cases in mind.

CASL

Here is an example using a couple of the things described above. This is a rough idea based on a brief look at the CASL docs.

Setup:

  1. Add ability to context
  2. Add casl scope loader to types that accepts action and subject in its type param
  3. Call ability.can with action and subject from scope loader
function createContext() {
  const user = getLoggedInUser();
  const ability = defineAbilitiesFor(user);

  return {
    user,
    ability
  }
}

const builder = new SchemaBuilder<{
  Context: {
    user: User,
    ability: Ability,
  };
  AuthScopes: {
    casl: [action: string, subject: unknown],
  };
}>({
  plugins: [ScopeAuthPlugin],
  authScopes: async (context) => ({
    casl: ([action, subject]) => context.ability.can(action, resource),
  }),
});

Auth for a field:

builder.queryField('example', (t) => t.string({
  authScopes: { casl: ['someAction', 'SomeSubject'] },
  resolve: () => 'hi',
}));

Auth for a type (applies to all fields)

builder.objectType('Thing', {
  authScopes: {
    casl: ['someAction', 'SomeSubject'],
  },
  fields: () => ({}),
});

Auth for field that uses the parent (instance of subject):

builder.objectField('ExampleType', 'exampleField',  (t) => t.string({
  authScopes: (parent) => ({ casl: ['someAction',  parent] }),
  resolve: () => 'hi',
}));

Auth for a type using parent (applies to all fields)

builder.objectType('Thing', {
  authScopes: parent => ({
    casl: ['someAction', parent],
  }),
  fields: () => ({}),
});

I put these descriptions and examples together pretty quickly, so there are probably lots of typos, and few other mistakes, but hopefully the general explanations make sense. For future questions like this, I think github discussions might be a better fit than issues. I'd like to track common questions like this there so they are easier for others to learn from without having a bunch of open issues.

from pothos.

hayes avatar hayes commented on August 15, 2024

To elaborate a bit on the CASL example above, I would probably use something like ['read', 'SomeResource'] on the 'SomeResource' type, and then add additional field level scopes for mutations like ['create', 'SomeResource'].

from pothos.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.