graphql-community / graphql-community/graphql-directive-auth

Redesign @hasRole directive: make it as flexible as possible

Open
#17 25 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
135
Forks
12
PR merge metrics
No merged PRs in 30d

Description

1. Take array of enums instead of string as argument in `@hasRole`.

(Please also keep in mind that roles (permission subsets) can be non-overlapping.)

Current implementation:

```graphql
directive @hasRole(role: String) on FIELD | FIELD_DEFINITION

type User
name: String
banned: Boolean @hasRole(role: "admin")
canPost: Boolean @hasRole(role: "reviewer, admin")
}
```

Proposed implementation:

```graphql
directive @hasRole(roles: [Role] = [UNKNOWN]) on FIELD | FIELD_DEFINITION

enum Role {
ADMIN
REVIEWER
USER
UNKNOWN
}

type User
name: String
banned: Boolean @hasRole(roles: [ADMIN])
canPost: Boolean @hasRole(roles: [REVIEWER, ADMIN])
}
```

It seems that an array of strings would be more flexible than an array of enums (because you don't need to update enum in the GraphQL SDL to add a new role), but it less type-safe (you don't get a list of possible values e.g. for autocomplete).

2. Add support of other directive locations besides `FIELD_DEFINITION` to apply the directive to e.g. mutations and fields in the input types used in these mutations for more granular permissions.

Did I understand correctly that `@hasRole` directive can only be applied to `FIELD_DEFINITION`, while there are many places where directives (in general) can be applied (see the full list here: https://github.com/graphql/graphql-js/blob/master/src/language/directiveLocation.js)?

Example use case:

Everyone can query, create and update users (except `canPost` and `banned` fields), but only users with role `ADMIN` can delete users.
Only users with role `REVIEWER` or `ADMIN` can have access (query, create and update) `canPost` field.
Only users with role `ADMIN` can have access (query, create and update) `banned` field.
Only users with role `MANAGER` can update `verified` field.

Proposed implementation:

```graphql
type User @hasRole(roles: [USER]) {
name: String
canPost: Boolean @hasRole(roles: [REVIEWER, ADMIN])
banned: Boolean @hasRole(roles: [ADMIN])
...
}

...

type Mutation {
createUser(data: UserCreateInput!): User!
updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User!
deleteUser(where: UserWhereUniqueInput!): User @hasRole(roles: [ADMIN])
...
}

...

input UserUpdateInput {
phone: String
verified: Boolean! @hasRole(roles: [MANAGER]) @default(value: false)
...
}
```

3. Add support of logical operations

Currently, `@hasRole(role: "user, admin")` checks if user has **some** (at least one any) of specified roles. What about **all**, **none** and other more complex cases? (Please also keep in mind that roles (permission subsets) can be non-overlapping.)

What about adding support of logic operations for nest rules (similar to https://github.com/maticzav/graphql-shield#and-or-not)? Something like (the syntax is subject to discussion):

```graphql
@hasRole(roles: "or(ADMIN, and(OWNER, not(EDITOR)))")
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reviewing the current @hasRole implementation and the discussion in this issue, then compare the requested directive locations with graphql-js's directiveLocation.js. The scope covers role arguments, field and input permissions, and nested logical operations; done would require an agreed design and corresponding implementation and tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
graphql, typescript
Domain
api, authorization, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.