Comments (2)
Auth is a complicated topic, lots of opinions, strategies and tradeoffs that can be made.
- All three of these are things that can be used and combined in different ways, so there isn't a concrete answer here.
- 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. 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.authScopes
as a function on a type: Similar toauthScopes
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.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 likequery { ticket(id: 123) { customer: { phoneNumber } } }
, the customer field could grant a scope that is checked by thephoneNumber
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.
- scope loaders (eg
- 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).
- 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:
- Add ability to context
- Add casl scope loader to types that accepts action and subject in its type param
- 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.
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)
- Add context object to validator function HOT 3
- Getting a weird Typescript error with Prisma plugin HOT 3
- Idea: a guide for incremental adoption of pothos in a nexus/typegraphql project HOT 1
- loadableObject doesn't infer type properly HOT 3
- Support Client Directives in Prisma plugin HOT 3
- Nextjs Error webpack : TypeError: _builder__WEBPACK_IMPORTED_MODULE_1__.builder.prismaObject HOT 2
- Can we use `graphql-scalars` scalars? HOT 10
- how to use edge as output field in relay mutation HOT 6
- [Question] Is there any way to add the prisma types with non prisma types to builder.ObjectType HOT 1
- Prisma Plugin - How to expose "decimal" or other DB types? HOT 4
- Can't use objectType with a generic class HOT 3
- Hardcoded path does not exist HOT 2
- [bug] Pothos Subscriptions with Apollo v4 HOT 2
- `relationCount` errors. on null returned for non-nullable field HOT 3
- Feat: [GraphQL Yoga] Request for access to edge environmental values (Cloudflare KV Namespaces, Vercel Edge Config, etc) HOT 2
- Change the Next.js example to use Yoga? HOT 1
- Trying to pass Options to errorOptions on the Error plugin for Pothos but getting error ``` TypeError: Ref ({ parentTypeName , fieldName })=>`${parentTypeName}${fieldName}` has not been implemented``` when trying to generate schema. HOT 3
- `resolveArrayConnection` returns nullable `ConnectionShape` HOT 2
- Type errors on nullable in Typescript 4.9 HOT 6
- Integrating Apollo cacheControl feature into schema not easily possible HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pothos.