nestjs / swagger

OpenAPI (Swagger) module for Nest framework (node.js) :earth_americas:
https://nestjs.com
MIT License
1.68k stars 465 forks source link

ApiSecurity does not expose ApiKey authorisation option #663

Closed djedlajn closed 4 years ago

djedlajn commented 4 years ago

I'm submitting a...


[ ] Regression 
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

By adding decorator on my controller route I should get option to use ApiKey authorization method. And it also fails to add header to request.

  1. Shows correctly at global option to add auth.

Screen Shot 2020-04-08 at 04 32 27

  1. Fails to show at secured route

Screen Shot 2020-04-08 at 04 32 58

Related issue: #484

Expected behavior

Option does not show at all.

Minimal reproduction of the problem with instructions

OpenAPI Configuration

  const options = new DocumentBuilder()
    .addApiKey({ type: 'apiKey', name: 'ApiKeyAuth', in: 'header' })
    .addBearerAuth()
    .setTitle('API Generator')
    .setDescription('API Gateway')
    .setVersion('1.0')
    .build();

Route example

  @Post('webhook/process')
  @ApiConsumes('multipart/form-data')
  @ApiSecurity('ApiKeyAuth')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        file: {
          type: 'string',
          format: 'binary'
        },
        uuid: {
          type: 'string'
        }
      }
    }
  })
  @UseInterceptors(FileInterceptor('file'))
  @UseGuards(LocalApiKeyGuard)
  webHookProcess(@UploadedFile() file, @Body('region') region: string, @Body('uuid') uuid: string) {
    return this.queueService.uploadImageAndProcess({ file, userId: uuid, region, name: file.originalname });
  }

What is the motivation / use case for changing the behavior?

Environment


Nest version: `7.0.7`
Nest-Swagger version: `4.5.1`


For Tooling issues:
- Node version: `v13.6.0`
- Platform:  `Mac, Linux`            
djedlajn commented 4 years ago

I have found solution tho I think it should be documented in better way. So I will probably make and PR and reference this issue.

Solution

  const options = new DocumentBuilder()
    .addApiKey({ type: 'apiKey', name: 'api_key', in: 'header', description: 'API Key For External calls' })
    .addBearerAuth()
    .setTitle('API Generator')
    .setDescription('API Gateway')
    .setVersion('1.0')
    .build()

Where I assume name needs to match optional array in @ApiSecurity decorator. Name is also the name of the entry that will be added in my case to header of the request.

And on controller itself decorator should look like: @ApiSecurity('api_key', ['api_key']) where second argument is array of security dependencies I assume. Documentation as noted above should be improved in this regard.

kamilmysliwiec commented 4 years ago

@djedlajn PRs are more than welcome :) Feel free to create one here https://github.com/nestjs/docs.nestjs.com And I'm glad you found the solution!

Benny739 commented 4 years ago

Maybe it helps someone, for me to get it to work, I had to add the name as well.

addApiKey({ type: 'apiKey', name: 'X-API-KEY', in: 'header' }, 'X-API-KEY')
enriqueaparicio-ph commented 3 years ago

X-API-KEY

Thanks! it worked for me :)

wnqueiroz commented 2 years ago

Sets the name to upper case worked for me too!

addApiKey({ type: 'apiKey', name: 'X-API-KEY', in: 'header' }, 'X-API-KEY') 
0p3r4t0r commented 1 year ago

@wnqueiroz @Benny739

Is it possible for you to provide a comprehensive example of what you did with addApiKey as well as if there are any decorators on your controllers? I've not been able to get this to work.

Warning: Probably a dirty hack For anyone else who comes across this here is a workaround with BearerAuth

Controller

  @ApiBearerAuth()
  @Controller
  ...your controller

Config

 const config = new DocumentBuilder()
   .setTitle('Title')
    .setDescription('Whatever')
    .setVersion('0.0.1')
    .addBearerAuth()
    .build();

Then you can check request headers for authorization and compare your key

Custom Guard

@Injectable()
export class ExampleGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const req = context.switchToHttp().getRequest();

    const key = req.headers['authorization'];

    // your validator function
    return key === 'Bearer <your actual key here>';
  }
}