{"_id":"@emrys-myrddin/envelop-generic-auth","_rev":"1-02d63b4efca4812a4943c433768fcfaa","name":"@emrys-myrddin/envelop-generic-auth","dist-tags":{"latest":"4.5.0-fork.2"},"versions":{"4.5.0-fork.1":{"name":"@emrys-myrddin/envelop-generic-auth","version":"4.5.0-fork.1","author":{"name":"Dotan Simha","email":"dotansimha@gmail.com"},"license":"MIT","sideEffects":false,"repository":{"type":"git","url":"https://github.com/n1ru4l/envelop.git","directory":"packages/plugins/generic-auth"},"main":"dist/cjs/index.js","module":"dist/esm/index.js","exports":{".":{"require":{"types":"./dist/typings/index.d.cts","default":"./dist/cjs/index.js"},"import":{"types":"./dist/typings/index.d.ts","default":"./dist/esm/index.js"},"default":{"types":"./dist/typings/index.d.ts","default":"./dist/esm/index.js"}},"./*":{"require":{"types":"./dist/typings/*.d.cts","default":"./dist/cjs/*.js"},"import":{"types":"./dist/typings/*.d.ts","default":"./dist/esm/*.js"},"default":{"types":"./dist/typings/*.d.ts","default":"./dist/esm/*.js"}},"./package.json":"./package.json"},"typings":"dist/typings/index.d.ts","typescript":{"definition":"dist/typings/index.d.ts"},"dependencies":{"@envelop/extended-validation":"^1.9.0","tslib":"^2.4.0"},"devDependencies":{"graphql":"16.3.0","typescript":"4.7.4"},"peerDependencies":{"@envelop/core":"^2.6.0","graphql":"^14.0.0 || ^15.0.0 || ^16.0.0"},"buildOptions":{"input":"./src/index.ts"},"publishConfig":{"directory":"dist","access":"public"},"type":"module","description":"This plugin allows you to implement custom authentication flow by providing a custom user resolver based on the original HTTP request. The resolved user is injected into the GraphQL execution `context`, and you can use it in your resolvers to fetch the cu","_id":"@emrys-myrddin/envelop-generic-auth@4.5.0-fork.1","dist":{"shasum":"ad06357b40d6420f1b3cfa07f71d8384af5b30a4","integrity":"sha512-ZkeXaF01fgagBUjfhqdjnZkEoebO6LFAdH3d5Cf4apEVLQ3BE7U0YHI4lKXc60icW1JaV0LTZbfII62S0uVR9A==","tarball":"https://registry.npmjs.org/@emrys-myrddin/envelop-generic-auth/-/envelop-generic-auth-4.5.0-fork.1.tgz","fileCount":19,"unpackedSize":72034,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDAC7UsjnYQMNXISxUkaedOgIojSV2cK2ud234xC+XrvwIhAOU/siRD977PfxMx5XMWsr9kpDhqIFS17GWWKRJfzPnK"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjJZCYACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoC4g//bEaNS/AuDhThGSR0pvjNmdlSvkWsAWe6O+zeA6bcJW1qkUiS\r\n0El+KUa0eQysWNJh78DrQvp9BZbaXo/Cq3iDPeb802JS5ILWblVP5L7diQUM\r\ngq/MCNwGJyGNtxeY9KbAbPbP6P3geWIzwKtMELrMsoJovTRR2/XUYIulaXOo\r\nu4MHW1X6Vb20aqSq5bWRjn1oGYOkD1+j1AQqzgDnAPGoG/g9f5mC2etoYg+g\r\ntgP9bT33Zfc3zEbRvC1uHw1o8zl4BHH/kLxmWMKgebA7XIOl1eF+XUpqJOs3\r\nIankJxkXD5GuU+Ntsc4M6UsrysQ//9GC8MErkfj4nwpfaoZVI3Vv5GvRxyJ/\r\n725oa30Lyl8ANO6LZZ3AyLLjmy3h+YsC86LATm7Qz1Je8FsZCVLBm1oJitJ/\r\nSWJcY601zcHG4LBEn7LIwcREG/Wyj5n6jztInj6+FbCgEzrOmEsR31C6IxRB\r\nMipF9MdnpCFZHfdctiJuJp/wg6atGv/8PUYag/7c+77+FuA0kBB71V8n/jq9\r\nhKYm6zP1gEycYyJy9P+FfBZLMTvS4E7fsPoA6hG/O95hUr5NJqBYVrm5PsRJ\r\nn6a46IcqAhLEdRvRgkvR0l+21CIx/t4aaZyKU1cDr8b6fWaLn49VaY+RRvNi\r\n3B3pbCD8ypp4z4/h1GxPFlcwX8nClgWD6gE=\r\n=tt/T\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"emrys-myrddin","email":"v.cocaud@gmail.com"},"directories":{},"maintainers":[{"name":"emrys-myrddin","email":"v.cocaud@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/envelop-generic-auth_4.5.0-fork.1_1663406232024_0.5886467467370209"},"_hasShrinkwrap":false},"4.5.0-fork.2":{"name":"@emrys-myrddin/envelop-generic-auth","version":"4.5.0-fork.2","author":{"name":"Dotan Simha","email":"dotansimha@gmail.com"},"license":"MIT","sideEffects":false,"repository":{"type":"git","url":"https://github.com/n1ru4l/envelop.git","directory":"packages/plugins/generic-auth"},"main":"dist/cjs/index.js","module":"dist/esm/index.js","exports":{".":{"require":{"types":"./dist/typings/index.d.cts","default":"./dist/cjs/index.js"},"import":{"types":"./dist/typings/index.d.ts","default":"./dist/esm/index.js"},"default":{"types":"./dist/typings/index.d.ts","default":"./dist/esm/index.js"}},"./*":{"require":{"types":"./dist/typings/*.d.cts","default":"./dist/cjs/*.js"},"import":{"types":"./dist/typings/*.d.ts","default":"./dist/esm/*.js"},"default":{"types":"./dist/typings/*.d.ts","default":"./dist/esm/*.js"}},"./package.json":"./package.json"},"typings":"dist/typings/index.d.ts","typescript":{"definition":"dist/typings/index.d.ts"},"dependencies":{"@envelop/extended-validation":"^1.9.0","tslib":"^2.4.0"},"devDependencies":{"graphql":"16.3.0","typescript":"4.7.4"},"peerDependencies":{"@envelop/core":"^2.6.0","graphql":"^14.0.0 || ^15.0.0 || ^16.0.0"},"buildOptions":{"input":"./src/index.ts"},"publishConfig":{"directory":"dist","access":"public"},"type":"module","description":"This plugin allows you to implement custom authentication flow by providing a custom user resolver based on the original HTTP request. The resolved user is injected into the GraphQL execution `context`, and you can use it in your resolvers to fetch the cu","_id":"@emrys-myrddin/envelop-generic-auth@4.5.0-fork.2","dist":{"shasum":"704527a61576ce5600535aa6a00c6090abaf6d08","integrity":"sha512-lS+yjb4OabqUXkiX01JBbhBt7MegSTHFQCdjhHEHSbTVcW8r5Xt0ewTvk0yI0RuGgeW2W0QR00m5/1qkuAcfPQ==","tarball":"https://registry.npmjs.org/@emrys-myrddin/envelop-generic-auth/-/envelop-generic-auth-4.5.0-fork.2.tgz","fileCount":19,"unpackedSize":77481,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBXV6oodjT2xFpcAJDNLyKOxGQrK3yy+7uGVkFxYz4NCAiEAkqEr8GlfkhLTiMMOFi6d2VruyhC9Jove6Vplk8FmXv4="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjRx0yACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmoxyg/+NnCXh/OOtlCxuaPqrnrwARPHMFwnvpPzQ/Cj72IIGGHNiXeV\r\nkuglA6u5785Wh6k8YTVpNrPP+au6N31SyEUM3TvR6EJCybRe3UVHK2oDNbSO\r\ntv8wFX0ymfC/+AUTfzvXz8sBF/T7KRevuQPC+kNeyfh6Taq8NgKa/bONZVYn\r\n6/ZDw3rZ1WB6xtz8yRxAlOk65im4dptTqjgr8DkAffnTQdwts6kcOAJ4734Y\r\nX2o/MKX/iyy8S6GgMADGrPmCydJ7+0BOCCZAPTWh3MGXavurDwscfWk+Hi2Y\r\n0NHj5wRsQ4j88z4HLwODZuaVSZhhRmmOeENV33t8lOzZBRx2kYaMOWVQZfmW\r\nvD5sd7PaPBxAteXGrYMXG0AkPxmFSm26PKyLsKRhSoIUN/jI5rddd3WL9nm2\r\n4RpNk6aFsPiNsWTQ04JodGVQQknmKR054/W7p/hDPtfd/7klmRKApyDRrbT4\r\nCNs4PV7MKgN236GXsWzPd9QF6XmBTqUCOEv9xbZ5u2+PuJYYvZGuxnnZKKjH\r\nrK/jX68pF9k5P55Rj+tqxs+bXUSeqN2Io4oHThVFNMO+VoatYuSujcSEDDvz\r\nQQn1MVYJhxt8nEOzC/8CG+j3okeBce3iz0dnT2t7Ut55Me4DyRiOLAsSi2Ot\r\nGE7uxGNykyGBkqAuCYalGKmj/wlw0a6YEL4=\r\n=CLe0\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"emrys-myrddin","email":"v.cocaud@gmail.com"},"directories":{},"maintainers":[{"name":"emrys-myrddin","email":"v.cocaud@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/envelop-generic-auth_4.5.0-fork.2_1665604913973_0.14850043178293149"},"_hasShrinkwrap":false}},"time":{"created":"2022-09-17T09:17:11.870Z","4.5.0-fork.1":"2022-09-17T09:17:12.225Z","modified":"2022-10-12T20:01:54.262Z","4.5.0-fork.2":"2022-10-12T20:01:54.189Z"},"maintainers":[{"name":"emrys-myrddin","email":"v.cocaud@gmail.com"}],"description":"This plugin allows you to implement custom authentication flow by providing a custom user resolver based on the original HTTP request. The resolved user is injected into the GraphQL execution `context`, and you can use it in your resolvers to fetch the cu","repository":{"type":"git","url":"https://github.com/n1ru4l/envelop.git","directory":"packages/plugins/generic-auth"},"author":{"name":"Dotan Simha","email":"dotansimha@gmail.com"},"license":"MIT","readme":"## `@envelop/generic-auth`\n\nThis plugin allows you to implement custom authentication flow by providing a custom user resolver based on the original HTTP request. The resolved user is injected into the GraphQL execution `context`, and you can use it in your resolvers to fetch the current user.\n\n> The plugin also comes with an optional `@auth` directive that can be added to your GraphQL schema and helps you to protect your GraphQL schema in a declarative way.\n\nThere are several possible flows for using this plugin (see below for setup examples):\n\n- **Option #1 - Complete Protection**: protected the entire GraphQL schema from unauthenticated access. Allow unauthenticated access for certain fields by annotating them with a `@skipAuth` directive or `skipAuth` field extension.\n- **Option #2 - Manual Validation**: the plugin will just resolve the user and injects it into the `context` without validating access to schema field.\n- **Option #3 - Granular field access by using schema field directives or field extensions**: Look for an `@auth` directive or `auth` extension field and automatically protect those specific GraphQL fields.\n\n## Getting Started\n\nStart by installing the plugin:\n\n```\nyarn add @envelop/generic-auth\n```\n\nThen, define your authentication methods:\n\n1. Resolve your user from the request by implementing `resolveUserFn`:\n\nUse this method to only extract the user from the context, with any custom code, for example:\n\n```ts\nimport { ResolveUserFn } from '@envelop/generic-auth'\n\ntype UserType = {\n  id: string\n}\n\nconst resolveUserFn: ResolveUserFn<UserType> = async context => {\n  // Here you can implement any custom sync/async code, and use the context built so far in Envelop and the HTTP request\n  // to find the current user.\n  // Common practice is to use a JWT token here, validate it, and use the payload as-is, or fetch the user from an external services.\n  // Make sure to either return `null` or the user object.\n\n  try {\n    const user = await context.authApi.authenticateUser(context.req.headers.authorization)\n\n    return user\n  } catch (e) {\n    console.error('Failed to validate token')\n\n    return null\n  }\n}\n```\n\n2. Define an optional validation method by implementing `validateUser`:\n\nThis method is optional; the default method will just verify the value returned by `resolveUser` and throw an error in case of a false value (`false | null | undefined`).\n\n```ts\nimport { ValidateUserFn } from '@envelop/generic-auth'\n\nconst validateUser: ValidateUserFn<UserType> = params => {\n  // Here you can implement any custom to check if the user is valid and have access to the server.\n  // This method is being triggered in different flows, based on the mode you chose to implement.\n\n  // If you are using the `protect-auth-directive` mode, you'll also get 2 additional parameters: the resolver parameters as object and the DirectiveNode of the auth directive.\n  // In `protect-auth-directive` mode, this function will always get called and you can use these parameters to check if the field has the `@auth` or `@skipAuth` directive\n\n  if (!user) {\n    throw new Error(`Unauthenticated!`)\n  }\n}\n```\n\nNow, configure your plugin based on the mode you wish to use:\n\n#### Option #1 - `protect-all`\n\nThis mode offers complete protection for the entire API. It protects your entire GraphQL schema by validating the user before executing the request. You can optionally skip auth validation for specific GraphQL fields by using the `@skipAuth` directive.\n\nTo setup this mode, use the following config:\n\n```ts\nimport { envelop } from '@envelop/core'\nimport { useGenericAuth, ResolveUserFn, ValidateUserFn } from '@envelop/generic-auth'\n\ntype UserType = {\n  id: string\n}\nconst resolveUserFn: ResolveUserFn<UserType> = async context => {\n  /* ... */\n}\nconst validateUser: ValidateUserFn<UserType> = params => {\n  /* ... */\n}\n\nconst getEnveloped = envelop({\n  plugins: [\n    // ... other plugins ...\n    useGenericAuth({\n      resolveUserFn,\n      validateUser,\n      mode: 'protect-all'\n    })\n  ]\n})\n```\n\n##### Allow unauthenticated access for specific fields using a field `directive`\n\n> By default, we assume that you have the GraphQL directive definition as part of your GraphQL schema (`directive @skipAuth on FIELD_DEFINITION`).\n\nThen, in your GraphQL schema SDL, you can add `@skipAuth` directive to your fields, and the default `validateUser` function will not get called while resolving that specific field:\n\n```graphql\ntype Query {\n  me: User!\n  protectedField: String\n  publicField: String @skipAuth\n}\n```\n\n> You can apply that directive to any GraphQL `field` definition, not only to root fields.\n\n> If you are using a different directive for authentication, you can pass `directiveOrExtensionFieldName` configuration to customize it.\n\n##### Allow unauthenticated access for specific fields using a field extension\n\n```typescript\nimport { GraphQLObjectType, GraphQLInt } from 'graphql'\n\nconst GraphQLQueryType = new GraphQLObjectType({\n  name: 'Query',\n  fields: {\n    foo: {\n      type: GraphQLInt,\n      resolve: () => 1,\n      extensions: {\n        skipAuth: true\n      }\n    }\n  }\n})\n```\n\n> If you want to use a different directive for authentication, you can use the `directiveOrExtensionFieldName` configuration to customize it.\n\n#### Option #2 - `resolve-only`\n\nThis mode uses the plugin to inject the authenticated user into the `context`, and later you can verify it in your resolvers.\n\n```ts\nimport { envelop } from '@envelop/core'\nimport { useGenericAuth, ResolveUserFn, ValidateUserFn } from '@envelop/generic-auth'\n\ntype UserType = {\n  id: string\n}\nconst resolveUserFn: ResolveUserFn<UserType> = async context => {\n  /* ... */\n}\nconst validateUser: ValidateUserFn<UserType> = async params => {\n  /* ... */\n}\n\nconst getEnveloped = envelop({\n  plugins: [\n    // ... other plugins ...\n    useGenericAuth({\n      resolveUserFn,\n      validateUser,\n      mode: 'resolve-only'\n    })\n  ]\n})\n```\n\nThen, in your resolvers, you can execute the check method based on your needs:\n\n```ts\nconst resolvers = {\n  Query: {\n    me: async (root, args, context) => {\n      await context.validateUser()\n      const currentUser = context.currentUser\n\n      return currentUser\n    }\n  }\n}\n```\n\n#### Option #3 - `protect-granular`\n\nThis mode is similar to option #2, but it uses the `@auth` SDL directive or `auth` field extension for protecting specific GraphQL fields.\n\n```ts\nimport { envelop } from '@envelop/core'\nimport { useGenericAuth, ResolveUserFn, ValidateUserFn } from '@envelop/generic-auth'\n\ntype UserType = {\n  id: string\n}\nconst resolveUserFn: ResolveUserFn<UserType> = async context => {\n  /* ... */\n}\nconst validateUser: ValidateUserFn<UserType> = params => {\n  /* ... */\n}\n\nconst getEnveloped = envelop({\n  plugins: [\n    // ... other plugins ...\n    useGenericAuth({\n      resolveUserFn,\n      validateUser,\n      mode: 'protect-granular'\n    })\n  ]\n})\n```\n\n##### Protect a field using a field `directive`\n\n> By default, we assume that you have the GraphQL directive definition as part of your GraphQL schema (`directive @auth on FIELD_DEFINITION`).\n\nThen, in your GraphQL schema SDL, you can add `@auth` directive to your fields, and the `validateUser` will get called only while resolving that specific field:\n\n```graphql\ntype Query {\n  me: User! @auth\n  protectedField: String @auth\n  # publicField: String\n}\n```\n\n> You can apply that directive to any GraphQL `field` definition, not only to root fields.\n\n> If you are using a different directive for authentication, you can pass `directiveOrExtensionFieldName` configuration to customize it.\n\n##### Protect a field using a field extension\n\n```typescript\nimport { GraphQLObjectType, GraphQLInt } from 'graphql'\n\nconst GraphQLQueryType = new GraphQLObjectType({\n  name: 'Query',\n  fields: {\n    foo: {\n      type: GraphQLInt,\n      resolve: () => 1,\n      extensions: {\n        auth: true\n      }\n    }\n  }\n})\n```\n\n> If you are using a different field extension for authentication, you can pass `directiveOrExtensionFieldName` configuration to customize it.\n\n#### Extend authentication with custom logic\n\nYou can also specify a custom `validateUser` function and get access to a handy object while using the `protect-all` and `protect-granular` mode:\n\n```ts\nimport { GraphQLError } from 'graphql'\nimport { ValidateUserFn } from '@envelop/generic-auth'\n\nconst validateUser: ValidateUserFn<UserType> = async ({ user }) => {\n  // Now you can use the 3rd parameter to implement custom logic for user validation, with access\n  // to the resolver data and information.\n\n  if (!user) {\n    return new GraphQLError(`Unauthenticated.`)\n  }\n}\n```\n\n##### With a custom directive with arguments\n\nIt is possible to add custom parameters to your `@auth` directive. Here's an example for adding role-aware authentication:\n\n```graphql\nenum Role {\n  ADMIN\n  MEMBER\n}\n\ndirective @auth(role: Role!) on FIELD_DEFINITION\n```\n\nThen, you use the `directiveNode` parameter to check the arguments:\n\n```ts\nimport { ValidateUserFn } from '@envelop/generic-auth'\n\nconst validateUser: ValidateUserFn<UserType> = async ({ user, fieldAuthDirectiveNode }) => {\n  // Now you can use the fieldAuthDirectiveNode parameter to implement custom logic for user validation, with access\n  // to the resolver auth directive arguments.\n\n  if (!user) {\n    throw new Error(`Unauthenticated!`)\n  }\n\n  const valueNode = fieldAuthDirectiveNode.arguments.find(arg => arg.name.value === 'role').value as EnumValueNode\n  const role = valueNode.value\n\n  if (role !== user.role) {\n    throw new Error(`No permissions!`)\n  }\n}\n```\n\n##### With a custom field extensions\n\nYou can use custom field extension to pass data to your `validateUser` function instead of using a directive.\nHere's an example for adding role-aware authentication:\n\n```ts\nimport { ValidateUserFn } from '@envelop/generic-auth'\n\nconst validateUser: ValidateUserFn<UserType> = async ({ user, fieldAuthExtension }) => {\n  // Now you can use the fieldAuthDirectiveNode parameter to implement custom logic for user validation, with access\n  // to the resolver auth directive arguments.\n\n  if (!user) {\n    throw new Error(`Unauthenticated!`)\n  }\n\n  const role = fieldAuthExtension.role\n\n  if (role !== user.role) {\n    throw new Error(`No permissions!`)\n  }\n}\n\nconst resolvers = {\n  Query: {\n    user: {\n      me: (_, __, { currentUser }) => currentUser,\n      extensions: {\n        auth: {\n          role: 'USER'\n        }\n      }\n    }\n  }\n}\n```\n\n##### With a custom validation function per field\n\nYou can also have access to operation variables and context via the `executionArgs` parameter.\nThis can be useful in conjunction with the `fieldAuthExtension` parameter to achieve custom per field validation.\n\n```ts\nimport { ValidateUserFn } from '@envelop/generic-auth'\n\nconst validateUser: ValidateUserFn<UserType> = async ({ user, executionArgs, fieldAuthExtension }) => {\n  if (!user) {\n    throw new Error(`Unauthenticated!`)\n  }\n\n  // You have access to the object define in the resolver tree, allowing to define any custom logic you want.\n  const validate = fieldAuthExtension?.validate\n  if (validate) {\n    await validate({ user, variables: executionArgs.variableValues, context: executionArgs.contextValue })\n  }\n}\n\nconst resolvers = {\n  Query: {\n    user: {\n      resolve: (_, { userId }) => getUser(userId),\n      extensions: {\n        auth: {\n          validate: ({ user, variables, context }) => {\n            // We can now have access to the operation and variables to decide if the user can execute the query\n            if (user.id !== variables.userId) {\n              throw new Error(`Unauthorized`)\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n","readmeFilename":"README.md"}