{"id":15133371,"url":"https://github.com/spoonx/stix-gates","last_synced_at":"2025-10-23T09:31:17.414Z","repository":{"id":48001133,"uuid":"149311042","full_name":"SpoonX/stix-gates","owner":"SpoonX","description":"💂A stix module that allows you to setup enrichers, validators and more for actions.","archived":false,"fork":false,"pushed_at":"2021-08-11T03:54:38.000Z","size":85,"stargazers_count":2,"open_issues_count":1,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-30T17:38:25.279Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SpoonX.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}},"created_at":"2018-09-18T15:28:50.000Z","updated_at":"2020-05-19T03:25:24.000Z","dependencies_parsed_at":"2022-08-12T16:20:22.680Z","dependency_job_id":null,"html_url":"https://github.com/SpoonX/stix-gates","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpoonX%2Fstix-gates","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpoonX%2Fstix-gates/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpoonX%2Fstix-gates/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SpoonX%2Fstix-gates/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SpoonX","download_url":"https://codeload.github.com/SpoonX/stix-gates/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237807457,"owners_count":19369595,"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-09-26T05:01:00.132Z","updated_at":"2025-10-23T09:31:16.962Z","avatar_url":"https://github.com/SpoonX.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![Stix](./stix-gates.svg)\n\n[![Slack Status](https://spoonx-slack.herokuapp.com/badge.svg)](https://spoonx-slack.herokuapp.com)\n\nA [stix](https://github.com/SpoonX/stix) module that allows you to setup enrichers, validators and infinitely more for actions.\n\n## What are gates?\n\nGates are like middleware, but different. They allow you to grant or reject passage. They sit between your router and your action, securing your endpoints. Here's a simplified overview of the flow:\n\n```\nRequest---\u003eRouter+---\u003eGates+------\u003eDispatcher+\n                 |         |                 |\n                 v         v                 v\n                404       4xx               2xx\n                 |         |                 |\n                 +---------+-----------------+--\u003eResponse\n```\n\n- The request will pass through the gates **in the order you defined them**.\n- A gate is allowed to return `undefined`, `boolean` or a `stix.Response` instance.\n- If a gate denies access to the request, the request goes straight to the response phase.\n\n## Setup\n\nIf you initialized a new stix project using the [boards cli stix preset](https://github.com/SpoonX/boards-preset-stix), stix-gates will already be included in your project and you can move on to the [using section](#using). If not, keep reading.\n\n1. In your stix project, simply run `yarn add stix-gates`.\n2. Add the module to your project's `src/config/modules.ts`:\n\n```ts\nimport { ModuleManagerConfigInterface } from 'stix';\nimport { Gates } from 'stix-gates';\n\nexport const modules: ModuleManagerConfigInterface = [\n  Gates,\n  // Your other modules.\n];\n```\n\n### Setting up gates\n\nSetting up stix-gates is easy.\n\n1. Make sure you have a `gates.ts` at `src/config/gates.ts`.\n2. Add it to your config (_add `export * from './gates';` to your `src/config/index.ts`_)\n\nDone. The stix-gates module will do the rest... Except for writing your gates of course, for which I do encourage you to keep reading!\n\n## Using\n\nUsing gates is, in the spirit of stix as magic-less as possible. This means that you maintain a clear overview of what's going on, and you can **cmd+click/ctrl+click** _(I don't judge... Publicly.)_ your way through your codebase as happy as the day you were born.\n\n### Gates configuration\n\nLet's take a look at the `src/config/gates.ts` file.\n\n```ts\nimport { Gate } from 'stix-gates';\nimport { SomeController } from '../api/controllers';\nimport { IsAuthenticated, IsNotGuest } from '../api/gates';\n\nconst compose = Gate.compose;\n\nexport const gate = {\n  locations: [ path.resolve(__dirname, '..', 'src', 'Gate') ], // Tell stix-gates where it can find your Gate classes\n  gates: {}, // Optional service-manager config\n  rules: new Map([\n    // By default, allow no access to anything.\n    // This is also the stix-gates default when no gates were found.\n    ['*', false],\n\n    // Allow access to all actions, except one.\n    // Produces: { OpenController: { '*': true, secretAction: false } }\n    [ OpenController, { '*': true, secretAction: false } ],\n\n    // Compose some rules for the UserController using the helper.\n    // Produces: { UserController: { profile: [ isAuthenticated, isNotGuest ] } }\n    compose(UserController, { profile: [ isNotGuest ] }, [ isAuthenticated ]),\n  ]),\n};\n```\n\nAs you can see, you can use the reference to your controller, and similarly to your gates. You can also just use arrays.\n\n### Gate implementation\n\nNow that you know how to configure gates, you'll need to start writing them.\n\nYou can put your gates anywhere you like. They can even be imported from other modules, how convenient is that? Here are a couple of examples to help you understand how gates work.\n\n**Simple result**\n\n```ts\nimport { ContextInterface } from 'stix';\n\nexport class IsAuthenticated extends AbstractGate {\n  public async passThrough (ctx: ContextInterface) {\n    return !!ctx.state.user;\n  }\t\n}\n```\n\n- Gates can be async functions.\n- `ctx` is the [koa context](https://koajs.com/#context). It holds the request, response, body etc.\n- When a gate returns `false`, stix-gates will create a `ClientErrorResponse.forbidden()` response for you.\n- When it returns `true`, the next gate will be applied. If this was the last gate, the action will be dispatched.\n\n**Default value**\n\n```ts\nimport { ContextInterface } from 'stix';\n\nexport class IsAuthenticated extends AbstractGate {\n  public passThrough (ctx: ContextInterface) {\n    if (!ctx.state.user) {\n      return false;\n    }\n  }\n}\n```\n\n- Gates are not required to return a value.\n- When nothing was returned (or `undefined`) the gate passes automatically (defaulting to `true`).\n- This allows you to use gates to patch the `ctx` with additional data. One example is using a gate to fetch a user from the database based on the user_id provided in the JWT.\n\n**Custom response**\n\n```ts\nimport { ContextInterface } from 'stix';\nimport { AbstractGate } from 'stix-gates';\n\nexport class IsAuthenticated extends AbstractGate {\n  public passThrough (ctx: ContextInterface) {\n    if (!ctx.state.user) {\n      return this.unauthorizedResponse();\n    }\n  }\n}\n```\n\n- Returning `false` from a gate defaults to a `ClientErrorResponse.forbidden()` denying the request.\n- Returning **any other `Response` type will _also_ result in the request being terminated** with your custom response.\n- This allows you to decide what type of response gets sent back early if needed.\n- Common responses are available on your gate when extending AbstractGate.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cem\u003e\u003cstrong\u003eMore available response methods\u003c/strong\u003e\u003c/em\u003e\u003c/summary\u003e\n\u003cp\u003e\n\nThe AbstractGate extends the AbstractResponseHelper, giving you the following helper methods:\n\n- okResponse\n- createdResponse\n- notFoundResponse\n- requestTimeoutResponse\n- forbiddenResponse\n- badRequestResponse\n- unauthorizedResponse\n- internalServerErrorResponse\n- permanentRedirectResponse\n\nYou can read more about responses in the Stix documentation.\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n## Helpers\n\nstix-gates comes with a couple of helpers to make working with it even more fun.\n\n### Gate.compose(controller, rules, baseRules?)\n\nBecause gates are indexed using Maps, and not everyone is comfortable with an array of arrays, we added the `Gate.compose()` helper method. The added advantage is that it allows you to define some baseRules, and it'll merge those in for every action defined.\n\nCode speaks. This example uses the `baseRules`:\n\n```ts\ncompose(UserController, { profile: [ isNotGuest ] }, [ isAuthenticated ]);\n\n// Produces:\n[\n  UserController,\n  {\n    profile: [ isAuthenticated, isNotGuest ],\n  },\n]\n```\n\nAnd here's a simple example using booleans:\n\n```ts\n// Allow access to all actions, except one.\ncompose(OpenController, { '*': true, secretAction: false });\n\n// Produces:\n[\n  OpenController,\n  {\n    '*': true,\n    secretAction: false,\n  }\n]\n```\n\nThis is especially useful if your rules repeat often.\n\n## License\n\nMIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspoonx%2Fstix-gates","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspoonx%2Fstix-gates","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspoonx%2Fstix-gates/lists"}