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

Add JWT authorization header in Swagger v3 #2915

Closed
Humberd opened this issue Apr 14, 2017 · 37 comments · Fixed by #4339
Closed

Add JWT authorization header in Swagger v3 #2915

Humberd opened this issue Apr 14, 2017 · 37 comments · Fixed by #4339

Comments

@Humberd
Copy link

Humberd commented Apr 14, 2017

I'm looking for a solution to add a JWT authorization header to each request like they did in this thread

Their solution for Swagger v2:

function addApiKeyAuthorization() {
	var key = encodeURIComponent($('#input_apiKey')[0].value);
	if (key && key.trim() != "") {
		var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("Authorization", "Bearer " + key, "header");
		window.swaggerUi.api.clientAuthorizations.add("bearer", apiKeyAuth);
		log("added key " + key);
	}
}
@fernandocamargoai
Copy link

+1

@fernandocamargoai
Copy link

In my case, I've defined an API KEY Auth with an Authorization header. I've also created a basic JWT with no expiration time that will only work on development (the dev secret was used to sign it). If I click on the Authorize button and type "Bearer ", it works. But I'd like to simply have it pre-configured instead of having to type it everytime. Isn't it a simple javascript snippet that I can use to configure it?

@fernandocamargoai
Copy link

After quite some time spent trying to figure it out, I found a workaround to do what I wanted. Basically, right after the basic configuration (index.html), I added the following code:

ui.authActions.authorize({JWT: {name: "JWT", schema: {type: "apiKey", in: "header", name: "Authorization", description: ""}, value: "Bearer <JWT>"}})

This is how my security definition is:

securityDefinitions:
  JWT:
    description: ""
    type: "apiKey"
    name: "Authorization"
    in: "header"

@TuureKaunisto
Copy link

TuureKaunisto commented Apr 27, 2017

@fernandocamargoti Is the code you pasted here in a public repo? It would be super helpful to take a look at the full working code since I couldn't get the latest version Swagger UI to send the Authorization header in the project I'm working on even with the code you pasted.

@fernandocamargoai
Copy link

@TuureKaunisto, It's not in public repo :(

But was your issue with that code? Are you using the most recent Swagger UI 3?

I basically cloned the master branch of this repository, copied what was inside the dist folder and added that line of code right after the normal configuration in the index.html. That line basically sets the Authorization (exactly what would happen if the user would click in Authorize and fill in the fields).

@danmusk
Copy link

danmusk commented May 3, 2017

Based on what worked in v2, I made a similar "hack" for v3:

In startup.cs

app.UseSwaggerUI(c =>
{
    c.InjectOnCompleteJavaScript("../swagger-bearer-auth.js");
});

I placed the swagger-bearer-auth.js in wwwroot, with the following content:

(function () {
    $(function () {
        $('#auth_container').append("<input type='text' id='input_apiKey' />"); 
        $("#input_apiKey").change(addApiKeyAuthorization);
    });

    function addApiKeyAuthorization() {
        var key = encodeURIComponent($('#input_apiKey')[0].value);
        if (key && key.trim() != "") {
            var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("Authorization", "Bearer " + key, "header");
            window.swaggerUi.api.clientAuthorizations.add("bearer", apiKeyAuth);
        }
    }
})();

@TuureKaunisto
Copy link

@fernandocamargoti thanks for your response. I did get it working now and the key gets set correctly. Thanks a lot for your help!

My problem was that the authorization header doesn't get sent or shown on the curl command although it does get set correctly. I guess there's a problem with my spec.

@owenconti
Copy link
Contributor

owenconti commented Jun 27, 2017

I found with the definition below, all I had to do was prefix my key with "Bearer " or "JWT ", etc.

Defintion:

  "securityDefinitions":{
    "JWT": {
      "description": "",
      "type": "apiKey",
      "name": "Authorization",
      "in": "header"
    },

Header in the request:

screen shot 2017-06-26 at 10 00 15 pm

It is entirely possible that I am missing something, so please let me know if I am off my rocker. Otherwise, @webron, what is the intended fix here?

@webron
Copy link
Contributor

webron commented Jun 27, 2017

@owenconti it's a bit complicated, we can sync up on that.

@nodriza-io
Copy link

@fernandocamargoti man you saved my life, I spent a whole day trying to find a way to do this. thanks for share.

@larmic
Copy link

larmic commented Sep 20, 2017

I'm trying to use @fernandocamargoti solution, but the header is still missing. I'm using swagger-ui-3.2.1.

I've downloaded and extract the dist folder and edited the index.html

        const ui = SwaggerUIBundle({
            urls: [
                {url: "http://localhost:8080/v2/api-docs?group=api", name: "api"},
                {url: "http://localhost:8080/v2/api-docs?group=admin-api", name: "admin api"}
            ],
            dom_id: '#swagger-ui',
            deepLinking: true,
            presets: [
                SwaggerUIBundle.presets.apis,
                SwaggerUIStandalonePreset
            ],
            plugins: [
                SwaggerUIBundle.plugins.DownloadUrl
            ],
            layout: "StandaloneLayout",
            securityDefinitions: {
                JWT: {
                    type: "apiKey",
                    description: "",
                    name: "Authorization",
                    in: "header"
                }
            }
        });

        ui.authActions.authorize({
            JWT: {
                name: "JWT",
                schema: {type: "apiKey", in: "header", name: "Authorization", description: ""},
                value: "MY_TOKEN"
            }
        });

        window.ui = ui

What is my problem?

@Henk8
Copy link

Henk8 commented Dec 1, 2017

@larmic did you find a solution? I encounter the same prolem

@larmic
Copy link

larmic commented Dec 3, 2017

@Henk8 I'm using requestInterceptor:

private renderSwaggerUi(url: string, authenticationToken: string) {
        console.log('[Swagger] render swagger ui for ' + url);
        new SwaggerUi({
            dom_id: '#swagger-ui',
            url: url,
            spec: '',
            presets: [presets.apis],
            requestInterceptor:
                function (request) {
                    console.log('[Swagger] intercept try-it-out request');
                    request.headers.Authorization = authenticationToken;
                    return request;
                }
        });

    }```

@sirianni
Copy link

sirianni commented Dec 4, 2017

FYI - I reverse engineered a way to pre-populate the initial value for the authorization on page load. This solves a use case for us where we are embedding swagger UI within an app that already has a JWT bearer token. Keep in mind this uses the internal redux store of SwaggerUI, so this is in lieu of an API provided by swaggerui itself.

        const authToken = 'foo';  // Your app gets this from some other context
        ui.getStore().dispatch({
            type: 'authorize',
            payload: {
                BearerAuth: {
                    name: 'BearerAuth',
                    schema: {
                        type: 'http',
                        scheme: 'bearer'
                    },
                    value: authToken
                }
            }
        })

@shockey shockey added this to the December 8, 2017 milestone Dec 4, 2017
@toaspzoo
Copy link

toaspzoo commented Dec 6, 2017

To have authorization header passed with every request you have to specify security scheme first and then you can use authorization button.

components:
  securitySchemes:
    token:
      type: http
      scheme: bearer
      in: header
      bearerFormat: JWT
security:
  - token: ''

@Henk8
Copy link

Henk8 commented Dec 13, 2017

@larmic Where can I put the requestInterceptor, or the function renderSwaggerUi?

@fereira
Copy link

fereira commented Mar 14, 2018

I've updated to swagger3 and using an openapi3 configuration file. I'm able to use a security scheme as defined here:
"securitySchemes": {
"basic": {
"type": "http",
"scheme": "basic"
},
"bearerAuth": {
"description": "JWT Authorization",
"type": "http",
"scheme": "bearer",
"in": "header",
"bearerFormat": "JWT"
}
}

That produces an Authorize dialog with a username/password for the basic authentication and a "value" for entering a JWT token for the bearerAuth security scheme

I can execute a post request (using curl) to get a jwt token then cut-in-paste that token into "value" form element when clicking on the "Authorize" button in the UI then when when trying out any of the API methods which have security set to "bearerAuth", everything works. However, instead of having to cut-in-paste the JWT token into the form element I'd like to have it render a username/password form just as it does when using "basic" authentication. It seems like several people have come up with solutions but are using things like Swing and Jersey. Has anyone created a configuration which allows one to enter user credentials to obtain the JWT token, then pass the token as an Authorization Bearer header?

@shockey
Copy link
Contributor

shockey commented Mar 17, 2018

preauthorizeApiKey and preauthorizeBasic instance methods will be available in the next version of Swagger-UI. They'll work whether you're using Swagger 2.0 or OpenAPI 3.0.

Thanks to everyone in this thread who provided input 😄

@rdehouss
Copy link

rdehouss commented Mar 26, 2018

Could you please give us an example on how to use preauthorizeBasic and preauthorizeApiKey?
We're in the case that our API requests basic auth + an api token 'X-API-KEY'.
We could do that in swagger-ui 2 with the following:

window.swaggerUi.api.clientAuthorizations.add("auth", new SwaggerClient.ApiKeyAuthorization('Authorization', auth, 'header'));
      window.swaggerUi.api.clientAuthorizations.add("apiKey", new SwaggerClient.ApiKeyAuthorization('X-API-KEY', appId, 'header'));

But can't find how to do that with the new swagger-ui 3 :'(

Even to get the swagger.json spec it requires the authentication (basic) of course, so, can't "play" with the securitySchemes

We're using swagger 2 spec file

Thanks a lot in advance!

@hkosova
Copy link
Contributor

hkosova commented Mar 26, 2018

@rdehouss, assuming your API definition contains security definitions like these:

securityDefinitions:
  basicAuth:
    type: basic
  apiKey:
    type: apiKey
    in: header
    name: X-API-KEY

security:
  - basicAuth: []
  - apiKey: []

you can use:

  const ui = SwaggerUIBundle({
    url: "https://my.api.com/swagger.yaml",
    ...
    onComplete: function() {
      // Here, "basicAuth" and "apiKey" are the security scheme names/keys in the "securityDefinitions" collection
      ui.preauthorizeBasic("basicAuth", "username", "password");
      ui.preauthorizeApiKey("apiKey", "acbde12345");
    }
  })

To verify that preauthorize* worked, click the "Authorize" button in Swagger UI - it will show that the client is already authorized with the specified username/password/API key.


Even to get the swagger.json spec it requires the authentication (basic) of course, so, can't "play" with the securitySchemes

If your API definition file itself (.json / .yaml) is protected by auth, you'll need to use the requestInterceptor to properly load it, as shown here:
#2793 (comment)

@fereira
Copy link

fereira commented Mar 26, 2018

ui.preauthorizeBasic("basicAuth", "username", "password");

I don't think that addresses the use case that I was asking about. It looks to me like this would
"hard code" a username and password for the form that is rendered by the UI when the "Authorize" button is clicked. What I am looking for is for having the UI render a username/password form when a "bearerAuth" security definition is used and the scheme is "bearer".

@shockey
Copy link
Contributor

shockey commented Mar 28, 2018

Hi @fereira,

The original ticket here was for programmatically setting authorization values - it sounds like you're describing a use case where you protect one authorization strategy behind another (JWT behind basic auth), and want to compress the user experience for that into one basic auth form that handles everything.

preauthorize can be used to build something that achieves that, but by itself is much lower level. As for the definition itself, there's no construct in Swagger/OpenAPI that can be used to describe a dependency between authorization strategies, so this is not something that can live in the core project.

In any event, there's 18 people in this thread now - if you open a new ticket I'd be happy to outline how you could achieve that behavior through a custom plugin.

@fereira
Copy link

fereira commented Mar 28, 2018

Thanks for the response Kyle. I probably won't get a chance to create a new issue until next week but as a point of clarification, the use case I'm describing is protecting an authorization behind another authorization mechanism. It's more providing an authentication mechanism which generates a authorization token. In my view, entering a username/password (or hardcoding credentials using a preauthorize method) is "authentication" and passing the token is "authorization".

@rdehouss
Copy link

Thx, #2793 (comment) worked fine!
For others looking for the issue, please note that this doesn't work when using with CORS.
Indeed, the OPTIONS request does not send the Authorization header and so, gets blocked right there.
Solution? Allow OPTIONS without credentials?

@shockey
Copy link
Contributor

shockey commented Mar 28, 2018

@rdehouss, yes, generally you should exempt OPTIONS requests from authorization in your API server. The Fetch Standard (which defines CORS flow) indicates that the browser should always omit credentials during preflight requests:

messages image 1980859222

Source; discussion #1 #2

@rdehouss
Copy link

Great, thx for the info!

@jimmytuc
Copy link

jimmytuc commented Apr 2, 2018

Much thanks, saved my time

@bunyk
Copy link

bunyk commented Nov 15, 2018

So, while googling on how to give my JWT token to swagger UI - this issue if what I find most often, but here I see so much things to try, so I'm not sure where to start. Is there any documentation page that describes where to set JWT token?

@shockey
Copy link
Contributor

shockey commented Nov 15, 2018

sure @bunyk, here are the docs for the methods you can use to set an ApiKey authorization value: https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md#instance-methods

@hkosova's comment above (#2915 (comment)) is the right way to do it.

In Swagger/OpenAPI 2.0, you'll need to manually add Bearer as well... here's what I'm talking about...

If I have a JWT security definition called MyJWTAuth:

// Swagger/OpenAPI 2.0 does not add `Bearer` for you
ui.preauthorizeApiKey("MyJWTAuth", "Bearer <your token value here>");

// OpenAPI 3.0 does
ui.preauthorizeApiKey("MyJWTAuth", "<your token value here>");

If you haven't yet set up a security definition for the JWT in your document, here are the docs you should look at first: https://swagger.io/docs/specification/authentication/bearer-authentication/

@bunyk
Copy link

bunyk commented Nov 16, 2018

Yes! With

components:
  securitySchemes:
    keycloak:
      type: http
      scheme: bearer
      bearerFormat: JWT
security:
  - keycloak: []

in my openapi spec, and by passing

    onComplete: () => {
      ui.preauthorizeApiKey("keycloak", token);
    }

to SwaggerUIBundle it works! Thank you very much.

@elyobo
Copy link

elyobo commented May 31, 2019

Being able to prepopulate the auth is great and works fine, but there's no way (that I can see) to get the auth that the user has manually put in (or to know that they have done so).

I would like to let the user put in their api key, stash that locally (cookie/localstorage/whatever) for their future convenience (using the preauth to populate it on their return), and also trigger a reload of the spec (because it requires that same api key to access the full version) with the API key provided.

Some of this is possible using ui.preauthorizeApiKey and requestInterceptor, but detecting the user interactions (that they have set an API key, and what that key was) is not obviously possible (mutation observer, plus peeking in to the store's state or something? Seem awful).

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

Successfully merging a pull request may close this issue.