Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support empty security #892

Closed
arronzhang opened this issue Aug 7, 2020 · 6 comments
Closed

Support empty security #892

arronzhang opened this issue Aug 7, 2020 · 6 comments

Comments

@arronzhang
Copy link

I'm submitting a...


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

Current behavior

When I add global security. I can't exclude one api from the @ApiSecurity().

const docBuilder = new DocumentBuilder().addSecurityRequirements('bearer')

Expected behavior

We can define global security follow the document openapi authentication and exclude from the path by set security: [] # No security.

Now we can use SetMetadata('swagger/apiSecurity', []) to hack this feature when define ClassDecorator. But the this data has been omit when define the MethodDecorator.

@kamilmysliwiec
Copy link
Member

Would you like to create a PR for this issue?

@SathursanS
Copy link

SathursanS commented Oct 20, 2020

@zzdhidden Have you solved your issue. I'm trying to remove my global authentication from my health endpoint. Also where did you set your security:[] parameter.

@arronzhang
Copy link
Author

@SathursanS I define a empty string and then clean from the apiObject.

import { SetMetadata, CustomDecorator } from '@nestjs/common';

export const ApiEmptySecurity = (): CustomDecorator =>
  SetMetadata('swagger/apiSecurity', ['']);

export function ApiFixEmptySecurity(apiObject: any): void {
  Object.values(apiObject.paths).forEach((path: any) => {
    Object.values(path).forEach((method: any) => {
      if (Array.isArray(method.security)) {
        method.security = method.security.filter((s: any) => !!s);
      }
    });
  });
}

@kamilmysliwiec
Copy link
Member

Would you like to create a PR for this issue?

@TBG-FR
Copy link

TBG-FR commented Nov 2, 2021

Having followed the officiel NestJS docs to secure my API, all my routes are protected by an API-Key, and some of them can avoid it, with a Public() decorator (as explained in the docs)

In order to get Swagger to use that API Key, and not wanting to manually add @ApiSecurity() on all my routes excepted the few that are public, I used the following :

const PublicAuthMiddleware = () => SetMetadata(IS_PUBLIC_KEY, true);
const PublicAuthSwagger = () => SetMetadata('swagger/apiSecurity', ['']);
export const Public = () => applyDecorators(
  PublicAuthMiddleware,
  PublicAuthSwagger,
) 

in addition to

  const swaggerConfig = new DocumentBuilder()
  [...]
  .addSecurity('ApiKeyAuth', {type: 'apiKey', name: 'Authorization', in: 'header', description: "A valid issued API Key"})
  .addSecurityRequirements('ApiKeyAuth')

However, I don't understand where I need to put the ApiFixEmptySecurity ?
(@zzdhidden can you explain ? Your code piece was already really useful, thanks for that !)

@kamilmysliwiec I understand your answer in #1319 but I think one more decorator would be useful : sometimes you'll want to have @ApiSecurity() (or ApiBearerAuth() or anything) enabled only on some of your routes, and sometimes you'll want to enable Auth globally and disable it on some of your routes, that's only one decorator, for a "big" benefit, no ?

@rajp33
Copy link

rajp33 commented Mar 16, 2022

For anyone that still needs a workaround, I modified the above code to get it working:

const PublicAuthMiddleware = SetMetadata(IS_PUBLIC_KEY, true);
const PublicAuthSwagger = SetMetadata('swagger/apiSecurity', ['public']);

export const Public = () => applyDecorators(
  PublicAuthMiddleware,
  PublicAuthSwagger,
)

And then where my server is initialized:

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

Object.values((document as OpenAPIObject).paths).forEach((path: any) => {
    Object.values(path).forEach((method: any) => {
    if (Array.isArray(method.security) && method.security.includes('public')) {
        method.security = [];
    }
    });
});
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants