Skip to content

Allow custom handling of GraphQL API permissions #2051

@mattgills

Description

@mattgills

Is your feature request related to a problem? Please describe.
In our instance of Vendure, we focus primarily on B2B commerce where each channel is a different store. In some cases we have stores that are fully anonymous, partially anonymous (login enabled but not required), and fully authenticated (login required). In the case of fully authenticated stores, it makes sense add restrictions to queries protected Permission.Public.

Describe the solution you'd like
We would like to be able to specify a custom permission handler/validator for API requests that are covered by Permission.Public. For example, we would want to add additional criteria to check what type of auth is required for a store, and use that config to determine what whether Permission.Public is strict enough. Therefore, channels that are anonymous or partially anonymous would allow those queries to behave as normal, but fully authenticated channels would block those requests (unless signed in).

Some further elaboration can be found in the Slack messages about this topic in "Additional context". It's also worth noting, per @michaelbromley's suggestion, that this could apply to all permissions.

Describe alternatives you've considered
You could overwrite the existing resolvers and change to Permission.Authenticated, but that would lead to incorrect behavior on the fully/partially anonymous channels.

Additional context
For some additional context, the starting point for a more open discussion on this feature request is below:

Matt Gillespie
...
Shop API Permissions
Currently we have stores that vary in how they approach authentication. We currently have stores that:

  • Require sign in to enter the store
  • Require sign in and admin approval (via custom plugin) to access the store
  • Does not require sign in, but gives the user the option (Vendure’s default)
  • Does not allow sign in, purely anonymous store
    With the first two scenarios in mind, we would like to further secure the product data and pricing that is accessible via the Shop API. It would be great to have the “Permission.Public” queries use an injectable strategy (or other configurable design) that allows each Vendure developer to specify what this check should do. In our case we'd probably check a channel custom field and validate the user is meeting the minimum criteria for the store's authentication.

Michael Bromley
I like the idea of making the handling of Permission.Public configurable. I might even go one step further and make it so any given permission can have a custom handler.

Matt Gillespie
Hey Michael! I've been meaning to open a feature request for the Permission.Public configurability (as well as other permissions) and would also enjoy working on this one to contribute to the community, if that makes sense. I was digging around the Vendure source code and was trying to think of how it would work / be designed. My original thought process was around changing the decorator / what's in the decorator, but have since realized that that seems like the incorrect path. My current thought shifted to where the logic is actually happening, more specifically in these two code snippets:

// auth-guard.ts
if (authDisabled || !permissions || isPublic) {
    return true;
} else {
    const canActivate =
        requestContext.userHasPermissions(permissions) || requestContext.authorizedAsOwnerOnly;
    if (!canActivate) {
        throw new ForbiddenError(LogLevel.Verbose);
    } else {
        return canActivate;
    }
}

// request-context.ts
userHasPermissions(permissions: Permission[]): boolean {
    const user = this.session?.user;
    if (!user || !this.channelId) {
        return false;
    }
    const permissionsOnChannel = user.channelPermissions.find(c => idsAreEqual(c.id, this.channelId));
    if (permissionsOnChannel) {
        return this.arraysIntersect(permissionsOnChannel.permissions, permissions);
    }
    return false;
}

The former code block would be more applicable to the shop-api as it would allow for handling of the Permission.Public in a configurable way. Perhaps it would change how isPublic is set (i.e., through a injectable strategy). In our case, the strategy would be to check if Permission.Public is a required condition and whether that channel is allowed to be accessed publicly.
The latter code block would be more applicable to the admin-api, which would allow devs to specify an injectable strategy that would run inside userHasPermissions. This code block may also apply to Permission.Authenticated, but as far as I can tell that corresponds to whether the user is specified on the request context (let me know if I am mistaken). I don't think our team would change this at the moment, but we have had discussions about using an internal tool to handle admin access. In that case, we could reach out to the service to see if they have the correct role associated (but that role is stored in another internal system).
Neither code block applies to PermissionOwner as that is intended to be "flag" to the developer that custom logic needs to be implemented anyway (kind of like we are already talking about in a somewhat adjacent way 😃 ).
I feel like I'm missing something here though, so just wanted to run the concept by you.

Michael Bromley
Hi Matt!
Yes, I'd say this would be a good addition to core. The first part is pretty straightforward I think - as you say, and injectable strategy could have a method that is executed to derive the value of canActivate.
The second one is slightly trickier if you want to be able to handle admin access without using the built-in permissions system. It's probably also doable but I think it needs some exploration as the scope of changes will probably end up being wider than that one bit of code.
Why don't you open a feature request with these ideas, then I can assign it to you and we can put further discussion in public 🙂

Metadata

Metadata

Assignees

Type

No type

Projects

Status

📅 Planned

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions