Swagger UI is an endpoint, and it should not be public

Serving the interactive documentation from the application is convenient and publishes a complete map of every endpoint, parameter and error code to anybody who finds the URL.

Route::middleware(['auth', 'can:view-api-docs'])->group(function () {
    Route::get('/docs', fn() => view('swagger'));
    Route::get('/openapi.json', fn() => response()->file(
        public_path('openapi.json')
    ));
});

// and the "try it" button, which posts to the REAL API

The try-it button is the part that gets overlooked: it sends real requests with whatever credentials the browser has, against whatever server the document names. Pointing it at production from a documentation page is how somebody deletes an order while reading. Publishing the document publicly is a legitimate choice for a public API and a poor default for an internal one.