@envelop/generic-auth 2.0.0-alpha-c8eef8e.0 → 2.0.1-alpha-7ca7cb8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -1,21 +1,15 @@
1
1
  import { DefaultContext, Plugin } from '@envelop/core';
2
- import { DirectiveNode, FieldNode, GraphQLError, GraphQLObjectType } from 'graphql';
2
+ import { DirectiveNode, GraphQLError, GraphQLResolveInfo } from 'graphql';
3
+ export * from './utils';
3
4
  export declare class UnauthenticatedError extends GraphQLError {
4
5
  }
5
6
  export declare type ResolveUserFn<UserType, ContextType = DefaultContext> = (context: ContextType) => null | UserType | Promise<UserType | null>;
6
- export declare type ValidateUserFnParams<UserType> = {
7
- /** The user object. */
8
- user: UserType;
9
- /** The field node from the operation that is being validated. */
10
- fieldNode: FieldNode;
11
- /** The object type which has the field that is being validated. */
12
- objectType: GraphQLObjectType;
13
- /** The directive node used for the authentication (If using an SDL flow). */
14
- fieldAuthDirectiveNode: DirectiveNode | undefined;
15
- /** The extensions used for authentication (If using an extension based flow). */
16
- fieldAuthExtension: unknown | undefined;
17
- };
18
- export declare type ValidateUserFn<UserType> = (params: ValidateUserFnParams<UserType>) => void | UnauthenticatedError;
7
+ export declare type ValidateUserFn<UserType, ContextType = DefaultContext> = (user: UserType, context: ContextType, resolverInfo?: {
8
+ root: unknown;
9
+ args: Record<string, unknown>;
10
+ context: ContextType;
11
+ info: GraphQLResolveInfo;
12
+ }, directiveNode?: DirectiveNode) => void | Promise<void>;
19
13
  export declare const DIRECTIVE_SDL = "\n directive @auth on FIELD_DEFINITION\n";
20
14
  export declare const SKIP_AUTH_DIRECTIVE_SDL = "\n directive @skipAuth on FIELD_DEFINITION\n";
21
15
  export declare type GenericAuthPluginOptions<UserType extends {} = {}, ContextType extends DefaultContext = DefaultContext> = {
@@ -26,6 +20,11 @@ export declare type GenericAuthPluginOptions<UserType extends {} = {}, ContextTy
26
20
  * Make sure to either return `null` or the user object.
27
21
  */
28
22
  resolveUserFn: ResolveUserFn<UserType, ContextType>;
23
+ /**
24
+ * Here you can implement any custom to check if the user is valid and have access to the server.
25
+ * This method is being triggered in different flows, besed on the mode you chose to implement.
26
+ */
27
+ validateUser?: ValidateUserFn<UserType, ContextType>;
29
28
  /**
30
29
  * Overrides the default field name for injecting the user into the execution `context`.
31
30
  * @default currentUser
@@ -43,12 +42,6 @@ export declare type GenericAuthPluginOptions<UserType extends {} = {}, ContextTy
43
42
  * @default skipAuth
44
43
  */
45
44
  authDirectiveName?: 'skipAuth' | string;
46
- /**
47
- * Customize how the user is validated. E.g. apply authorization role based validation.
48
- * The validation is applied during the extended validation phase.
49
- * @default `defaultProtectAllValidateFn`
50
- */
51
- validateUser?: ValidateUserFn<UserType>;
52
45
  } | {
53
46
  /**
54
47
  * Just resolves the user and inject to authenticated user into the `context`.
@@ -60,21 +53,14 @@ export declare type GenericAuthPluginOptions<UserType extends {} = {}, ContextTy
60
53
  * resolves the user and inject to authenticated user into the `context`.
61
54
  * And checks for `@auth` directives usages to run validation automatically.
62
55
  */
63
- mode: 'protect-single';
56
+ mode: 'protect-auth-directive';
64
57
  /**
65
58
  * Overrides the default directive name
66
59
  * @default auth
67
60
  */
68
61
  authDirectiveName?: 'auth' | string;
69
- /**
70
- * Customize how the user is validated. E.g. apply authorization role based validation.
71
- * The validation is applied during the extended validation phase.
72
- * @default `defaultProtectSingleValidateFn`
73
- */
74
- validateUser?: ValidateUserFn<UserType>;
75
62
  });
76
- export declare function defaultProtectAllValidateFn<UserType>(params: ValidateUserFnParams<UserType>): void | UnauthenticatedError;
77
- export declare function defaultProtectSingleValidateFn<UserType>(params: ValidateUserFnParams<UserType>): void | UnauthenticatedError;
63
+ export declare function defaultValidateFn<UserType, ContextType>(user: UserType, contextType: ContextType): void;
78
64
  export declare const useGenericAuth: <UserType extends {} = {}, ContextType extends DefaultContext = DefaultContext>(options: GenericAuthPluginOptions<UserType, ContextType>) => Plugin<{
79
- validateUser: ValidateUserFn<UserType>;
65
+ validateUser: ValidateUserFn<UserType, ContextType>;
80
66
  }>;
package/index.js CHANGED
@@ -3,7 +3,16 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const graphql = require('graphql');
6
- const extendedValidation = require('@envelop/extended-validation');
6
+
7
+ function getDirective(info, name) {
8
+ var _a;
9
+ const { parentType, fieldName, schema } = info;
10
+ const schemaType = schema.getType(parentType.name);
11
+ const field = schemaType.getFields()[fieldName];
12
+ const astNode = field.astNode;
13
+ const authDirective = (_a = astNode === null || astNode === void 0 ? void 0 : astNode.directives) === null || _a === void 0 ? void 0 : _a.find(d => d.name.value === name);
14
+ return authDirective || null;
15
+ }
7
16
 
8
17
  class UnauthenticatedError extends graphql.GraphQLError {
9
18
  }
@@ -13,87 +22,33 @@ const DIRECTIVE_SDL = /* GraphQL */ `
13
22
  const SKIP_AUTH_DIRECTIVE_SDL = /* GraphQL */ `
14
23
  directive @skipAuth on FIELD_DEFINITION
15
24
  `;
16
- function defaultProtectAllValidateFn(params) {
17
- if (params.user == null && !params.fieldAuthDirectiveNode && !params.fieldAuthExtension) {
18
- const schemaCoordinate = `${params.objectType.name}.${params.fieldNode.name.value}`;
19
- return new UnauthenticatedError(`Accessing '${schemaCoordinate}' requires authentication.`, [params.fieldNode]);
20
- }
21
- }
22
- function defaultProtectSingleValidateFn(params) {
23
- if (params.user == null && (params.fieldAuthDirectiveNode || params.fieldAuthExtension)) {
24
- const schemaCoordinate = `${params.objectType.name}.${params.fieldNode.name.value}`;
25
- return new UnauthenticatedError(`Accessing '${schemaCoordinate}' requires authentication.`, [params.fieldNode]);
25
+ function defaultValidateFn(user, contextType) {
26
+ if (!user) {
27
+ throw new UnauthenticatedError('Unauthenticated!');
26
28
  }
27
29
  }
28
30
  const useGenericAuth = (options) => {
29
- var _a, _b;
30
- const contextFieldName = options.contextFieldName || 'currentUser';
31
- if (options.mode === 'protect-all' || options.mode === 'protect-single') {
32
- const directiveName = (_a = options.authDirectiveName) !== null && _a !== void 0 ? _a : (options.mode === 'protect-all' ? 'skipAuth' : 'auth');
33
- const validateUser = (_b = options.validateUser) !== null && _b !== void 0 ? _b : (options.mode === 'protect-all' ? defaultProtectAllValidateFn : defaultProtectSingleValidateFn);
34
- const extractAuthMeta = (input) => {
35
- var _a, _b, _c;
36
- return {
37
- fieldAuthExtension: (_a = input.extensions) === null || _a === void 0 ? void 0 : _a[directiveName],
38
- fieldAuthDirectiveNode: (_c = (_b = input.astNode) === null || _b === void 0 ? void 0 : _b.directives) === null || _c === void 0 ? void 0 : _c.find(directive => directive.name.value === directiveName),
39
- };
40
- };
31
+ const fieldName = options.contextFieldName || 'currentUser';
32
+ const validateUser = options.validateUser || defaultValidateFn;
33
+ if (options.mode === 'protect-all') {
41
34
  return {
42
- onPluginInit({ addPlugin }) {
43
- addPlugin(extendedValidation.useExtendedValidation({
44
- rules: [
45
- function AuthorizationExtendedValidationRule(context, args) {
46
- const user = args.contextValue[contextFieldName];
47
- const handleField = (fieldNode, objectType) => {
48
- const field = objectType.getFields()[fieldNode.name.value];
49
- if (field == null) {
50
- // field is null/undefined if this is an introspection field
51
- return;
52
- }
53
- const { fieldAuthExtension, fieldAuthDirectiveNode } = extractAuthMeta(field);
54
- const error = validateUser({
55
- user,
56
- fieldNode,
57
- objectType,
58
- fieldAuthDirectiveNode,
59
- fieldAuthExtension,
60
- });
61
- if (error) {
62
- context.reportError(error);
63
- }
64
- };
65
- return {
66
- Field(node) {
67
- const fieldType = graphql.getNamedType(context.getParentType());
68
- if (graphql.isIntrospectionType(fieldType)) {
69
- return false;
70
- }
71
- if (graphql.isObjectType(fieldType)) {
72
- handleField(node, fieldType);
73
- }
74
- else if (graphql.isUnionType(fieldType)) {
75
- for (const objectType of fieldType.getTypes()) {
76
- handleField(node, objectType);
77
- }
78
- }
79
- else if (graphql.isInterfaceType(fieldType)) {
80
- for (const objectType of args.schema.getImplementations(fieldType).objects) {
81
- handleField(node, objectType);
82
- }
83
- }
84
- return undefined;
85
- },
86
- };
87
- },
88
- ],
89
- }));
90
- },
91
35
  async onContextBuilding({ context, extendContext }) {
92
36
  const user = await options.resolveUserFn(context);
93
37
  extendContext({
94
- [contextFieldName]: user,
38
+ [fieldName]: user,
39
+ validateUser,
95
40
  });
96
41
  },
42
+ onExecute() {
43
+ return {
44
+ async onResolverCalled({ args, root, context, info }) {
45
+ const authDirectiveNode = getDirective(info, options.authDirectiveName || 'skipAuth');
46
+ if (authDirectiveNode)
47
+ return;
48
+ await context.validateUser(context[fieldName], context);
49
+ },
50
+ };
51
+ },
97
52
  };
98
53
  }
99
54
  else if (options.mode === 'resolve-only') {
@@ -101,9 +56,36 @@ const useGenericAuth = (options) => {
101
56
  async onContextBuilding({ context, extendContext }) {
102
57
  const user = await options.resolveUserFn(context);
103
58
  extendContext({
104
- [contextFieldName]: user,
59
+ [fieldName]: user,
60
+ validateUser: () => validateUser(user, context),
61
+ });
62
+ },
63
+ };
64
+ }
65
+ else if (options.mode === 'protect-auth-directive') {
66
+ return {
67
+ async onContextBuilding({ context, extendContext }) {
68
+ const user = await options.resolveUserFn(context);
69
+ extendContext({
70
+ [fieldName]: user,
71
+ validateUser,
105
72
  });
106
73
  },
74
+ onExecute() {
75
+ return {
76
+ async onResolverCalled({ args, root, context, info }) {
77
+ const authDirectiveNode = getDirective(info, options.authDirectiveName || 'auth');
78
+ if (authDirectiveNode) {
79
+ await context.validateUser(context[fieldName], context, {
80
+ info,
81
+ context: context,
82
+ args,
83
+ root,
84
+ }, authDirectiveNode);
85
+ }
86
+ },
87
+ };
88
+ },
107
89
  };
108
90
  }
109
91
  return {};
@@ -112,6 +94,6 @@ const useGenericAuth = (options) => {
112
94
  exports.DIRECTIVE_SDL = DIRECTIVE_SDL;
113
95
  exports.SKIP_AUTH_DIRECTIVE_SDL = SKIP_AUTH_DIRECTIVE_SDL;
114
96
  exports.UnauthenticatedError = UnauthenticatedError;
115
- exports.defaultProtectAllValidateFn = defaultProtectAllValidateFn;
116
- exports.defaultProtectSingleValidateFn = defaultProtectSingleValidateFn;
97
+ exports.defaultValidateFn = defaultValidateFn;
98
+ exports.getDirective = getDirective;
117
99
  exports.useGenericAuth = useGenericAuth;
package/index.mjs CHANGED
@@ -1,5 +1,14 @@
1
- import { GraphQLError, getNamedType, isIntrospectionType, isObjectType, isUnionType, isInterfaceType } from 'graphql';
2
- import { useExtendedValidation } from '@envelop/extended-validation';
1
+ import { GraphQLError } from 'graphql';
2
+
3
+ function getDirective(info, name) {
4
+ var _a;
5
+ const { parentType, fieldName, schema } = info;
6
+ const schemaType = schema.getType(parentType.name);
7
+ const field = schemaType.getFields()[fieldName];
8
+ const astNode = field.astNode;
9
+ const authDirective = (_a = astNode === null || astNode === void 0 ? void 0 : astNode.directives) === null || _a === void 0 ? void 0 : _a.find(d => d.name.value === name);
10
+ return authDirective || null;
11
+ }
3
12
 
4
13
  class UnauthenticatedError extends GraphQLError {
5
14
  }
@@ -9,87 +18,33 @@ const DIRECTIVE_SDL = /* GraphQL */ `
9
18
  const SKIP_AUTH_DIRECTIVE_SDL = /* GraphQL */ `
10
19
  directive @skipAuth on FIELD_DEFINITION
11
20
  `;
12
- function defaultProtectAllValidateFn(params) {
13
- if (params.user == null && !params.fieldAuthDirectiveNode && !params.fieldAuthExtension) {
14
- const schemaCoordinate = `${params.objectType.name}.${params.fieldNode.name.value}`;
15
- return new UnauthenticatedError(`Accessing '${schemaCoordinate}' requires authentication.`, [params.fieldNode]);
16
- }
17
- }
18
- function defaultProtectSingleValidateFn(params) {
19
- if (params.user == null && (params.fieldAuthDirectiveNode || params.fieldAuthExtension)) {
20
- const schemaCoordinate = `${params.objectType.name}.${params.fieldNode.name.value}`;
21
- return new UnauthenticatedError(`Accessing '${schemaCoordinate}' requires authentication.`, [params.fieldNode]);
21
+ function defaultValidateFn(user, contextType) {
22
+ if (!user) {
23
+ throw new UnauthenticatedError('Unauthenticated!');
22
24
  }
23
25
  }
24
26
  const useGenericAuth = (options) => {
25
- var _a, _b;
26
- const contextFieldName = options.contextFieldName || 'currentUser';
27
- if (options.mode === 'protect-all' || options.mode === 'protect-single') {
28
- const directiveName = (_a = options.authDirectiveName) !== null && _a !== void 0 ? _a : (options.mode === 'protect-all' ? 'skipAuth' : 'auth');
29
- const validateUser = (_b = options.validateUser) !== null && _b !== void 0 ? _b : (options.mode === 'protect-all' ? defaultProtectAllValidateFn : defaultProtectSingleValidateFn);
30
- const extractAuthMeta = (input) => {
31
- var _a, _b, _c;
32
- return {
33
- fieldAuthExtension: (_a = input.extensions) === null || _a === void 0 ? void 0 : _a[directiveName],
34
- fieldAuthDirectiveNode: (_c = (_b = input.astNode) === null || _b === void 0 ? void 0 : _b.directives) === null || _c === void 0 ? void 0 : _c.find(directive => directive.name.value === directiveName),
35
- };
36
- };
27
+ const fieldName = options.contextFieldName || 'currentUser';
28
+ const validateUser = options.validateUser || defaultValidateFn;
29
+ if (options.mode === 'protect-all') {
37
30
  return {
38
- onPluginInit({ addPlugin }) {
39
- addPlugin(useExtendedValidation({
40
- rules: [
41
- function AuthorizationExtendedValidationRule(context, args) {
42
- const user = args.contextValue[contextFieldName];
43
- const handleField = (fieldNode, objectType) => {
44
- const field = objectType.getFields()[fieldNode.name.value];
45
- if (field == null) {
46
- // field is null/undefined if this is an introspection field
47
- return;
48
- }
49
- const { fieldAuthExtension, fieldAuthDirectiveNode } = extractAuthMeta(field);
50
- const error = validateUser({
51
- user,
52
- fieldNode,
53
- objectType,
54
- fieldAuthDirectiveNode,
55
- fieldAuthExtension,
56
- });
57
- if (error) {
58
- context.reportError(error);
59
- }
60
- };
61
- return {
62
- Field(node) {
63
- const fieldType = getNamedType(context.getParentType());
64
- if (isIntrospectionType(fieldType)) {
65
- return false;
66
- }
67
- if (isObjectType(fieldType)) {
68
- handleField(node, fieldType);
69
- }
70
- else if (isUnionType(fieldType)) {
71
- for (const objectType of fieldType.getTypes()) {
72
- handleField(node, objectType);
73
- }
74
- }
75
- else if (isInterfaceType(fieldType)) {
76
- for (const objectType of args.schema.getImplementations(fieldType).objects) {
77
- handleField(node, objectType);
78
- }
79
- }
80
- return undefined;
81
- },
82
- };
83
- },
84
- ],
85
- }));
86
- },
87
31
  async onContextBuilding({ context, extendContext }) {
88
32
  const user = await options.resolveUserFn(context);
89
33
  extendContext({
90
- [contextFieldName]: user,
34
+ [fieldName]: user,
35
+ validateUser,
91
36
  });
92
37
  },
38
+ onExecute() {
39
+ return {
40
+ async onResolverCalled({ args, root, context, info }) {
41
+ const authDirectiveNode = getDirective(info, options.authDirectiveName || 'skipAuth');
42
+ if (authDirectiveNode)
43
+ return;
44
+ await context.validateUser(context[fieldName], context);
45
+ },
46
+ };
47
+ },
93
48
  };
94
49
  }
95
50
  else if (options.mode === 'resolve-only') {
@@ -97,12 +52,39 @@ const useGenericAuth = (options) => {
97
52
  async onContextBuilding({ context, extendContext }) {
98
53
  const user = await options.resolveUserFn(context);
99
54
  extendContext({
100
- [contextFieldName]: user,
55
+ [fieldName]: user,
56
+ validateUser: () => validateUser(user, context),
57
+ });
58
+ },
59
+ };
60
+ }
61
+ else if (options.mode === 'protect-auth-directive') {
62
+ return {
63
+ async onContextBuilding({ context, extendContext }) {
64
+ const user = await options.resolveUserFn(context);
65
+ extendContext({
66
+ [fieldName]: user,
67
+ validateUser,
101
68
  });
102
69
  },
70
+ onExecute() {
71
+ return {
72
+ async onResolverCalled({ args, root, context, info }) {
73
+ const authDirectiveNode = getDirective(info, options.authDirectiveName || 'auth');
74
+ if (authDirectiveNode) {
75
+ await context.validateUser(context[fieldName], context, {
76
+ info,
77
+ context: context,
78
+ args,
79
+ root,
80
+ }, authDirectiveNode);
81
+ }
82
+ },
83
+ };
84
+ },
103
85
  };
104
86
  }
105
87
  return {};
106
88
  };
107
89
 
108
- export { DIRECTIVE_SDL, SKIP_AUTH_DIRECTIVE_SDL, UnauthenticatedError, defaultProtectAllValidateFn, defaultProtectSingleValidateFn, useGenericAuth };
90
+ export { DIRECTIVE_SDL, SKIP_AUTH_DIRECTIVE_SDL, UnauthenticatedError, defaultValidateFn, getDirective, useGenericAuth };
package/package.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "@envelop/generic-auth",
3
- "version": "2.0.0-alpha-c8eef8e.0",
3
+ "version": "2.0.1-alpha-7ca7cb8.0",
4
4
  "sideEffects": false,
5
5
  "peerDependencies": {
6
- "@envelop/core": "^1.6.1",
6
+ "@envelop/core": "1.7.2-alpha-7ca7cb8.0",
7
7
  "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
8
8
  },
9
- "dependencies": {
10
- "@envelop/extended-validation": "^1.3.1"
11
- },
12
9
  "repository": {
13
10
  "type": "git",
14
11
  "url": "https://github.com/dotansimha/envelop.git",
@@ -30,6 +27,7 @@
30
27
  "./*": {
31
28
  "require": "./*.js",
32
29
  "import": "./*.mjs"
33
- }
30
+ },
31
+ "./package.json": "./package.json"
34
32
  }
35
33
  }
package/utils.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { DirectiveNode, GraphQLResolveInfo } from 'graphql';
2
+ export declare function getDirective(info: GraphQLResolveInfo, name: string): null | DirectiveNode;