aw3r1se/swagger-auth

Persistent authorization and silent token refresh for Swagger UI in Laravel

Maintainers

Package info

github.com/aw3r1se/swagger-auth

pkg:composer/aw3r1se/swagger-auth

Transparency log

Statistics

Installs: 5

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-03 04:30 UTC

This package is auto-updated.

Last update: 2026-08-03 04:32:11 UTC


README

Persistent authorization and silent token refresh for Swagger UI in Laravel.

Swagger UI drops its bearer token on reload, and once the token expires every Try it out returns 401. This package captures the token from your own login endpoint, keeps it in localStorage, and refreshes it before it dies.

  • captures the token from the login / refresh response and authorizes the UI
  • refreshes ahead of expiry — the interceptor reads the JWT expiry claim and awaits a refresh once less than skew seconds are left, so no request goes out with a dead token
  • collapses concurrent refreshes into one in-flight request
  • clears the token on logout, optionally refreshes in the background on an unexpected 401
  • adds a toggle next to Authorize that turns all of it off

Install

composer require aw3r1se/swagger-auth

That is the whole setup. The provider is auto-discovered, appends a middleware to l5-swagger.defaults.routes.middleware.api, and that middleware injects the script before </head>. The script wraps window.SwaggerUIBundle, so it does not care where in the markup it lands. An interceptor already set by the view is chained, not dropped.

It also coerces persistAuthorization: the stock l5-swagger view renders the string "false", which is truthy in JavaScript.

Toggle

A key-with-arrows button next to Authorize. Off means no capture, no refresh, no clearing — Swagger UI behaves as if the package were absent, which is what you want when pasting a token by hand or reproducing something with a deliberately stale one. On it turns gold and spins once.

State lives in localStorage under swagger-auth.enabled and survives reloads. Also scriptable:

SwaggerAuth.enabled();        // => true
SwaggerAuth.setEnabled(false);

Configuration

php artisan vendor:publish --tag=swagger-auth-config
Key Default
scheme bearerAuth security scheme from your spec
token_key access_token field of the login/refresh response body
expiry_claim exp JWT payload claim holding the unix expiry
skew 30 seconds before expiry to refresh ahead of time
routes.login /auth/login matched by pathname; null disables the hook
routes.refresh /auth/refresh
routes.logout /auth/logout
inject true auto-injection; false for manual mode
persist_authorization true null keeps l5-swagger's own setting
toggle true the button next to Authorize
labels.on / labels.off its title attribute
csrf true send X-CSRF-TOKEN, if the request has a session
refresh_on_unauthorized true refresh on a 401; the request is not replayed

Every key has an environment counterpart — SWAGGER_AUTH_SCHEME, SWAGGER_AUTH_REFRESH_PATH, SWAGGER_AUTH_SKEW, …

The view is publishable too, under --tag=swagger-auth-views.

Manual mode

For a hand-written page, or a Swagger UI not served by l5-swagger, set inject to false:

@include('swagger-auth::script')

<script>
    window.ui = SwaggerUIBundle({
        requestInterceptor: SwaggerAuth.requestInterceptor,
        responseInterceptor: SwaggerAuth.responseInterceptor,
        persistAuthorization: true,
        // ...
    });
</script>

The interceptors read window.ui, assigned only after SwaggerUIBundle returns, so requests issued before that — the spec fetch — pass through untouched.

Notes

darkaonline/l5-swagger is suggested, not required: without it the provider leaves the config alone and manual mode still works. zircote/swagger-php and the Swagger UI assets are not involved at all.

The refresh endpoint must take the current token in the Authorization header and answer with the new one under token_key — the default shape of php-open-source-saver/jwt-auth and tymon/jwt-auth. Tokens with no decodable expiry claim are left alone and fall back to refresh_on_unauthorized. A refresh that comes back 401 or 403 clears the token; anything else — a network blip, a malformed body — is logged and left for the next attempt.

X-CSRF-TOKEN is only sent when the documentation route actually starts a session. Out of the box l5-swagger registers its routes without the web group, so there is none and the header is omitted rather than filled with a token belonging to nobody.

The token sits in localStorage, readable by any XSS on the same origin. Keep Swagger UI off production, or behind authentication.

Credits

Toggle icon: rotate-ccw-key from Lucide, ISC.

License

MIT