1

I am using openApi 3.0 specification, koa2-swagger-ui, and swagger-jsdoc. I am trying to get the Authorize button on the swagger ui to allow me to enter a JWT token so my requests will be authorized.

I followed the OpenApi 3.0 documentation for setting up the bearerAuth in the securitySchemes and also used security to make it global. All this was implemented in my swagger-config.yaml.

What I want is to be able to click authorize on the swagger ui and have the option to enter a JWT. Currently when I click authorize the box is empty. empty authorization request response 401 swagger ui

Swagger.json

    {
  "openapi": "3.0.0",
  "info": {
    "title": "LMS API Specification",
    "version": "1.0.0",
    "description": "Open documentation for LMS API"
  },
  "host": "localhost:8091",
  "basePath": "/",
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/instructor/me": {
      "get": {
        "tags": [
          "Instructor"
        ],
        "description": "Finds all classes and their status for the current user",
        "responses": {
          "200": {
            "description": "You have successfully found all classes and their status for the current user"
          }
        }
      }
    }
  },
  "tags": []
}

swagger-config.yaml

openapi: 3.0.0
info:
  title: LMS API Specification
  version: 1.0.0
  description: Open documentation for LMS API
host: localhost:8091
basePath: /
apis: ['api/v1/instructor/index.js']
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
security:
  - bearerAuth: []

app.js

import Koa from 'koa'
import cors from 'koa-cors'
import serveStatic from 'koa-static'
// import websockify from 'koa-websocket'

import Logger from './lib/Logger'
import authInit from './auth'

import index from './routes/index'
import auth from './routes/auth'
import launch from './routes/launch'
import lesson from './routes/lesson'
import v1 from './api/v1'
import Router from 'koa-router'

export default async port => {
  const koaSwagger = require('koa2-swagger-ui');
  // const app = websockify(new Koa
  const app = new Koa()
  const swaggerJSDoc = require('swagger-jsdoc');
  var router = new Router()
  await authInit(app)

  // Definitions for the swagger docs
  const swaggerDefinition = {
    info: {
      // API informations (required)
      title: 'LMS API Specification', // Title (required)
      version: '1.0.0', // Version (required)
      description: 'OpenAPI documentation for LMS', // Description (optional)
    },
    host: `localhost:8091/api/v1`, // Host (optional)
    basePath: '/', // Base path (optional)
  };
  const options = {
    // Import swaggerDefinitions
    swaggerDefinition,
    // Path to the API docs
    // Note that this path is relative to the current directory from which the Node.js is ran, not the application itself.
    apis: ['api/v1/instructor/index.js'],
  };
  // Initialize swagger-jsdoc -> returns validated swagger spec in json format
  const swaggerSpec = swaggerJSDoc(options);
  router.get('/swagger.json', async (ctx, next) => {

    ctx.set('Content-Type', 'application/json')
    ctx.body = (swaggerSpec);
    return
  });
  app.use(
    koaSwagger({
      swaggerOptions: {
        url: 'http://localhost:8091/swagger.json', // example path to json

      },
      hideTopbar: true,
      routePrefix: '/docs', // route where the view is returned
    }),
  );

  Logger.info(`Running in ${process.env.NODE_ENV} environment`)

  app
    .use(cors())
    .use(serveStatic(__dirname + '/assets'))
    .use(index.routes())
    .use(auth.routes())
    .use(launch.routes())
    .use(lesson.routes())
    .use(v1.routes())
    .use(router.routes())

  return app.listen(port, () => {
    Logger.info(`> Ready on port ${port}`)
  })
}
Luke Reagan
  • 61
  • 1
  • 6
  • Please post OpenAPI definition file (YAML/JSON) or your source code annotations for bearer auth. – Helen Jun 27 '19 at 18:48
  • I have it tagged under config file – Luke Reagan Jun 27 '19 at 19:04
  • Please don't post _images_ of code. Paste the code as text and use the `{}` button to format it as code. – Helen Jun 27 '19 at 19:25
  • Ok I will be back at my desk shortly and will update – Luke Reagan Jun 27 '19 at 19:29
  • @Helen Let me know if that is better. Thanks, Luke – Luke Reagan Jun 27 '19 at 19:43
  • What version of Swagger UI do you use? To find the version number, open the browser dev tools > Console tab and evaluate `versions`. – Helen Jun 27 '19 at 21:33
  • @Helen It says I am using version: "3.22.3" – Luke Reagan Jun 27 '19 at 22:44
  • Since your spec is `openapi: 3.0.0` you need to replace `host: localhost:8091` and `basePath: /` with `servers: [{url: 'http://localhost:8091/'}]`. Other than that your spec is fine. If you paste your spec into Swagger Editor at http://editor.swagger.io (which also uses UI v. 3.22.3), the Authorization dialog works there. Maybe this is an issue with how swagger-jsdoc serves Swagger UI. Are there any errors in the browser console? Any failed requests on the Network tab? – Helen Jun 28 '19 at 07:55
  • @Helen Okay I changed host/basePath to servers. I then regenerated my swagger.json file and the authorize button is still empty. When I pull up chromes dev tools while on my swagger ui I do not see any errors in the console or network tab. – Luke Reagan Jun 28 '19 at 16:06
  • 1
    @Helen I just got it to work by adding "openapi: 3.0.1" in my swagger definition. I am not sure why it worked but now it works. Thank you for helping me trouble shoot this. – Luke Reagan Jun 28 '19 at 16:19
  • Great! Please post your solution as an answer and [mark it as accepted](https://meta.stackexchange.com/a/5235/131247) so that it can help future readers. – Helen Jun 28 '19 at 16:22

2 Answers2

4

The way I got this to end up working is by updating my app.js file and my swagger-config.yaml file to look like this...

app.js

import Koa from 'koa'
import cors from 'koa-cors'
import serveStatic from 'koa-static'
// import websockify from 'koa-websocket'

import Logger from './lib/Logger'
import authInit from './auth'

import index from './routes/index'
import auth from './routes/auth'
import launch from './routes/launch'
import lesson from './routes/lesson'
import v1 from './api/v1'
import Router from 'koa-router'

export default async port => {
  const koaSwagger = require('koa2-swagger-ui');
  // const app = websockify(new Koa
  const app = new Koa()
  const swaggerJSDoc = require('swagger-jsdoc');
  var router = new Router()
  await authInit(app)

  // Definitions for the swagger docs
  const swaggerDefinition = {
    openapi: '3.0.1',
    info: {
      // API informations (required)
      title: 'LMS API Specification', // Title (required)
      version: '1.0.0', // Version (required)
      description: 'OpenAPI documentation for LMS', // Description (optional)
    },
    servers: [{url: 'http://localhost:8091/'}],
    components: {
      securitySchemes: {
        bearerAuth: {
          type: 'http',
          scheme: 'bearer',
          bearerFormat: 'JWT',
        }
      }
    },
    security: [{
      bearerAuth: []
    }]
  };
  const options = {
    // Import swaggerDefinitions
    swaggerDefinition,
    // Path to the API docs
    // Note that this path is relative to the current directory from which the Node.js is ran, not the application itself.
    apis: ['api/v1/instructor/index.js'],
  };
  // Initialize swagger-jsdoc -> returns validated swagger spec in json format
  const swaggerSpec = swaggerJSDoc(options);
  router.get('/swagger.json', async (ctx, next) => {

    ctx.set('Content-Type', 'application/json')
    ctx.body = (swaggerSpec);
    return
  });
  app.use(
    koaSwagger({
      swaggerOptions: {
        url: 'http://localhost:8091/swagger.json', // example path to json
      },
      hideTopbar: true,
      routePrefix: '/docs', // route where the view is returned
    }),
  );

  Logger.info(`Running in ${process.env.NODE_ENV} environment`)

  app
    .use(cors())
    .use(serveStatic(__dirname + '/assets'))
    .use(index.routes())
    .use(auth.routes())
    .use(launch.routes())
    .use(lesson.routes())
    .use(v1.routes())
    .use(router.routes())

  return app.listen(port, () => {
    Logger.info(`> Ready on port ${port}`)
  })
}

swagger-config.yaml

openapi: 3.0.1
info:
  title: LMS API Specification
  version: 1.0.0
  description: Open documentation for LMS API
servers:
- url: http://localhost:8091/
apis: ['api/v1/instructor/index.js']
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
security:
  - bearerAuth: []

Basically I added openapi: 3.0.1 to the swaggerDefinition.

Luke Reagan
  • 61
  • 1
  • 6
1

I'm using swagger-jsdoc with Koa

Here is the complete example of the working clean code with JWT authentication.

Folder and file structure

==> app.js
==> routes/users.route.js
==> middlewares/auth.middleware.js
==> swagger/swaggerOptions.json

app.js

const koa = require('koa');
const koaRouter = require('koa-router');
const cors = require('@koa/cors');
const koaBody = require('koa-bodyparser');
const koaSend = require('koa-send');
const serve = require('koa-static');
const swaggerJsdoc = require('swagger-jsdoc');
const koaSwagger = require('koa2-swagger-ui').koaSwagger;
const app = new koa();
const router = new koaRouter();

const authMiddleware = require('./middlewares/auth.middleware');
const users = require('./routes/users.route');

const swagger = swaggerJsdoc(require('./swagger/swaggerOptions.json'));

router.get('/swagger.json', ctx => ctx.body = swagger);
router.get('/api-docs', koaSwagger({ routePrefix: false, swaggerOptions: { spec: swagger } }));

router.use('/users', users.routes(), users.allowedMethods());

app 
  .use(serve('.'))
  .use(koaBody({ jsonLimit: '100mb', formLimit: '100mb', textLimit: '100mb' }))
  .use(cors())
  .use(router.routes())
  .use(router.allowedMethods())

app.listen(3000);

users.router.js

const koaRouter = require('koa-router');
const router = new koaRouter();

/**
 * @swagger
 *  tags:
 *   name: Users
 *   description: API to manage action types.
 */

/**
*  @swagger
*   components:
*     securitySchemes:
*       AccessToken:      # arbitrary name for the security scheme
*         type: apiKey
*         in: header       # can be "header", "query" or "cookie"
*         name: x-access-token  # name of the header, query parameter or cookie
*/

/**
*  @swagger
*   components:
*     schemas:
*       ArrayResponse:
*         type: object
*         required:
*           - success
*           - message
*         properties:
*           success:
*             type: boolean
*             description: successsful or failed
*           message:
*             type: string
*             description: name can be any string
*           data:
*             type: array
*         example:
*            success: true
*            message: "ok"
*            data: []
*/ 

/**
 * @swagger
 *  /:
 *    get:
 *      summary: it will return list of all users
 *      tags: [Users]
 *      security:
 *        - AccessToken: []
 *      responses:
 *        "200":
 *          description: record found successfully.
 *          content:
 *            application/json:
 *              schema:
 *                $ref: '#/components/schemas/ArrayResponse'
 *
 */

router.get('/', authMiddleware(), async (ctx, next) => {  
    ctx.status = 200;
    ctx.body = {
        success: true,
        message: "all users",
        data: [1, 2, 3, 4, 5]
    }
});

module.exports = router;

swaggerOptions.json

{
    "definition": {
        "openapi": "3.0.0",
        "info": {
            "title": "admin Swagger Doc",
            "version": "1.0.0",
            "description": "Complete api information",
            "contact": {
                "name": "admin",
                "url": "http://localhost:3000",
                "email": "admin@admin.com"
            }
        },
        "servers": [
            {
                "url": "http://localhost:3000",
                "description": "Local Server"
            },
            {
                "url": "http:/0.0.0.0:3001",
                "description": "Development Server"
            }
        ]
    },
    "schemes": [
        "https",
        "http"
    ],
    "apis": [
        "./routes/*.js"
    ]
}

auth.middleware.js

const jwt = require("jsonwebtoken");
function middleware() {
    return async (ctx, next) => {
        const accessToken = ctx.headers["x-access-token"];
        if (!accessToken) {

            log("token not found");
            ctx.throw("x-access-token header is required.");
        } 

        try {
            const decoded = jwt.verify(accessToken, "SECRET_KEY");
            ctx.state.authUser = decoded;
        } catch (error) {
            log("invalid x-access-token.", error.message);
            ctx.throw("x-access-token invalid.");
        }
        await next();
    }
}
module.exports = middleware;

enter image description here

M. Hamza Rajput
  • 7,810
  • 2
  • 41
  • 36