Is it possible to add Authentication to access to NestJS' Swagger Explorer

25,002

Solution 1

Securing access to your Swagger with HTTP Basic Auth using NestJS with Express

First run npm i express-basic-auth then add the following to your main.{ts,js}:

// add import
import * as basicAuth from 'express-basic-auth';

// ...

// Sometime after NestFactory add this to add HTTP Basic Auth
app.use(
    ['/docs', '/docs-json'],
    basicAuth({
        challenge: true,
        users: {
            yourUserName: 'p4ssw0rd',
        },
    }),
);


// Your code
const options = new DocumentBuilder()
    .setTitle('My App')
    .setSchemes('https')
    .setDescription('My App API documentation')
    .setVersion('1.0')
    .build()

const document = SwaggerModule.createDocument(app, options)
SwaggerModule.setup('docs', app, document, {
    customSiteTitle: 'My App documentation',
})

// ...

With this in place you will be prompted on any of the /docs route with a HTTP Basic Auth prompt. We have to name /docs-json explicitly too, to protect the generated JSON OpenAPI file.

You should not put the credentials in your code/repository but rather in your .env and access via the ConfigService.

I have seen this solution first here.

Solution 2

Just add .addBearerAuth() (without any parameters) to your swagger options

and @ApiBearerAuth() to your Controller methods

const options = new DocumentBuilder()
    .setTitle('My App')
    .setSchemes('https')
    .setDescription('My App API documentation')
    .setVersion('1.0')
    .addBearerAuth()
    .build()

Solution 3

UPDATE

As per recent changes in DocumentBuilder methods, this how it worked for me. Sharing for the people who are using new versions.

const options = new DocumentBuilder()
.setTitle('My API')
.setDescription('API used for testing purpose')
.setVersion('1.0.0')
.setBasePath('api')
.addBearerAuth(
  { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
  'access-token',
)
.build();

const document = SwaggerModule.createDocument(app, options);

Update Also, please use @ApiBearerAuth() on your controller function to add auth.

@Get('/test')
@ApiBearerAuth()

access-token is the name for reference in swagger doc. Your token in the header will be passed as below:

curl -X GET "http://localhost:3004/test" -H "accept: application/json" -H "Authorization: Bearer test-token"

Solution 4

Updated following breaking/API changes in @nestjs/swagger version 4.0.

Hi, Took a lot of try&fail to get this right. The comments in the code is what is important to understand. The names rely on each other for this to work.

main.ts

    const options = new DocumentBuilder()
        .setTitle('my-title')
        .setDescription('my-descirption')
        .setVersion('1.0')
        .addBearerAuth(
          {
            type: 'http',
            scheme: 'bearer',
            bearerFormat: 'JWT',
            name: 'JWT',
            description: 'Enter JWT token',
            in: 'header',
          },
          'JWT-auth', // This name here is important for matching up with @ApiBearerAuth() in your controller!
        )
        .build();
      const document = SwaggerModule.createDocument(app, options);
      SwaggerModule.setup('api', app, document);

And in your controller you do the following (note @ApiBearerAuth() using the same name as the name on the swagger options in main.ts):

app.controller.ts

    @Roles(Role.Admin)
      @UseGuards(JwtAuthGuard, RolesGuard)
      @ApiTags('Admin')
      @ApiOperation({ summary: 'Get admin section' })
      @Get('admin')
      @ApiBearerAuth('JWT-auth') // This is the one that needs to match the name in main.ts
      getAdminArea(@Request() req) {
        return req.user;
      }

Hope this saves somebody the time it took me to understand what was going on.

Solution 5

For anyone with similar challenge, you can add Authentication to your Swagger UI in Nestjs as shown below.

const options = new DocumentBuilder()
.setTitle('Sample Project API')
.setDescription('This is a sample project to demonstrate auth in Swagger UI')
.setVersion('1.0')
.addTag('Nestjs Swagger UI')
.setContactEmail('[email protected]')
.addBearerAuth('Authorization', 'header', 'basic')
.setBasePath('api')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('docs', app, document);

So .addBearerAuth takes 3 arguments (key-name, location, authentication-type). authorization-type can be basic, bearer or apikey

Share:
25,002
josec89
Author by

josec89

Updated on July 09, 2022

Comments

  • josec89
    josec89 almost 2 years

    I'm currently using Swagger in my NestJS project, and I have the explorer enabled:

    in main.js

    const options = new DocumentBuilder()
        .setTitle('My App')
        .setSchemes('https')
        .setDescription('My App API documentation')
        .setVersion('1.0')
        .build()
    
    const document = SwaggerModule.createDocument(app, options)
    SwaggerModule.setup('docs', app, document, {
        customSiteTitle: 'My App documentation',
    })
    

    With this, the explorer is accessible in /docs which is what I expected. But I was wondering if it's possible to add any Authentication layer to the explorer, so only certain requests are accepted.

    I want to make this explorer accessible in production, but only for authenticated users.

    Thanks in advance :)

  • chrismarx
    chrismarx over 4 years
    I got an error when specifying "bearer" as the authentication-type to the .addBearerAuth method. Turns out if you just don't include the third parameter, it enables bearer authentication. Using the 'basic' value did turn on username/password http auth-
  • Lu Blue
    Lu Blue about 4 years
    they made a huge change on the DocumentBuilder methods and their params, I hope someone makes an example of this changes.
  • Jacobdo
    Jacobdo almost 4 years
    Somehow this does not work for me, the header does not get applied to the request - the curl output stays - curl -X GET "localhost:3000/unit-type" -H "accept: /"
  • pravindot17
    pravindot17 almost 4 years
    @Jacobdo can you see lock icon on your endpoint in swagger doc? You can click on it and pass the access token, if not then you need to add @ApiBearerAuth() in controller function, see updated answer
  • Maciej Sikorski
    Maciej Sikorski almost 4 years
    This tells about the security of your endpoints, not the swagger itself.
  • Ali Turki
    Ali Turki over 3 years
    just .addBearerAuth({ in: 'header', type: 'http' })
  • KiwiKilian
    KiwiKilian almost 3 years
    The question is about securing access to the swagger page itself, not showing the auth options on routes swagger displays. See my answer for actually securing your /docs endpoint with HTTP Basic Auth.
  • Pini Cheyni
    Pini Cheyni almost 3 years
    I followed this demo with the swagger4 but I'm having an issue with the scopes object, To register the API I used the scopeURL, and when I set only the name like you suggested profile, I get an error which says that I can't request this scope
  • CyberEternal
    CyberEternal almost 3 years
    Actually, I have not used the scopeURL. I have set the scope as an object like in an example. and there can be added many properties like {profile: 'profile', email:'email', ...}. The value of scopes can be also an array, like ['profile', 'email', ...]. But I'm not sure that you can use scopeURL as a value of the scope parameter since it can't be a string. You can check the module codes and see that.
  • riqitang
    riqitang over 2 years
    it'd be even better if it didn't require the @ApiBearerAuth decorator on controllers/routes.
  • CTS_AE
    CTS_AE about 2 years
    I assumed the top level bearer would apply it to everything, but I was wrong -- I guess you really do need it on every controller. Edit: is there any way to persist the authentication between refreshes?