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

On reloading Swagger UI, it is loosing the authentication #120

Closed
bigbharatjain opened this issue Feb 21, 2018 · 9 comments
Closed

On reloading Swagger UI, it is loosing the authentication #120

bigbharatjain opened this issue Feb 21, 2018 · 9 comments
Labels

Comments

@bigbharatjain
Copy link

bigbharatjain commented Feb 21, 2018

On reloading Swagger UI, it is loosing the authentication.
@DarkaOnLine Is there a way to keep the user login even if I reload the Swagger UI?


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

@joseph-montanez
Copy link
Contributor

As far as Swagger UI allows via configuration is only setting the client id and client secret as well as some others, but not a username, password, or access token.

https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md

Adding this to the javascript of resources/vendor/l5-swagger/index.blade.php will pass in the ID and secret. So it saves some typing.

  ui.initOAuth({
      clientId: "your-client-id",
      clientSecret: "your-client-secret-if-required",
  });

The auth props component itself allows a username to be set, but not the password:

https://github.com/swagger-api/swagger-ui/blob/master/src/core/components/auth/oauth2.jsx

However it just calls authorizeOauth2 which you can access via: ui.authActions.authorizeOauth2()... After some hunting around there is:

    ui.authActions.preAuthorizeImplicit({
        auth: {
            schema: {
                flow: 'password',
                get: function (key) {
                    return this[key];
                }
            },
            name: 'passport'
        },
        token: {

        },
        isValid: true
    });

Running this closes the "Authorize" lock so a valid token needs to be set. Considering there are a lot of ways to authorize it might be a headache to implement.

We also need to intercept the response for when the authorization is provided, so additional responseInterceptor needs to be configured.

Overall Swagger UI does not store tokens, and probably on purpose. There is no switch to enable this, but looks like there are little things that can be done to remember a token via cookie, local storage, indexdb, etc and when the page is reloaded, populate the token back in.

@joseph-montanez
Copy link
Contributor

So here is an example of remembering the token for OAuth2 password authentication (depends on Laravel Passport). This would go inside resources/views/vendor/l5-swagger/index.blade.php. When you try to authorize's the client_id and client_secret are pre-populated. After you authorize, the token is remembered in LocalStorage, so you can refresh the page and everything should still work.

window.onload = function() {
  var urlToDocs = @json($urlToDocs);
  var passwordClient = @json(\Laravel\Passport\Client::where('password_client', 1)->whereNull('user_id')->first());
  // Build a system
  const ui = SwaggerUIBundle({
    dom_id: '#swagger-ui',

    url: urlToDocs,
    operationsSorter: {!! isset($operationsSorter) ? '"' . $operationsSorter . '"' : 'null' !!},
    configUrl: {!! isset($additionalConfigUrl) ? '"' . $additionalConfigUrl . '"' : 'null' !!},
    validatorUrl: {!! isset($validatorUrl) ? '"' . $validatorUrl . '"' : 'null' !!},
    oauth2RedirectUrl: "{{ route('l5-swagger.oauth2_callback') }}",

    requestInterceptor: function(request) {
      request.headers['X-CSRF-TOKEN'] = @json(csrf_token());
      return request;
    },

    responseInterceptor: function (response) {
        if (response.status >= 200 && response.status < 300) {
            var docsUrl = @json(config('l5-swagger.paths.docs_json'));
            var storageKeys = Object.keys(window.localStorage);

            if (response.url.indexOf(urlToDocs) < 0 && storageKeys.indexOf('token') < 0 && response.obj) {
              window.localStorage.setItem('token', JSON.stringify(response.obj));
            }
        }
        return response;
    },

    presets: [
      SwaggerUIBundle.presets.apis,
      SwaggerUIStandalonePreset
    ],

    plugins: [
      SwaggerUIBundle.plugins.DownloadUrl
    ],

    layout: "StandaloneLayout"
  });

  if (passwordClient) {
    ui.initOAuth({
      clientId: passwordClient.id,
      clientSecret: passwordClient.secret,
    });
  }

  var tokenData = window.localStorage.getItem('token');
  var token = null;
  if (tokenData) {
      try {
          token = JSON.parse(tokenData);
      } catch(e) {

      }
  }

  if (token) {
      ui.authActions.preAuthorizeImplicit({
          auth: {
              schema: {
                  flow: 'password',
                  get: function (key) {
                      return this[key];
                  }
              },
              name: 'passport'
          },
          token: token,
          isValid: true
      });
  }

  window.ui = ui
}

There is still the issue of if you log out, or reauthorize none of the token data is updated. Maybe there is a hook in Swagger UI for logging up / refreshing tokens.

@piwel
Copy link

piwel commented Jun 6, 2018

Thanks @joseph-montanez for this code !

The secret is (nowadays, on Laravel 5.6) hidden by default from the JSON view.

You could get it by completing the third line with makeVisible('secret') :

    var passwordClient = @json(\Laravel\Passport\Client::where('password_client', 1)->whereNull('user_id')->first()->makeVisible('secret'));

But be sure to know what you're doing :)

And by the way, add this somewhere to keep the information that the user has logged out on refresh :

    var oldLogout = ui.authActions.logout;
    ui.authActions.logout = function(payload) {
        window.localStorage.removeItem('token');
        return oldLogout(payload);
    };

@Razzwan
Copy link

Razzwan commented Jan 8, 2020

This is my solution for Bearer authKey in header:

(function() {
  const TOKEN_NAME = 'JwtToken';
  setTimeout(function() {
    // 1. If token already exists, set it from sessionStorage
    const JWTToken = sessionStorage.getItem(TOKEN_NAME);
    if (JWTToken) {
      ui.authActions.preAuthorizeImplicit({
        auth: {
          schema: {
            type: 'apiKey',
            in: 'header',
            name: 'Authorication',
            get: function(key) {
              return this[key];
            },
          },
          name: 'bearer',
          value: JWTToken,
        },
        token: {},
        isValid: true,
      });
    }

    // 2. If token was not set yet, save it to sessionStorage
    const openBtn = document.querySelector('btn authorize unlocked');
    if (openBtn) {
      openBtn.addEventListener('click', function() {
        setTimeout(function() {
          const authBtn = document.querySelector('btn modal-btn auth authorize button');
          authBtn.addEventListener('click', function(e) {
            const tokenInput = document.querySelector('input:not([class])');
            if (tokenInput && tokenInput.value) {
              sessionStorage.setItem(TOKEN_NAME, tokenInput.value);
              console.log('Token vas successfully saved!!!');
            }
          });
        }, 1000);
      });
    }
  }, 1000);
})();

@MattSmith74
Copy link

MattSmith74 commented Jan 14, 2020

hi @RAZWANN, can you post your answer here? answer was not working by the way,

https://stackoverflow.com/questions/59571440/store-swagger-ui-tokens-permanently-after-browser-refresh-or-computer-restart

@mateo2181
Copy link

This is my solution:

// resources/views/vendor/swagger-lume/index.blade.php

window.onload = function() {

        const TOKEN_NAME = 'JwtToken';

        // Build a system
        const ui = SwaggerUIBundle({
            dom_id: '#swagger-ui',

            url: "{!! $urlToDocs !!}",
            operationsSorter: {!! isset($operationsSorter) ? '"' . $operationsSorter . '"' : 'null' !!},
            configUrl: {!! isset($additionalConfigUrl) ? '"' . $additionalConfigUrl . '"' : 'null' !!},
            validatorUrl: {!! isset($validatorUrl) ? '"' . $validatorUrl . '"' : 'null' !!},
            oauth2RedirectUrl: "{{ route('swagger-lume.oauth2_callback') }}",

            presets: [
                SwaggerUIBundle.presets.apis,
                SwaggerUIStandalonePreset
            ],
            onComplete: () => {
                const JWTToken = localStorage.getItem(TOKEN_NAME);
                if(JWTToken) {
                    ui.preauthorizeApiKey("passport", JWTToken)
                }
            },
            plugins: [
                SwaggerUIBundle.plugins.DownloadUrl
            ],

            layout: "StandaloneLayout"
        })

        window.ui = ui

        setTimeout(function() {
            const JWTToken = localStorage.getItem(TOKEN_NAME);

            // 2. If token was not set yet, save it to localStorage
            const openBtn = document.querySelector('.btn.authorize.unlocked');
            if (openBtn) {
            openBtn.addEventListener('click', function() {
                setTimeout(function() {
                const authBtn = document.querySelector('.btn.modal-btn.auth.authorize');
                authBtn.addEventListener('click', function(e) {
                    const tokenInput = document.querySelector('input:not([class])');
                    if (tokenInput && tokenInput.value) {
                    localStorage.setItem(TOKEN_NAME, tokenInput.value);
                    console.log('Token vas successfully saved!!!');
                    }
                });
                }, 1000);
            });
            }
        }, 1000);
    };

@huer12
Copy link

huer12 commented Sep 6, 2020

Adding a listener to the authorize button did not work for me, so I came up with this solution:

(function () {
    const API_KEY = 'ApiKey';
    setTimeout(function () {

      // store the api key in the local storage
      var originalAuthorize = ui.authActions.authorize;
      
      ui.authActions.authorize = function (payload) {
        window.localStorage.setItem(API_KEY, payload.ApiKeyScheme.value);
        return originalAuthorize(payload);
      };

      // if logout is clicked delete the api key in the local storage
      var originalLogout = ui.authActions.logout;

      ui.authActions.logout = function (payload) {
        window.localStorage.removeItem(API_KEY);
        return originalLogout(payload);
      };

      // If token already exists, load it from local storage
      const apiKey = window.localStorage.getItem(API_KEY);
      if (apiKey) {
        window.ui.preauthorizeApiKey('ApiKeyScheme', apiKey);
      }
    }, 1000);
  })();

Note, depending on your security settings, you will get different data in the payload of the authorize function. Also the preAuthorize must match your security settings.

@hakankaan
Copy link

Just putting "persistAuthorization: true" to SwaggerUIBundle json solved my problem.

const ui = SwaggerUIBundle({
dom_id: '#swagger-ui',

url: "{!! $urlToDocs !!}",
operationsSorter: {!! isset($operationsSorter) ? '"' . $operationsSorter . '"' : 'null' !!},
configUrl: {!! isset($configUrl) ? '"' . $configUrl . '"' : 'null' !!},
validatorUrl: {!! isset($validatorUrl) ? '"' . $validatorUrl . '"' : 'null' !!},
oauth2RedirectUrl: "{{ route('l5-swagger.'.$documentation.'.oauth2_callback') }}",

requestInterceptor: function(request) {
  request.headers['X-CSRF-TOKEN'] = '{{ csrf_token() }}';
  return request;
},

presets: [
  SwaggerUIBundle.presets.apis,
  SwaggerUIStandalonePreset
],

plugins: [
  SwaggerUIBundle.plugins.DownloadUrl
],

layout: "StandaloneLayout",

persistAuthorization: true,
})

You can find other needed parameters here.

@DarkaOnLine
Copy link
Owner

Closing due to no activity for a very long time

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

No branches or pull requests

9 participants