{"id":13739326,"url":"https://github.com/AstrumU/graphql-authz","last_synced_at":"2025-05-08T19:33:57.047Z","repository":{"id":40702602,"uuid":"385019125","full_name":"AstrumU/graphql-authz","owner":"AstrumU","description":"GraphQL authorization layer","archived":false,"fork":false,"pushed_at":"2024-08-12T16:09:32.000Z","size":1111,"stargazers_count":192,"open_issues_count":24,"forks_count":10,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-05T05:36:23.352Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AstrumU.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-11T18:02:25.000Z","updated_at":"2025-04-28T06:36:35.000Z","dependencies_parsed_at":"2023-11-30T16:25:57.312Z","dependency_job_id":"706d18fb-dc3c-45b4-89aa-83c7610bd37c","html_url":"https://github.com/AstrumU/graphql-authz","commit_stats":{"total_commits":96,"total_committers":8,"mean_commits":12.0,"dds":"0.48958333333333337","last_synced_commit":"9f3bebfe3dbb1fb7a07d3f301c6da3f03dc8a644"},"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AstrumU%2Fgraphql-authz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AstrumU%2Fgraphql-authz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AstrumU%2Fgraphql-authz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AstrumU%2Fgraphql-authz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AstrumU","download_url":"https://codeload.github.com/AstrumU/graphql-authz/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253135464,"owners_count":21859647,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T04:00:32.456Z","updated_at":"2025-05-08T19:33:56.668Z","avatar_url":"https://github.com/AstrumU.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","Defensive Security"],"sub_categories":["Authentication \u0026 Authorization"],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"./img/graphql-authz-gh-cover.png\" height=\"250px\" /\u003e\u003c/p\u003e\n\n# \n\n\u003cp align=\"center\"\u003eGraphQL authorization layer, flexible, (not only) directive-based, compatible with all modern GraphQL architectures.\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://codecov.io/gh/AstrumU/graphql-authz\"\u003e\u003cimg src=\"https://codecov.io/gh/AstrumU/graphql-authz/branch/main/graph/badge.svg?token=5IMVNTLQGF\" height=\"20\"/\u003e\u003c/a\u003e\n  \u003ca href=\".\"\u003e\u003cimg src=\"https://badgen.net/github/checks/AstrumU/graphql-authz/main\" height=\"20\"/\u003e\u003c/a\u003e\n  \u003ca href=\"http://www.typescriptlang.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg\" height=\"20\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@graphql-authz/core\"\u003e\u003cimg src=\"https://badgen.net/npm/v/@graphql-authz/core\" height=\"20\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@graphql-authz/core\"\u003e\u003cimg src=\"https://badgen.net/npm/types/@graphql-authz/core\" height=\"20\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\n## Overview\n\nFlexible modern way of adding an authorization layer on top of your existing GraphQL microservices or monolith backend systems.\n\nFull overview can be found in this blog post: [https://the-guild.dev/blog/graphql-authz](https://the-guild.dev/blog/graphql-authz)\n\n## Features\n- Attaching rules to Query/Mutation/Object/Interface/Field\n- Attaching rules using directives, extensions or authSchema\n- Pre and Post execution rules\n- Any sync/async JS code inside rules\n- Compatible with all modern GraphQL architectures\n\n## Examples\n\nCheck examples in `examples` folder:\n- [Apollo Server (schema-first, directives)](examples/packages/apollo-server-schema-first)\n- [Apollo Server (code-first, extensions)](examples/packages/apollo-server-code-first)\n- [express-graphql (schema-first, directives)](examples/packages/express-graphql)\n- [GraphQL Helix (schema-first, authSchema)](examples/packages/graphql-helix)\n- [Envelop (schema-first, directives)](examples/packages/envelop)\n- [TypeGraphQL (code-first, extensions)](examples/packages/type-graphql)\n- [NestJS (code-first, directives)](examples/packages/nestjs)\n- [Schema Stitching (gateway, directives)](examples/packages/schema-stitching)\n- [Apollo Federation (gateway, authSchema)](examples/packages/apollo-federation)\n\n## Integration\n\nTo integrate graphql-authz into the project you can follow several steps depending on your architecture:\n\n\n\n- [Installation](#installation)\n- [Creating Rules](#creating-rules)\n- [Configuring server](#configuring-server)\n  - [Configuring Apollo Server plugin](#configuring-apollo-server-plugin)\n  - [Configuring Envelop plugin](#configuring-envelop-plugin)\n  - [Configuring express-graphql](#configuring-express-graphql)\n  - [Configuring graphql-helix](#configuring-graphql-helix)\n- [Configuring schema for directive usage](#configuring-schema-for-directive-usage)\n  - [Schema First](#schema-first)\n  - [Code First](#code-first)\n- [Attaching rules](#attaching-rules)\n  - [Using Directives](#using-directives)\n    - [Schema-First](#schema-first)\n    - [Code-First](#code-first)\n  - [Using extensions (Code-First only)](#using-extensions-code-first-only)\n    - [Using TypeGraphQL or NestJS decorators](#using-typegraphql-or-nestjs-decorators)\n    - [Using GraphQL.js constructors](#using-graphqljs-constructors)\n    - [Using GiraphQL](#using-giraphql)\n  - [Using AuthSchema](#using-authschema)\n\nRead more\n- [Pre execution rules vs Post execution rules](#pre-execution-rules-vs-post-execution-rules)  \n- [Wildcard rules](#wildcard-rules)\n- [Composing rules](#composing-rules)\n- [Custom errors](#custom-errors)\n\n\n## Installation\n\n`yarn add @graphql-authz/core`\n\n## Creating Rules\n\nCreate rule and `rules` object\n```ts\nimport { preExecRule } from \"@graphql-authz/core\";\n\nconst IsAuthenticated = preExecRule()(context =\u003e !!context.user);\n\nconst rules = {\n  IsAuthenticated\n} as const;\n```\n\nAlternatively you can create a rule as a class\n  ```ts\n  import { PreExecutionRule } from \"@graphql-authz/core\";\n\n  class IsAuthenticated extends PreExecutionRule {\n    public execute(context) {\n      if (!context.user) {\n        throw this.error;\n      }\n    }\n  }\n\n  const rules = {\n    IsAuthenticated\n  } as const;\n  ```\n\n## Configuring server\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cstrong id=\"configuring-apollo-server-plugin\"\u003eConfiguring Apollo Server plugin\u003c/strong\u003e\n  \u003c/summary\u003e\n\n  See [Apollo Server plugin readme](packages/plugins/apollo-server/README.md)\n  or check examples:\n\n  [Apollo Server (schema-first, directives)](examples/packages/apollo-server-schema-first)\n\n  [Apollo Server (code-first, extensions)](examples/packages/apollo-server-code-first)\n\n\u003c/details\u003e\n\u003cbr\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cstrong id=\"configuring-envelop-plugin\"\u003eConfiguring Envelop plugin\u003c/strong\u003e\n  \u003c/summary\u003e\n\n  See [Envelop plugin readme](packages/plugins/envelop/README.md) or check examples:\n  \n  [Envelop (schema-first, directives)](examples/packages/envelop)\n\u003c/details\u003e\n\u003cbr\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cstrong id=\"configuring-express-graphql\"\u003eConfiguring express-graphql\u003c/strong\u003e\n  \u003c/summary\u003e\n\n  Ensure authenticator is configured to add user info to the request object\n  ```ts\n  const authenticator = (req, res, next) =\u003e {\n    const user = someHowAuthenticateUser(req.get(\"authorization\")));\n    req.user = user;\n    next();\n  };\n  \n  app.use(authenticator);\n  ```\n  \n  Provide `customExecuteFn` to `graphqlHTTP`\n  ```ts\n  import { execute } from 'graphql';\n  import { graphqlHTTP } from 'express-graphql';\n  import { wrapExecuteFn } from '@graphql-authz/core';\n  \n  graphqlHTTP({\n    ...\n    customExecuteFn: wrapExecuteFn(execute, { rules }),\n    ...\n  })\n  ```\n  \n  ### For Directives usage\n  \n  Apply directive transformer to schema\n  ```ts\n  import { authZDirective } from '@graphql-authz/directive';\n  \n  const { authZDirectiveTransformer } = authZDirective();\n  \n  graphqlHTTP({\n      ...\n      schema: authZDirectiveTransformer(schema),\n      customExecuteFn: wrapExecuteFn(execute, { rules }),\n      ...\n    })\n  ```\n  \n  ### For AuthSchema usage\n  \n  Pass additional parameter `authSchema` to `wrapExecuteFn`\n  ```ts\n  import { execute } from 'graphql';\n  import { wrapExecuteFn } from '@graphql-authz/core';\n  \n  graphqlHTTP({\n    ...\n    customExecuteFn: wrapExecuteFn(execute, { rules: authZRules, authSchema }),\n    ...\n  })\n  ```\n  \n  \n  Check an example: [express-graphql (schema-first, directives)](examples/packages/express-graphql)\n\n\u003c/details\u003e\n\u003cbr\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003cstrong id=\"configuring-graphql-helix\"\u003eConfiguring GraphQL Helix\u003c/strong\u003e\n  \u003c/summary\u003e\n\n  Ensure context parser is configured to perform authentication and add user info to context\n  ```ts\n  import { processRequest } from 'graphql-helix';\n  \n  processRequest({\n    ...\n    contextFactory: () =\u003e ({\n      user: someHowAuthenticateUser(req.get(\"authorization\")))\n    }),\n    ...\n  })\n  ```\n  \n  Provide `execute` option to `processRequest`\n  ```ts\n  import { execute } from 'graphql';\n  import { processRequest } from 'graphql-helix';\n  import { wrapExecuteFn } from '@graphql-authz/core';\n  \n  processRequest({\n    ...\n    execute: wrapExecuteFn(execute, { rules })\n    ...\n  })\n  ```\n  \n  ### For Directives usage\n  \n  Apply directive transformer to schema\n  ```ts\n  import { authZDirective } from '@graphql-authz/directive';\n  \n  const { authZDirectiveTransformer } = authZDirective();\n  \n  processRequest({\n      ...\n      schema: authZDirectiveTransformer(schema),\n      execute: wrapExecuteFn(execute, { rules }),\n      ...\n    })\n  ```\n  \n  ### For AuthSchema usage\n  \n  Pass additional parameter `authSchema` to `wrapExecuteFn`\n  ```ts\n  import { execute } from 'graphql';\n  import { processRequest } from 'graphql-helix';\n  import { wrapExecuteFn } from '@graphql-authz/core';\n  \n  processRequest({\n    ...\n    execute: wrapExecuteFn(execute, { rules, authSchema })\n    ...\n  })\n  ```\n  \n  Check an example: [GraphQL Helix (schema-first, authSchema)](examples/packages/graphql-helix)\n\u003c/details\u003e\n\u003cbr\u003e\n\n## Configuring schema for directive usage\n\n### Schema First \nAdd rules and directive definition to the schema\n  ```ts\n    # this is custom list of your rules\n    enum AuthZRules {\n      IsAuthenticated\n      IsAdmin\n      CanReadPost\n      CanPublishPost\n    }\n\n    # this is a common boilerplate\n    input AuthZDirectiveCompositeRulesInput {\n      and: [AuthZRules]\n      or: [AuthZRules]\n      not: AuthZRules\n    }\n\n    # this is a common boilerplate\n    input AuthZDirectiveDeepCompositeRulesInput {\n      id: AuthZRules\n      and: [AuthZDirectiveDeepCompositeRulesInput]\n      or: [AuthZDirectiveDeepCompositeRulesInput]\n      not: AuthZDirectiveDeepCompositeRulesInput\n    }\n\n    # this is a common boilerplate\n    directive @authz(\n      rules: [AuthZRules]\n      compositeRules: [AuthZDirectiveCompositeRulesInput]\n      deepCompositeRules: [AuthZDirectiveDeepCompositeRulesInput]\n    ) on FIELD_DEFINITION | OBJECT | INTERFACE\n  ```\nAlternatively generate rules and directive definition\n  ```ts\n  import { directiveTypeDefs } from \"@graphql-authz/core\";\n  import { authZGraphQLDirective } from \"@graphql-authz/directive\";\n\n  const directive = authZGraphQLDirective(rules);\n  const authZDirectiveTypeDefs = directiveTypeDefs(directive);\n\n  const typeDefs = gql`\n    ${authZDirectiveTypeDefs}\n\n    ...\n\n  `;\n  ```\n\n### Code First\nPass directive to schema options to generate rules and directive definition\n  ```ts\n  import { authZGraphQLDirective } from \"@graphql-authz/directive\";\n\n  new GraphQLSchema({\n    ...\n    directives: [authZGraphQLDirective(rules)]\n    ...\n  });\n  ```\n\n## Attaching rules\n\n### Using Directives\n\nAdd authz directive to Query/Mutation/Object/Interface/Field you need to perform authorization on\n\n#### Schema-First\n\n```ts\ninterface TestInterface @authz(rules: [Rule01]) {\n  testField1: String! @authz(rules: [Rule02])\n}\n\ntype TestType implements TestInterface @authz(rules: [Rule01]) {\n  testField1: String!\n  testField2: Float! @authz(rules: [Rule02])\n}\n\ntype Query {\n  testQuery: TestType! @authz(rules: [Rule01, Rule02])\n}\n\ntype Mutation {\n  testMutation: TestType! @authz(rules: [Rule01, Rule02])\n}\n```\n\n#### Code-First\n\nUsing [TypeGraphQL](https://typegraphql.com/) or [NestJS](https://nestjs.com/) decorators\n```ts\n@InterfaceType()\n@Directive(`@authz(rules: [Rule01]`)\nclass TestInterface {\n  @Field()\n  @Directive(`@authz(rules: [Rule02])`)\n  public testField1!: string;\n}\n\n@ObjectType({ implements: TestInterface })\n@Directive(`@authz(rules: [Rule01])`)\nclass TestType {\n  @Field()\n  public testField1!: string;\n\n  @Field()\n  @Directive(`@authz(rules: [Rule02])`)\n  public testField2!: number;\n}\n\n@Resolver()\nclass ResolverClass {\n\n  @Query(() =\u003e TestType)\n  @Directive(`@authz(rules: [Rule01, Rule02])`)\n  public testQuery() {\n    ...\n  }\n\n  @Mutation(() =\u003e TestType)\n  @Directive(`@authz(rules: [Rule01, Rule02])`)\n  public testMutation() {\n    ...\n  }\n}\n```\nAlternatively custom decorator-wrapper can be used\n```ts\nimport { IAuthConfig } from \"@graphql-authz/core\";\n\nfunction AuthZ(config: IAuthConfig\u003ctypeof rules\u003e) {\n  const args = Object.keys(config)\n    .map(\n      key =\u003e `${key}: ${JSON.stringify(config[key]).replace(/\"/g, '')}`\n    )\n    .join(', ');\n  const directiveArgs = `(${args})`;\n  return Directive(`@authz${directiveArgs}`);\n}\n\n@InterfaceType()\n@AuthZ({ rules: ['Rule01'] })\nclass TestInterface {\n  @Field()\n  @AuthZ({ rules: ['Rule02'] })\n  public testField1!: string;\n}\n\n```\n\n### Using extensions (Code-First only)\n\n#### Using [TypeGraphQL](https://typegraphql.com/) or [NestJS](https://nestjs.com/) decorators\n\nIt looks pretty similar to the directive usage with decorators\n```ts\nimport { IAuthConfig } from \"@graphql-authz/core\";\n\nfunction AuthZ(args: IAuthConfig\u003ctypeof rules\u003e) {\n  return Extensions({\n    authz: {\n      directives: [\n        {\n          name: 'authz',\n          arguments: args\n        }\n      ]\n    }\n  });\n}\n\n@InterfaceType()\n@AuthZ({ rules: ['Rule01'] })\nclass TestInterface {\n  @Field()\n  @AuthZ({ rules: ['Rule02'] })\n  public testField1!: string;\n}\n```\n#### Using [GraphQL.js](https://graphql.org/graphql-js/) constructors\n```ts\nimport { IAuthConfig } from \"@graphql-authz/core\";\n\nfunction createAuthZExtensions(args: IAuthConfig\u003ctypeof rules\u003e) {\n  return {\n    authz: {\n      directives: [\n        {\n          name: 'authz',\n          arguments: args\n        }\n      ]\n    }\n  };\n}\n\nconst Post = new GraphQLObjectType({\n  name: 'Post',\n  extensions: createAuthZExtensions({\n    rules: ['CanReadPost']\n  }),\n  fields: () =\u003e ({\n    ...\n  })\n});\n\nconst Query = new GraphQLObjectType({\n  name: 'Query',\n  fields: {\n    post: {\n      type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(Post))),\n      extensions: createAuthZExtensions({\n        rules: ['IsAuthenticated']\n      }),\n      ...\n    }\n  }\n});\n```\n\n#### Using [GiraphQL](https://giraphql.com/)\n```ts\nimport SchemaBuilder from '@giraphql/core';\nimport AuthzPlugin from '@giraphql/plugin-authz';\n\nconst builder = new SchemaBuilder\u003c{\n  AuthZRule: keyof typeof rules;\n}\u003e({\n  plugins: [AuthzPlugin],\n});\n\nbuilder.queryType({\n  fields: (t) =\u003e ({\n    users: t.field({\n      type: [User],\n      authz: {\n        rules: ['IsAuthenticated'],\n      },\n      resolve: () =\u003e users,\n    }),\n  }),\n});\n\nconst Post = builder.objectRef\u003cIPost\u003e('Post');\n\nPost.implement({\n  authz: {\n    rules: ['CanReadPost'],\n  },\n  fields: (t) =\u003e ({\n    id: t.exposeID('id'),\n  }),\n});\n```\n\n[GiraphQL](https://giraphql.com/) provides a native GraphQL Authz plugin [@giraphql/plugin-authz](https://giraphql.com/plugins/authz) that helps to attach auth rules to Queries/Mutations/Objects/Interfaces/Fields in a type-safe way\n\n### Using AuthSchema\n\n  ```ts\n  const authSchema = {\n    Post: { __authz: { rules: ['CanReadPost'] } },\n    User: {\n      email: { __authz: { rules: ['IsAdmin'] } }\n    },\n    Mutation: {\n      publishPost: { __authz: { rules: ['CanPublishPost'] } }\n    },\n    Query: {\n      users: { __authz: { rules: ['IsAuthenticated'] } }\n    }\n  };\n  ```\n## Pre execution rules vs Post execution rules\n\n**Pre execution rules** are executed ***before*** any resolvers are called and have `context` and `fieldArgs` as arguments.\n\n`context` is GraphQL context\n\n`fieldArgs` is an object that has all arguments passed to `Query`/`Mutation`/`Field` the rule is applied to. If the rule is applied to `ObjectType` or `InterfaceType` then `fieldArgs` is an empty object.\n\nIf no other data is needed to perform authorization (e.g. actual data from the database or actual response to the request) then the Pre execution rule should be used.\n\nTo create simple Pre execution rule `preExecRule` function should be used\n```ts\nimport { preExecRule } from \"@graphql-authz/core\";\n\nconst IsAuthenticated = preExecRule()(context =\u003e !!context.user);\n  ```\n\nAlternatively new class extended from `PreExecutionRule` could be created\n\n```ts\nimport { PreExecutionRule } from \"@graphql-authz/core\";\n\nclass IsAuthenticated extends PreExecutionRule {\n  public execute(context) {\n    if (!context.user) {\n      throw this.error;\n    }\n  }\n}\n```\n\n**Post execution rules** are executed ***after*** all resolvers logic is executed and the result response is ready. Because of that fact, Post execution rules have access to the actual result of query execution. This can resolve authorization cases when the decision depends on attributes of the requested entity, for example, if only the author of the post can see the post if it is in draft status.\n\nIn order to create Post execution rule `postExecRule` function should be used\n```ts\nconst CanReadPost = postExecRule({\n  selectionSet: '{ status author { id } }'\n})(\n  (\n    context: IContext,\n    fieldArgs: unknown,\n    post: { status: string; author: { id: string } },\n    parent: unknown\n  ) =\u003e\n    post.status === 'public' || context.user?.id === post.author.id\n);\n```\n\nAlternatively a new class extended from `PostExecutionRule` could be created\n\n```ts\nclass CanReadPost extends PostExecutionRule {\n  public execute(\n    context: IContext,\n    fieldArgs: any,\n    post: Post,\n    parent: unknown\n  ) {\n    if (post.status !== \"public\" \u0026\u0026 context.user?.id !== post.author.id) {\n      throw this.error;\n    }\n  }\n\n  public selectionSet = \"{ status author { id } }\";\n}\n```\n\nBy adding `selectionSet` we ensure that even if the client originally didn't request fields that are needed to perform authorization these fields are present in the result value that comes to a rule as an argument.\n\n**Please note** that with post execution rules resolvers are executed even if the rule throws an unauthorized error, so such rules are not suitable for Mutations or Queries that update some counters (count of views for example)\n\n### Alternative by querying DB inside rules\n\nAll rules can be async so for cases where authorization depends on real data and should be performed strictly before resolvers execution (for Mutation or Queries that update some counters) you can query DB right inside pre execution rule\n\n```ts\nconst CanPublishPost = preExecRule()(\n  async (\n    context: IContext,\n    fieldArgs: { postId: string }\n  ) =\u003e {\n    const post = await db.posts.get(fieldArgs.postId);\n    return post.authorId === context.user?.id;\n  }\n);\n```\n\n### Alternative by querying schema itself inside rules\n\ngraphql-authz doesn't mutate executable schema so you can query schema itself right inside Pre execution rule\n\n**Note:** you need to pass schema to context to access it inside rules\n\n```ts\nconst CanPublishPost = preExecRule()(\n  async (\n    context: IContext,\n    fieldArgs: { postId: string }\n  ) =\u003e {\n    // query executable schema from rules\n    const graphQLResult = await graphql({\n      schema: context.schema,\n      source: `query post { post(id: ${fieldArgs.postId}) { author { id } } }`\n    });\n\n    const post = graphQLResult.data?.post;\n\n    return post \u0026\u0026 post.author.id === context.user?.id;\n  }\n);\n```\n\n## Wildcard rules\n\nAttaching rules using wildcards is supported by authSchema. Wildcard can be used as a name of Object and as a name of field in different combinations. Here are some examples:\n\n```ts\n  // Reject rule is attached to every Object\n  const authSchema = {\n    '*': { __authz: { rules: ['Reject'] } }\n  };\n```\n\n```ts\n  // Reject rule is attached to every Field of every Object\n  const authSchema = {\n    '*': {\n      '*': { __authz: { rules: ['Reject'] } }\n    }\n  };\n```\n\n```ts\n  // Reject rule is attached to every Object AND every Field of every Object\n  const authSchema = {\n    '*': {\n      { __authz: { rules: ['Reject'] } },\n      '*': { __authz: { rules: ['Reject'] } }\n    }\n  };\n```\n\n```ts\n  // Reject rule is attached to every Field of User Object\n  const authSchema = {\n    User: {\n      '*': { __authz: { rules: ['Reject'] } }\n    }\n  };\n```\n\n```ts\n  // Reject rule is attached to createdAt Field of every Object\n  const authSchema = {\n    '*': {\n      createdAt: { __authz: { rules: ['Reject'] } }\n    }\n  };\n```\n\n### Overwriting wildcard rules\n\nWildcard rules are attached to Object/Field only if no other rules are attached to it so to overwrite wildcard rule it's only necessary to attach any other rule to the Object/Field. Wildcard rules can be overwritten by rules attached using directive or extensions as well.\n\n```ts\n  // Reject rule is attached to all fields of the User object except of the id field. IsAuthenticated rule is attached to the id field.\n  const authSchema = {\n    User: {\n      '*': { __authz: { rules: ['Reject'] } },\n      id: { __authz: { rules: ['IsAuthenticated'] } }\n    }\n  };\n```\n\n```ts\n  // Reject rule is attached to all Objects except of the User Object. IsAuthenticated rule is attached to the User Object.\n  const authSchema = {\n    '*': { __authz: { rules: ['Reject'] } },\n    User: { __authz: { rules: ['IsAuthenticated'] } }\n  };\n```\n\n### Wildcard rules priority\n\nWildcard rules are not composing with each other. Only one wildcard rule is attached to the certain Object/Field. Wildcard rules are attached to a Field in following priority:\n1. Any Field in certain Object:\n```ts\n  const authSchema = {\n    User: {\n      '*': { __authz: { rules: ['Reject'] } }\n    }\n  };\n```\n2. Certain field in any object:\n```ts\n  const authSchema = {\n    '*': {\n      createdAt: { __authz: { rules: ['Reject'] } }\n    }\n  };\n```\n3. Any field in any object:\n```ts\n  const authSchema = {\n    '*': {\n      '*': { __authz: { rules: ['Reject'] } }\n    }\n  };\n```\n\nWith following example:\n\n```ts\n  const authSchema = {\n    '*': {\n      '*': { __authz: { rules: ['Rule01'] } },\n      id: { __authz: { rules: ['Rule02'] } }\n    },\n    User: {\n      '*': { __authz: { rules: ['Rule03'] } }\n    }\n  };\n```\nonly Rule03 is attached to the id field of the User object\n\n## Composing rules\n\n### Default composition\n\nRules that are passed to `@authz` directive as `rules` list are composing with AND logical operator. So `@authz(rules: [Rule01, Rule02])` means that authorization will be passed only if `Rule01` AND `Rule02` passed.\n\n### Create composition rules\n\nTo create different composition rules `and`, `or`, `not` functions should be used\n\n```ts\nimport { and, or, not } from \"@graphql-authz/core\";\n\nconst TestAndRule = and([Rule01, Rule02, Rule03]);\n\nconst TestOrRule = or([Rule01, Rule02, Rule03]);\n\nconst TestNotRule = not([Rule01, Rule02, Rule03]);\n```\n\nAlternatively new class extended from `AndRule`, `OrRule` or `NotRule` can be created\n\n```ts\nimport {\n  AndRule,\n  OrRule,\n  NotRule\n} from \"@graphql-authz/core\";\n\nclass TestAndRule extends AndRule {\n  public getRules() {\n    return [Rule01, Rule02, Rule03];\n  }\n}\n\nclass TestOrRule extends OrRule {\n  public getRules() {\n    return [Rule01, Rule02, Rule03];\n  }\n}\n\nclass TestNotRule extends NotRule {\n  public getRules() {\n    return [Rule01, Rule02, Rule03];\n  }\n}\n```\n\nWith such code\n\n`TestAndRule` will pass only if all of `Rule01`, `Rule02`, `Rule03` pass\n\n`TestOrRule` will pass if any of `Rule01`, `Rule02`, `Rule03` pass\n\n`TestNotRule` will pass only if every of `Rule01`, `Rule02`, `Rule03` fail\n\nComposition rules can be used just like regular rules\n\n```ts\n@authz(rules: [TestAndRule])\n@authz(rules: [TestOrRule])\n@authz(rules: [TestNotRule])\n```\n\nAlso, composite rules can be composed of other composite rules\n\n```ts\nclass TestOrRule02 extends OrRule {\n  public getRules() {\n    return [TestAndRule, TestOrRule, TestNotRule];\n  }\n}\n```\n\n### Inline composition rules\n\nRules can be composed in an inline way by using `compositeRules` and `deepCompositeRules` parameters. The difference between them is `compositeRules` supports only one level of inline composing. `deepCompositeRules` supports any levels of composing but it requires `id` key for the existing rule identificator.\n\nInline rules composition works with directives, extensions and authSchema\n\n```ts\n@authz(compositeRules: [{\n  or: [Rule01, Rule03],\n  not: [Rule02]\n}])\n\n@authz(deepCompositeRules: [{\n  or: [\n    {\n      and: [{ id: Rule01 }, { id: Rule02 }]\n    },\n    {\n      or: [{ id: Rule03 }, { id: Rule04 }]\n    }\n  ]\n}])\n```\n\n```ts\nconst authSchema = {\n  User: {\n    __authz: {\n      compositeRules: [{\n        or: ['Rule01', 'Rule03'],\n        not: ['Rule02']\n      }]\n    }\n  },\n  Post: {\n    body: {\n      __authz: {\n        deepCompositeRules: [{\n          or: [\n            {\n              and: [{ id: 'Rule01' }, { id: 'Rule02' }]\n            },\n            {\n              or: [{ id: 'Rule03' }, { id: 'Rule04' }]\n            }\n          ]\n        }]\n      }\n    }\n  }\n}\n```\n\nPre and Post execution rules can be mixed in any way inside all types of composite rules.\n\n## Custom errors\n\nTo provide custom error for rule the `error` option should be provided\n\n```ts\nimport { UnauthorizedError } from '@graphql-authz/core';\n\nconst SomeRule = postExecRule({\n  error: 'User is not authenticated'\n})(() =\u003e { /* rule body */ });\n```\n\nUsing rule class\n\n```ts\nimport { UnauthorizedError } from '@graphql-authz/core';\n\nclass SomeRule extends PreExecutionRule {\n  public error = new UnauthorizedError(\"User is not authenticated\");\n  public execute() {\n    throw this.error;\n  }\n}\n```\n\nIt's important to throw an instance of `UnauthorizedError` imported from `@graphql-authz/core` to enable composite rules to correctly handle errors thrown from rules. If an instance of `UnauthorizedError` is thrown it's treated as a certain rule didn't pass. If the rule is wrapped with `NotRule` then execution should continue. Any other errors are treated as runtime errors and are thrown up, so if any other error is thrown from the rule that is wrapped with `NotRule` it will fail the entire request.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAstrumU%2Fgraphql-authz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAstrumU%2Fgraphql-authz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAstrumU%2Fgraphql-authz/lists"}