@ponelat did we end up exposing a method for doing that?
Nope... ( I mean its possible, but ugly ).
I suggest the following ( ping @fehguy @shockey )
We expose a swaggerProxy option, that will act as requestInterceptor / responseInterceptor pipeline for decorating _requests for swagger specs_.
We expose a simple interface for adding _any_ auths ( ie: finish off the auth feature )
Swagger.ApiKeyAuthorization, Swagger.PasswordAuthorization, Swagger.HeaderAuthorization, Swagger.OAuthTokenAuthorizationHere are some ideas...
Swagger({
url: '...',
swaggerAuthorizations: [ // Note, an array of Auths only applied to fetching swagger specs
Swagger.ApiKeyAuthorization('myQuery', 'foo', 'query'),
Swagger.QueryAuthorization('myQuery', 'bar'), // Better than ApiKeyAuthorization?
Swagger.PasswordAuthorization('fooUser', 'fooPaswsword')
]
}).then(...)
Swagger({
url: '...',
swaggerProxy(req) {
req.headers.Authorization = 'bar'
// or
this.passwordAuth(req, 'fooUser', 'fooPassword') // Add a basic auth
return this.http(req) // Continue the request
// or
return // undefined, we'd infer that we should continued the request as above. In case the user forgets to
}
}).then(...)
Swagger({
url: '...',
swaggerProxies: [ // Note, an array of functions, applied to fetching swagger specs
Swagger.PasswordAuthorization('fooUser', 'fooPaswsword'), // decorate request with this auth
...,
(req) => { // We can write our own decorators...
if(this.matchUrl(req.url, 'example.com') { // Might be a useful helper
req.headers['X-ThisIsASpecialRequest'] = true // Whatever
return Swagger.HeaderAuthorization('FooHeader', 'bar')(req) // we can also use the helpers.
}
}
],
tryItOutProxies: [ ... ] // Same for try-it-out-requests?
}).then(...)
@ponelat I think @buunguyen had some thoughts on that. I guess the main question is, why change the behavior from 2.X?
The key difference is that we were applying auth to both requests in the try-it-out and fetching the swagger specs ( from what I recall ). I think they should be separate.
Something not mentioned here but important, is having a literal representation of auths... so that they can be including the config file ( function aren't easily added to the config file, if even possible ).
Right, but that I still fail to see why we need to break interface. We can possibly add, but the first step would be to support the original behavior.
That's fine too.
This is about the next steps. The future of Auth for all mankind.
Sorry for a tangential question, but can we also have support for Authorization token scheme for fetching the API? I know it's been addressed in the past per #1503, but I don't see if it's still supported in the new version. Per your comments from above it would seem it's not, is that right?
@dinvlad - that's the goal, yes.
@webron Any indications on the priority of this issue? we are very interested on embedding swagger-ui into our project in the near term, but this is a blocker for us as all our swagger specs are protected by authentication.
The P2 is the priority. Not top at the moment. If it's urgent to you, please consider submitting a PR.
I'm having kind of the same issue.
https://github.com/swagger-api/swagger-ui/issues/3157
I also had this issue with the api endpoint when executing them.
In this case I made a simple workaround with api_key
/**
* @swagger
* securityDefinitions:
* APIKeyHeader:
* type: apiKey
* in: header
* name: Authorization
*/
And when asked for the key, I copy the Bearer key generated outside swagger.
Is there a way to workaround with something like this for the swagger.json getting?
I made the most horrible workaround but it worked for me and thats good enough for me now.
Idea:
Override System Fetch function to add extra header when getting swagger.json
So I add an input that will call a function that gets System fetch from UI and overrides it with some extra code from me, this will add the Authorization code on the header, get swagger file and return everything to normal.
Code:
on index.html right after
<script>
var orgFetch;
window.setExtraHeader = function(bearerCode) {
var system = window.ui.getSystem();
if(!system) return;
if(!orgFetch) {
console.log('Getting original fetch function')
orgFetch = system.fn.fetch;
}
system.fn.fetch = function(obj) {
if(!obj) return;
if(!obj.headers) {
obj.headers = {};
}
obj.headers['Authorization'] = bearerCode
console.log('Fetch Object:', obj);
return orgFetch(obj)
.then(function(fetchRes) {
if(fetchRes && fetchRes.ok) {
setTimeout(function() {
document.getElementById('bearer-code-input').remove();
system.fn.fetch = orgFetch;
}, 100)
}
return fetchRes;
})
}
system.specActions.download()
}
</script>
<div>
<input id="bearer-code-input" type="text" placeholder="Copy / Paste your Authorization code here!" onchange="window.setExtraHeader(this.value)" style="height: 2em;width: 100%;font-size: 1em;"/>
</div>
Hi @martinpagesaal,
I am also trying to adapt your workaround, but it fails to reload the UI. Can you please tell me the specific location where you included the above code?
Is it after this script or somewhere else?
Hi @madhushib
This is my full index.html
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
<script>
var orgFetch;
window.setExtraHeader = function(bearerCode) {
var system = window.ui.getSystem();
if(!system) return;
if(!orgFetch) {
console.log('Getting original fetch function')
orgFetch = system.fn.fetch;
}
system.fn.fetch = function(obj) {
if(!obj) return;
if(!obj.headers) {
obj.headers = {};
}
obj.headers['Authorization'] = bearerCode
console.log('Fetch Object:', obj);
return orgFetch(obj)
.then(function(fetchRes) {
if(fetchRes && fetchRes.ok) {
setTimeout(function() {
document.getElementById('bearer-code-input').remove();
system.fn.fetch = orgFetch;
}, 100)
}
return fetchRes;
})
}
system.specActions.download()
}
</script>
<div>
<input id="bearer-code-input" type="text" placeholder="1. Goto Admin. 2. Query Admin user and get Code. 3. Paste it here and Enter 4. Explore" onchange="window.setExtraHeader(this.value)" style="height: 2em;width: 100%;font-size: 1em;"/>
</div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "/swagger.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
}
</script>
</body>
</html>
and this is part of my app.js
var swaggerDefinition = {
info: {
title: 'Node Swagger API',
version: '1.0.0',
description: 'Demonstrating how to describe a RESTful API with Swagger',
},
host: appConfig.swagger.host,
basePath: '',
};
// options for the swagger docs
var options = {
// import swaggerDefinitions
swaggerDefinition: swaggerDefinition,
// path to the API docs
apis: ['./model/*.js', './conf/routes.js'],
};
// initialize swagger-jsdoc
var swaggerSpec = swaggerJSDoc(options);
app.get('/swagger.json', jwt(), function(req, res) {
res.setHeader('Content-Type', 'application/json');
res.send(swaggerSpec);
});
app.get('/api-docs/index.html', function(req, res) {
res.setHeader('Content-Type', 'text/html');
res.sendFile(path.join(__dirname, './api-docs', 'index.html'));
});
app.get(express.static(path.join(__dirname, 'public')));
I hope this helps you!
@martinpagesaal, i had been try your solution, it works well.
@madhushib , there have another solution for this issue. According the sample for jersey(https://github.com/swagger-api/swagger-samples/tree/master/java/java-jersey-jaxrs/src/main/java/io/swagger/sample), just need 2 steps.
Nope... ( I mean its possible, but ugly ).
@ponelat Can we maybe see the ugly code until this issue is resolved?
So far the only solution I can think of is XMLHttpRequest interceptor...
@TheCodeDestroyer
We do have requestInterceptors, they are for all requests ( try-it-out, $ref downloading, initial swagger downloading, etc ). And one could add a custom check for the url there ( if url == your swagger spec, add auth ).
An alternative is to change the download action, if its _only_ the swagger spec you're concerned with.
Both can be done, and will try to get an example soon as I get a free moment.
Setting configs.preFetch works for me:
const ui = SwaggerUIBundle({
configs: {
preFetch: function(req) {
// Modify req, set auth
return req
}
}
})
@danilobuerger Nice find, but judging by this comment (assuming I'm looking at the correct implementation) it sounds like configs.preFetch is being deprecated?
// Wrap a http function ( there are otherways to do this, conisder this deprecated )
export function makeHttp(httpFn, preFetch, postFetch) {
postFetch = postFetch || (a => a)
preFetch = preFetch || (a => a)
Yeah, I read that too. My hope is that this story will be done before its removed.
I don't encourage the long-term use of a deprecated interface... but FWIW, we won't remove preFetch and postFetch before swagger-client@4.
@webron is there a rough time schedule for implementation?
We need access to protected API spec because our definitions are security-critical and thus not public.
Context: We would like to use openAPI for Confluence, but the definitions should be modular in a protected repostory.
@CarstenRilke, no one is working on this at the moment, as our resources are limited and our main focus is OAS3 support right now.
If you have the time, a pull request that fixes this would be appreciated. If you need this ASAP, please note that folks have mentioned workarounds to this issue upthread!
@CarstenRilke - also, it's not that it's not doable with the current code - make sure you read the thread fully.
It was pointed out to us that our request interceptors were _not_ intercepting spec fetch. This has been fixed, and will be available this Friday when we release Swagger-UI again.
Given user myUser and password myPassword, here's an example, using httpbin as an example of a page protected by Basic auth:
SwaggerUI({
url: "https://httpbin.org/basic-auth/myUser/myPassword",
requestInterceptor: (req) => {
if(req.loadSpec) {
var hash = btoa("myUser" + ":" + "myPassword") // base64
req.headers.Authorization = "Basic " + hash
}
return req
}
})
Basically, you define a requestInterceptor that attaches the basic auth information for you. Modify
myUser and myPassword to suit your needs.
If you have questions about this solution, please open a new ticket.
Thanks everyone!
Thanks @shockey .
Note that setting a requestInterceptor will also affect Try-it-out requests, so for anyone who _only_ wants the basic auth header applied to the fetch of the spec URL (after the next release on Friday with the fix), you could do this:
// The spec URL
const url = "https://www.example.com/authorised-users-only/spec.json";
SwaggerUI({
url, // spec url
requestInterceptor: (req) => {
// Only set Authorization header if the request matches the spec URL
if (req.url === url) {
req.headers.Authorization = "Basic " + btoa("myUser" + ":" + "myPassword");
}
return req;
}
})
Thanks for pointing that out, @scottohara! You're correct, I overlooked that in my original example.
There's an internal flag that's set on the request, loadSpec. I've updated my example above to use it, and I'm going to add some tests to ensure that flag stays in the DownloadUrl plugin.
Is there any chance this can be supported with the Docker image?
@brianupskill as the ticket is closed, the request is pretty much lost (hard to track). Please file a new ticket.
ui.getConfigs().requestInterceptor = (req) => {
req.headers.hahaha= "123";
return req;
}
It worked for me
@HaleyWang _please_ don't change things on the object you get back from ui.getConfigs(), it's not an official interface and is likely to break in the future 馃槃
I'm too doing the same things as @HaleyWang , Because I want to change the access token value dynamically.
@shockey , How can I change the Authorization header dynamically after initializing the swagger UI ?
I tried the ui.preauthorizeApiKey("MyJWTAuth", "Bearer <your token value here>") as per this comment https://github.com/swagger-api/swagger-ui/issues/2915#issuecomment-439166045 , But it didn't work.
I'm using Swagger/OpenAPI 2.0 , and swagger-ui version 3.22.2.
@tmkasum and everyone else: I use code like
const requestInterceptor = async req => {
req.headers['Authorization'] = 'Bearer ' + await getIdToken();
return req;
};
SwaggerUI({
url,
dom_id: '#swagger-ui',
requestInterceptor,
showMutatedRequest: true,
});
In swagger-ui 3.2x, to manually set Authorization header based on values entered in the authorize popup for basic authentication, we can use below code.
const ui = SwaggerUIBundle({
dom_id: '#swagger-ui',
requestInterceptor: (req) => {
if (!req.loadSpec) {
var authorized = this.ui.authSelectors.authorized();
//'Basic_Authentication' is security scheme key for basic authentication in the OpenApi file
var basicAuth = getEntry(authorized, 'Basic_Authentication');
if (basicAuth) {
var basicAuthValue = getEntry(basicAuth, 'value');
if (basicAuthValue) {
var username = getEntry(basicAuthValue, 'username');
var password = getEntry(basicAuthValue, 'password');
if (username && password) {
req.headers.Authorization = "Basic " + btoa(username + ":" + password);
}
}
}
}
return req;
}
//traverse through the object structure of swagger-ui authorized object to get value for an entryName
function getEntry(complexObj, entryName) {
if (complexObj && complexObj._root && complexObj._root.entries) {
var objEntries = complexObj._root.entries;
for (var t = 0; t < objEntries.length; t++) {
var entryArray = objEntries[t];
if (entryArray.length > 1) {
var name = entryArray[0];
if (name === entryName) {
return entryArray[1];
}
}
}
}
return null;
}
Most helpful comment
Thanks @shockey .
Note that setting a
requestInterceptorwill also affect Try-it-out requests, so for anyone who _only_ wants the basic auth header applied to the fetch of the spec URL (after the next release on Friday with the fix), you could do this: