Javalin: Add support for specifying security and securitySchema

Created on 14 Nov 2019  路  21Comments  路  Source: tipsy/javalin

Hi,
I'm trying to set an Authorization header for a rest call using the swagger plugin via annotations.

@OpenApi(
        method = HttpMethod.POST,
        description = "Chiamata necessaria per l\'inizio della redazione di un atto telematico",
        operationId = "startRedazione",
        summary = "Inizia redazione atto",
        tags = {"redazione"},
        headers = {
                @OpenApiParam(name = "Authorization", required = true),
                @OpenApiParam(name = "HeaderTest", required = true)
        },
        responses = {
                @OpenApiResponse(status = "200", description = "OK - Ritorna l\'id (uuid) della bozza nel response body.", content = @OpenApiContent(type = "application/json", from = ResponseIdRedazione.class)),
                @OpenApiResponse(status = "400", description = "BadRequest - Namespace, nomeatto o datitoken non impostati")
        }
)
@Override
public void handle(Context ctx) throws Exception {
....
}

As you can see I specified two headers:
@OpenApiParam(name = "Authorization", required = true),
@OpenApiParam(name = "HeaderTest", required = true)

but when I test the call in swagger adding the two values, the Authorization header is the only one missing from the call:

curl -X POST "http://localhost:12000/api/startredazione" -H "accept: application/json" -H "HeaderTest: b"

AuthorizationHeaderMissing

If I try to add more headers nothing change, the Authorization header is the only one omitted in the curl.

FEATURE REQUEST HELP WANTED OPENAPI

All 21 comments

This is because the swagger javascript filters some headers. If you look into the files and search for "PARAMETER_HEADER_BLACKLIST" you'll find them. The spring openapi/swagger ui plugin has the same behaviour and there you have a dedicated button for setting the security options (see image) which I can not find in Javalin.
That's at least what I have found (I'm very new to spring)
authorize
authorize2

Thanks @Aweorih

As described in the swagger doc (https://swagger.io/docs/specification/authentication/bearer-authentication/) to use bearer authentication you have to add a "security scheme" to openapi components like this:

1) Define the security scheme type (HTTP bearer)

components:
  securitySchemes:
    bearerAuth:            # arbitrary name for the security scheme
      type: http
      scheme: bearer
      bearerFormat: JWT 

and then use the security keyword to apply this scheme to the desired scope

globally:

2) Apply the security globally to all operations

security:
  - bearerAuth: []         # use the same name as above

or to just a few operations

    paths:
      /something:
        get:
          security:
            - bearerAuth: []

The problem is I don't think the current javalin openapi plugin allow this (see also the issue "Swagger openapi support for JWT? #804" opened by me)

Probably other options should be added to the OpenApiOptions class to allow to handle security schemes in the component and to apply the security globally.. and new annotations, or annotations parameters to apply the security only just to a few operations. The problem is I don't know Kotlin so I can't do it by myself.

For the moment I managed to create a separate rest resource which take the openapi json output generated by the OpenApiHandler resource, and I changed the json manually adding the 2 things I specified before and I can see and use the same "authorize" button shown by @Aweorih on my resource. Looking at the code I don't think is possible to change the output generated by the OpenApiHandler class of the openapi plugin directly ..

@mavek87 The problem is I don't know Kotlin so I can't do it by myself.

If you know Java I think you would pick it up easily, I would be happy to assist with code reviews.

I'm not sure, but I think we can just need to grab the security scheme from here: https://github.com/derveloper/kotlin-openapi3-dsl/blob/master/src/main/kotlin/cc/vileda/openapi/dsl/OpenApiDsl.kt

I have an incomplete PR for this here: https://github.com/tipsy/javalin/pull/852

I just started looking at the codebase, so I'm not sure how it ties together yet.

(Hints appreciated, @sealedtx and @TobiasWalle)

I already added support for this use case.
You can check the tests for an example:

Definitions:
https://github.com/tipsy/javalin/blob/master/src/test/java/io/javalin/openapi/TestOpenApi.kt#L72
https://github.com/tipsy/javalin/blob/master/src/test/java/io/javalin/openapi/TestOpenApi.kt#L83

Results:
https://github.com/tipsy/javalin/blob/master/src/test/java/io/javalin/openapi/json.kt#L228
https://github.com/tipsy/javalin/blob/master/src/test/java/io/javalin/openapi/json.kt#L576

_Edit:_
The support for security configuration per route is currently missing. For this we need to update the Javalin DSL and Annotations. @tipsy Your PR tackles this problem.

@TobiasWalle just made my PR to cover this feature, will appreciate if you review it

@sealedtx Thank you! But I also just opened a PR which is similar to your PR.

I want to avoid that we copy the OpenApiDsl library. That's why I kept my implementation as small as possible.

I think it is okay to define the SecurityScheme with the OpenApiDsl on intialiazation. You also can add the reference via the OpenApiDsl if you use the java/kotlin API.

In my PR I just added the annotation API.

I think this solution is easier to maintain, because you don't duplicate the Swagger data model.

But I'm open for discussions.

@TobiasWalle, I understand your way of thinking, but in my opinion those additional 40 lines of code bring little more clarity to people who use javalin.

From a user perspective, is the main difference between the PRs that @sealedtx's PR generates the securitySchemes part automatically based on what security is added to the different handlers, while for @TobiasWalle's PR the user has to write that part manually?

@tipsy Yeah that's the main difference.

Should the user really define the security scheme in the annotations? I think normally you have just one or a few ways to authenticate in your application. My concern is, if the user need to define the security scheme directly in the annotation is, that this leads to a lot of unnecessary duplication.

If the user then want to update it's security scheme, he needs to update all the places, where he uses this scheme.

That's why I preferred to separate the "reference" from the "SecurityScheme".

@sealedtx You are right, the API in my PR could be simplified. But I would prefer some simple helper methods, in the style of the OpenApiDsl. This way the user can decide to use the simpler (more limited) helpers or use the more complex OpenApi classes directly to avoid the limitations. For these helper we could use a lot of your code. Once we settled on reference vs. no reference, I can update my PR to include your changes.

@TobiasWalle As user I would prefer just to define authorization requirement on requests together with other openapi docs and let the other things to be done underhood. But you have more experience developing this plugin, and if you are saying that your solution is easier to maintain I believe you, so merge it. Thank you for review ;)

I think normally you have just one or a few ways to authenticate in your application

I think that sounds plausible. I often end up having some secured and some public endpoints, but never two different schemes.

I'm still very new to all of OpenAPI, so I don't have a strong opinion here, but I'm starting to lean towards @TobiasWalle's alternative. I like the idea of generating it automatically, but I guess in most cases it would be much simpler to just reference one of the predefined schemes than to specify the whole thing in the handler. 馃

The fact that it's a smaller code change is also a positive, since I'm already pretty lost in the code base of this plugin 馃槵

@TobiasWalle The support for security configuration per route is currently missing. For this we need to update the Javalin DSL and Annotations. @tipsy Your PR tackles this problem.

I don't want to sound like an idiot, but what part of that exactly does my PR tackle? 馃

I don't want to sound like an idiot, but what part of that exactly does my PR tackle? 馃

I just skimmed over your PR and though that you added support for the security configuration per route, but I was wrong 馃槈

@TobiasWalle As user I would prefer just to define authorization requirement on requests together with other openapi docs and let the other things to be done underhood. But you have more experience developing this plugin, and if you are saying that your solution is easier to maintain I believe you, so merge it. Thank you for review ;)

Thank you for your understanding 馃檪. I see my PR more like the absolute minimal implementation of this feature. But I think there would still be space for a more advanced implementation like yours. But I thin first we should see if this is really a requirement by a lot of users.

If you all agree, I think we should merge my PR now and think about extending it later if needed.

@mavek87 would my PR #856 fix this issue for you?

I just skimmed over your PR and though that you added support for the security configuration per route, but I was wrong 馃槈

Great, then I'm following - I realized I was doing it all wrong when I looked at your test 馃槄

I think that sounds plausible. I often end up having some secured and some public endpoints, but never two different schemes.

It's quite normal to both allow for a traditional OAuth2 security scheme as well as a token based APIKey on the same endpoints.

It's quite normal to both allow for a traditional OAuth2 security scheme as well as a token based APIKey on the same endpoints.

I should have been more clear. What I meant to say is that I think it's uncommon to have multiple different schemes that are exclusive to one endpoint:

Common:

ENDPOINT 1 - Scheme 1, Scheme 2
ENDPOINT 2 - Scheme 1, Scheme 2
ENDPOINT 3 - Public
ENDPOINT 4 - Scheme 1, Scheme 2

Uncommon:

ENDPOINT 1 - Scheme 1
ENDPOINT 2 - Scheme 2
ENDPOINT 3 - Scheme 3
ENDPOINT 4 - Public

The main point being that it makes more sense to define 1 to n schemes upfront and reference them from the endpoints, as opposed to defining the scheme itself in the endpoint.

@TobiasWalle @sealedtx Thank you very much for the feature, but how can I use it in Java? I can't find any example in the doc so I'm trying to figure out how to do it looking at the code. @tipsy Is the feature already released in javalin 3.7?

This has been released as part of 3.8.0 now: https://javalin.io/news/2020/03/27/javalin-3.8.0-released.html !

Is there any examples on how to use @OpenApiSecurity with JWT?

Was this page helpful?
0 / 5 - 0 ratings