{"id":25019061,"url":"https://github.com/femike/swagger-protect","last_synced_at":"2025-04-13T03:23:49.826Z","repository":{"id":48093838,"uuid":"392270816","full_name":"femike/swagger-protect","owner":"femike","description":"Module swagger-protect for protect openapi ui, NestJS, Fastify, Express","archived":false,"fork":false,"pushed_at":"2022-08-13T00:54:57.000Z","size":953,"stargazers_count":6,"open_issues_count":2,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-26T20:21:39.264Z","etag":null,"topics":["express","fastify","nest","nestjs","swagger-ui","typescript"],"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/femike.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":"2021-08-03T10:01:30.000Z","updated_at":"2024-10-14T09:39:20.000Z","dependencies_parsed_at":"2022-08-12T18:31:11.639Z","dependency_job_id":null,"html_url":"https://github.com/femike/swagger-protect","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/femike%2Fswagger-protect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/femike%2Fswagger-protect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/femike%2Fswagger-protect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/femike%2Fswagger-protect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/femike","download_url":"https://codeload.github.com/femike/swagger-protect/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248658428,"owners_count":21140945,"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":["express","fastify","nest","nestjs","swagger-ui","typescript"],"created_at":"2025-02-05T11:19:55.205Z","updated_at":"2025-04-13T03:23:49.798Z","avatar_url":"https://github.com/femike.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@femike/swagger-protect\" target=\"blank\"\u003e\u003cimg src=\"images/logo.svg\" width=\"120\" alt=\"Swagger Protect Logo\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://www.npmjs.com/org/femike\"\u003e\u003cimg src=\"https://img.shields.io/github/package-json/v/nestjs/nest/v9.0.1?label=%40nestjs%2Fcore\" alt=\"NestJS Version\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/org/femike\"\u003e\u003cimg src=\"https://img.shields.io/github/package-json/v/expressjs/express/4.18.1?label=express\" alt=\"Express Version\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/org/femike\"\u003e\u003cimg src=\"https://img.shields.io/github/package-json/v/fastify/fastify/v4.4.0?label=fastify\" alt=\"Fastify Version\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/org/femike\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@femike/swagger-protect.svg\" alt=\"NPM Version\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/org/femike\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/@nestjs/core.svg\" alt=\"Package License\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/org/femike\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/@femike/swagger-protect.svg\" alt=\"NPM Downloads\" /\u003e\u003c/a\u003e\n\u003c!-- \u003ca href=\"#\"\u003e\u003cimg src=\"https://img.shields.io/badge/donate-PayPal-ff3f59.svg\" alt=\"Donate PayPal\" /\u003e\u003c/a\u003e --\u003e\n\u003ca href=\"https://yoomoney.ru/to/41001486944398/250\"\u003e\u003cimg src=\"https://img.shields.io/badge/donate-%D0%AEMoney-blueviolet.svg\" alt=\"Donate ЮMoney\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# :bookmark_tabs:\n\n- [Fastify Hook](#swagger-protect-fastify-hook)\n- [Express Middleware](#swagger-protect-express-middleware)\n- [NestJS For Express](#swagger-protect-nestjs-module-for-express)\n- [NestJS For Fastify](#swagger-protect-nestjs-module-for-fastify)\n- [API Spec](#api-spec)\n- [Examples](#examples)\n- [UI Module](#ui-module)\n- [Known Issues](#known-issues)\n- [Roadmap](#roadmap)\n\n## Description\n\nA small tool to protect access to the openapi user interface. Creates a mechanism for checking the request URL: `/ api / *` and checks for the existence of a Cookie `swagger_token`, if a cookie is present, checks its validity through a callback, in case of failure, redirects to the authorization page `/login-api/index.html?backUrl=/path/to/openapi/ui`. After successfuly authorization, returns to the `backUrl`.\n\n### Versions compability\n\n- `^2.0.1` Supports: **Express** `v4.17`, **Fastify** `v3`, **NestJS** `v7`- `v8`\n- `^9.0.3` Supports: **Express** `v4.18`, **Fastify** `v4`, **NestJS** `v9`\n\n## Installation\n\n```bash\nnpm install @femike/swagger-protect@^2.0.1 # or @^9.0.3 for nestjs v9\n```\n\n```bash\nyarn add @femike/swagger-protect@^2.0.1\n```\n\n### Swagger protect Fastify hook\n\n[:small_red_triangle:up](#bookmark_tabs \"Up to menu\")\n\nEasy way to protect swagger with fastify use a hook.\n\n```typescript\n// ./src/main.ts\nimport { fastifyProtectSwagger } from '@femike/swagger-protect'\nimport { getConnection } from 'typeorm'\nimport { TokenEntity } from 'your-application/src/entities'\n\n// With clean fastify application\nfastify.addHook(\n  'onRequest',\n  fastifyProtectSwagger({\n    guard: (\n      token, // must return boolean result (token: string) =\u003e Promise\u003cboolean\u003e for example typeorm find token on fail return false\n    ) =\u003e\n      getConnection()\n        .getRepository(TokenEntity)\n        .findOneOrFail(token)\n        .then(t =\u003e t.token === token),\n    cookieKey: 'swagger_token', // key must be stored in cookies on login.\n    swaggerPath: 'api', // entry point will be protect with guard above.\n    loginPath: '/login-api', // redirect on fail guard.\n  }),\n)\n// For NestJS With Fastify as Adapter hook for module see below.\nfastifyAdapter.getInstance().addHook(\n  'onRequest',\n  fastifyProtectSwagger({\n    guard: token =\u003e\n      getConnection()\n        .getRepository(TokenEntity)\n        .findOneOrFail(token)\n        .then(t =\u003e t.token === token),\n    cookieKey: 'swagger_token',\n    swaggerPath: /^\\/api\\/(json|static\\/index.html)(?:\\/)?$/,\n    loginPath: '/login-api',\n  }),\n)\n```\n\nWhen guard return `true`, hook go to the next way and show swagger open api page.\n\nIf guard return `false`, user will be redirected to the page `/login-api`\n\n\u003e **Note** Your must create frontend application with sign-in form and set cookie\n\u003e with `swagger_token` key setted above on succesfuly login.\n\n\u003e Or use `@femike/swager-protect-ui` see [below](#ui-module).\n\n### Swagger protect Express middleware\n\n[:small_red_triangle:up](#bookmark_tabs \"Up to menu\")\n\n\u003e **Warning** Cookie-parser must be import before setup protect middleware.\n\n```typescript\n// ./src/main.ts\nimport { expressProtectSwagger } from '@femike/swagger-protect'\nimport express from 'express'\nimport { createSwagger } from './swagger'\nimport cookieParser from 'cookie-parser'\nconst app = express()\n\napp.get('/', (req, res) =\u003e res.send('Home Page \u003ca href=\"/api\"\u003eAPI\u003c/a\u003e'))\n\nasync function bootstrap() {\n  app.use(cookieParser()) // @!important need set cookie-parser before setup protect middleware\n  expressProtectSwagger({\n    guard: (token: string) =\u003e !!token, // if token exists access granted!\n  })\n  createSwagger(app).listen(3000, () =\u003e {\n    console.log(`Application is running on: http://localhost:3000`)\n  })\n}\nbootstrap()\n```\n\n### Swagger protect NestJS Module for Express\n\n[:small_red_triangle:up](#bookmark_tabs \"Up to menu\")\n\n\u003e **Warning** `Express` have no methods override exists routes, we must register middleware before setup `Swagger`.\n\n```typescript\n// touch ./src/swagger/config.ts\nimport { registerExpressProtectSwagger } from '@femike/swagger-protect'\nimport type { INestApplication } from '@nestjs/common'\nimport { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'\nimport { SwaggerGuard } from './guard'\n\nexport const SWAGGER_PATH = 'api'\n\nconst options = new DocumentBuilder()\n  .setTitle('Cats example')\n  .setDescription('The cats API description')\n  .setVersion('1.0')\n  .addTag('cats')\n  .addBearerAuth()\n  .build()\n\nexport function createSwagger(app: INestApplication): INestApplication {\n  registerExpressProtectSwagger(app, {\n    guard: new SwaggerGuard(),\n    swaggerPath: SWAGGER_PATH,\n    loginPath: '/login-api',\n    cookieKey: 'swagger_token',\n  })\n  const document = SwaggerModule.createDocument(app, options)\n  SwaggerModule.setup(SWAGGER_PATH, app, document)\n  return app\n}\n```\n\n\u003e **Note** Parrameters `guard`, `swaggerPath` `loginPath` and `cookieKey` have no effect in module `SwaggerProtect` when we using `Express`.\n\n```typescript\n// ./src/main.ts\nimport { SwaggerProtect } from '@femike/swagger-protect'\nimport { Module } from '@nestjs/common'\nimport { CatsModule } from './cats/cats.module'\nimport { SwaggerLogin } from './swagger'\n\n@Module({\n  imports: [\n    CatsModule,\n    SwaggerProtect.forRoot({\n      guard: () =\u003e false, // no effect on express\n      logIn: new SwaggerLogin(),\n      swaggerPath: 'api', // no effect on express\n      loginPath: '/login-api', // no effect on express\n      cookieKey: 'swagger_token', // no effect on express\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n```typescript\n// $ touch ./src/swagger/swagger.login.ts\nimport {\n  SwaggerProtectLogInDto,\n  SwaggerLoginInterface,\n} from '@femike/swagger-protect'\nimport { v4 as uuid } from 'uuid'\n\n/**\n * Swagger Login\n */\nexport class SwaggerLogin implements SwaggerLoginInterface {\n  async execute({\n    login,\n    password,\n  }: SwaggerProtectLogInDto): Promise\u003c{ token: string }\u003e {\n    return login === 'admin' \u0026\u0026 password === 'changeme'\n      ? { token: uuid() }\n      : { token: '' }\n  }\n}\n```\n\nExample `login` service must be implemented from `SwaggerLoginInterface`\n\n### Swagger protect NestJS Module for Fastify\n\n[:small_red_triangle:up](#bookmark_tabs \"Up to menu\")\n\nCreate class `guard` must be implemented from `SwaggerGuardInterface`\n\n```typescript\n// $ touch ./src/swagger/swagger.guard.ts\n\nimport type { SwaggerGuardInterface } from '@femike/swagger-protect'\nimport { Inject } from '@nestjs/common'\nimport { AuthService } from '../auth'\n\n/**\n * Swagger Guard\n */\nexport class SwaggerGuard implements SwaggerGuardInterface {\n  constructor(@Inject(AuthService) private readonly service: AuthService) {}\n\n  /**\n   * Method guard\n   */\n  async canActivate(token: string): Promise\u003cboolean\u003e {\n    return await this.service.method(token)\n  }\n}\n```\n\nNow register module `SwaggerProtect`\n\n\u003e **Note** `Fastify` middleware give little bit more than `Express`, `swaggerPath` meight be `RegExp` It can protect not only `swagger openapi UI`.\n\n\u003e **Warning** But remember if you override this option you must protect two entry points `/api/json` and `/api/static/index.html` in this `RegExp`\n\n```typescript\n// ./src/app.module.ts\n\nimport { LoggerModule } from '@femike/logger'\nimport { Module } from '@nestjs/common'\nimport { SwaggerProtect } from '@femike/swagger-protect'\n\n@Module({\n  imports: [\n    LoggerModule,\n    SwaggerProtect.forRootAsync\u003c'fastify'\u003e({\n      // \u003c- pass\n      imports: [AuthModule],\n      useFactory: () =\u003e ({\n        guard: SwaggerGuard,\n        logIn: SwaggerLogin,\n        swaggerPath: /^\\/api\\/(json|static\\/index.html)(?:\\/)?$/,\n        useUI: true, // switch swagger-protect-ui\n      }),\n    }),\n  ],\n  controllers: [AppController],\n  providers: [HttpStrategy, AppService, AppLogger],\n})\nexport class AppModule {}\n```\n\n\u003e **Warning** The controller `login-api` uses `ClassSerializer` you have to add `ValidationPipe` and container for fallback errors.\n\n```typescript\n// ./src/main.ts\n\n  ...\n\n    app.useGlobalPipes(\n    new ValidationPipe({\n      transform: true,\n      disableErrorMessages: false,\n      enableDebugMessages: true,\n    }),\n  )\n  useContainer(app.select(AppModule), { fallbackOnErrors: true })\n\n  ...\n\n```\n\n\u003e **Note** If `useUI` options is not disabled, module creates controller with answered path `/login-api` on `GET` request redirect to static `index.html` `UI` on `POST` request passed data to callback function or injected class implemented from `SwaggerLoginInterface` response return data to `UI` where on success set `Cookie`.\n\n```mermaid\ngraph TD;\n    REQUEST--\u003eGUARD_CALLBACK--\u003eCOOKIE_VALIDATE\n    COOKIE_VALIDATE--\u003eLOGIN_UI\n    COOKIE_VALIDATE--\u003eOPENAPI\n    LOGIN_UI--\u003ePOST_REQUEST_AUTH\n    POST_REQUEST_AUTH--\u003eLOGIN_UI\n    LOGIN_UI--\u003eSET_COOKIE\n    SET_COOKIE--\u003eCOOKIE_VALIDATE\n    SET_COOKIE--\u003eBACK_URL\n    BACK_URL--\u003eOPENAPI\n```\n\n## API Spec\n\n[:small_red_triangle:up](#bookmark_tabs \"Up to menu\")\n\nThe `forRoot()` method takes an options object with a few useful properties.\n\n| Property       | Type             | Description                                                                                                                                       |\n| -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `guard`        | Function / Class | Function or Class guard must be return boolean result. Class meight be implemented `SwaggerGuardInterface`. Default: `(token: string) =\u003e !!token` |\n| `logIn`        | Function / Class | Function or Class logIn must return object with key token. Class meight be implemented `SwaggerLoginInterface`. Default: `() =\u003e ({ token: '' })`  |\n| `swaggerPath?` | string / RegExp  | Default: RegExp `/^\\/api(?:\\/\\|-json\\|\\/json\\|\\/static.+\\|\\/swagger.+)?$/` for `fastify`                                                          |\n| `loginPath?`   | string           | Path where user will be redirect on fail guard. Default `/login-api`                                                                              |\n| `cookieKey?`   | string           | Key name stored in Cookie. Default `swagger_token`                                                                                                |\n| `useUI?`       | Boolean          | Use or not user interface for login to swagger ui. When loginPath was changed from `/login-api` ui will be disabled. Default `true`               |\n\n## Examples\n\n[:small_red_triangle:up](#bookmark_tabs \"Up to menu\")\n\n\u003e **Note** See full examples on `Github` [@femike/swagger-protect/tree/main/samples](https://github.com/femike/swagger-protect/tree/main/samples)\n\u003e\n\n## UI Module\n\n[:small_red_triangle:up](#bookmark_tabs \"Up to menu\")\n\n### Installation ui module\n\n```bash\nnpm i @femike/swagger-protect-ui\n```\n\n```bash\nyarn add @femike/swagger-protect-ui\n```\n\nDefault url: `/login-api`\n\n\u003e **Note** UI have no settings, it can be only disabled with options `useUI`: `false` in `forRoot()` or `forRootAsync()`\n\u003e Form send `POST` request to `/login-api` with data `{ login, password }` on response set Cookie with default key `swagger_token`\n\n\u003cp align=\"center\"\u003e\n\u003cimg width=\"540\" src=\"https://github.com/femike/swagger-protect-ui/raw/main/images/screen_1.png\"\u003e\u003c/img\u003e\n\u003c/p\u003e\n\n## Known Issues\n\n[:small_red_triangle:up](#bookmark_tabs \"Up to menu\")\n\n\u003e **Warning** If you want to use global prefix dont forget set exclude path `login-api`\n\n```ts\napp.setGlobalPrefix('api/v1', {\n  exclude: ['login-api'],\n})\n```\n\n## Roadmap\n\n[:small_red_triangle:up](#bookmark_tabs \"Up to menu\")\n\n- [x] Fastify Hook\n- [x] Express Middleware\n- [x] NestJS Module\n- [x] [UI - login](https://www.npmjs.com/package/@femike/swagger-protect-ui)\n- [x] [Example Page UI](https://femike.github.io/swagger-protect-ui/)\n- [x] Sample fastify\n- [x] Sample express\n- [x] Sample nestjs fastify\n- [x] Tests e2e nest-fastify\n- [x] Tests e2e nest-express\n- [x] Tests e2e express\n- [x] Tests e2e fastify\n- [x] Units test replaceApi\n- [ ] Units tests\n- [x] Github CI\n- [ ] Inject Swagger UI Layout\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffemike%2Fswagger-protect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffemike%2Fswagger-protect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffemike%2Fswagger-protect/lists"}