Swagger Logo

Introduction

Init configuration of Swagger

Step 1: Install Required Packages

Step 2: Configure JWT Authentication

Step 3: Configure Swagger

Controllers configuration

First add to following annotation in the main Controller to create the securityScheme in order to activate the authentication for the routes that are not open:

/**
 *
 * @OA\Info(
 *    title="JGomes site API",
 *    version="1.0.0",
 * ),
 * @OA\SecurityScheme(
 *     type="http",
 *     description="Login with email and password to get the authentication token",
 *     name="Token based on user credentials",
 *     in="header",
 *     scheme="bearer",
 *     bearerFormat="JWT",
 *     securityScheme="apiAuth",
 * )
 */

Annotation example of a Controller function that does not need authentication ( open route to public ):

    /**
     * @OA\Post(
     *     path="/api/send",
     *     summary="Send a message",
     *     tags={"Message"},
     *     @OA\Parameter(
     *         name="name",
     *         in="query",
     *         description="User's name",
     *         required=true,
     *         @OA\Schema(type="string")
     *     ),
     *     @OA\Parameter(
     *         name="email",
     *         in="query",
     *         description="User's email",
     *         required=true,
     *         @OA\Schema(type="string")
     *     ),
     *     @OA\Parameter(
     *         name="subject",
     *         in="query",
     *         description="User's subject",
     *         required=false,
     *         @OA\Schema(type="string")
            ),
     *      @OA\Parameter(
     *          name="content",
     *          in="query",
     *          description="User's content",
     *          required=true,
     *          @OA\Schema(type="string")
     *      ),
     *     @OA\Response(response="201", description="Message sent successfully"),
     *     @OA\Response(response="422", description="Validation errors")
     * )
     */

Annotation of the Controller function that does the login with email / password:

    /**
     * @OA\Post(
     * path="/api/v1/login",
     * summary="Sign in via api",
     * description="Login by username email and password",
     * operationId="authLoginApi",
     * tags={"Authorization"},
     * @OA\RequestBody(
     *    required=true,
     *    description="User authentication",
     *    @OA\JsonContent(
     *       required={"email","password"},
     *       @OA\Property(property="email", type="string", format="text", example="test@test.test"),
     *       @OA\Property(property="password", type="string", format="text", example="Test@123"),
     *    ),
     * ),
     * @OA\Response(
     *    response=422,
     *    description="Wrong credentials response - Password is invalid",
     *    @OA\JsonContent(
     *       @OA\Property(property="message", type="string", example="Sorry, wrong password. Please try again")
     *        )
     *     )
     * )
     */

Annotation example of a Controller function that needs authentication ( private route that needs a valid token to do the request ):

    /**
     * @OA\Get(
     * path="/api/v1/check",
     * summary="Check if user is authenticated",
     * security={{ "apiAuth": {} }},
     * description="Check if user is authenticated",
     * operationId="CheckIfUserIsAuthenticated",
     * tags={"Authorization"},
     * @OA\Response(
     *    response=401,
     *    description="Not authenticated",
     *    @OA\JsonContent(
     *       @OA\Property(property="message", type="string", example="Need to the login first.")
     *        )
     *     )
     *   )
     * )
     */

Extra configurations

Customize Swagger view:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register(): void
    {
        app()->singleton('urlToDocs', function () {
            return env('APP_URL') . '/docs/api-docs.json';
        });

        app()->singleton('swaggeruibundle', function () {
            return env('APP_URL') . '/docs/asset/swagger-ui-bundle.js';
        });

        app()->singleton('swaggeruistandalonepreset', function () {
            return env('APP_URL') . '/docs/asset/swagger-ui-standalone-preset.js';
        });

        app()->singleton('swagger-ui', function () {
            return env('APP_URL') . '/docs/asset/swagger-ui.css';
        });
    }

    public function boot()
    {
        //
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{config('l5-swagger.documentations.'.$documentation.'.api.title')}}</title>
    <link rel="stylesheet" type="text/css" href="{!! app('swagger-ui') !!}">      <--------- HERE
    <style>
    html
    {
        box-sizing: border-box;
        overflow: -moz-scrollbars-vertical;
        overflow-y: scroll;
    }
    *,
    *:before,
    *:after
    {
        box-sizing: inherit;
    }

    body {
      margin:0;
      background: #fafafa;
    }
    </style>
</head>

<body>
<div id="swagger-ui"></div>
<script src="{!! app('swaggeruibundle') !!}"></script>      <--------- HERE
<script src="{!! app('swaggeruistandalonepreset') !!}"></script>      <--------- HERE

<script>
    window.onload = function() {
        // Build a system
        const ui = SwaggerUIBundle({
            dom_id: '#swagger-ui',
            url: "{!! app('urlToDocs') !!}",      <--------- HERE
            operationsSorter: {!! isset($operationsSorter) ? '"' . $operationsSorter . '"' : 'null' !!},
            configUrl: {!! isset($configUrl) ? '"' . $configUrl . '"' : 'null' !!},
            validatorUrl: {!! isset($validatorUrl) ? '"' . $validatorUrl . '"' : 'null' !!},
            oauth2RedirectUrl: "{{ route('l5-swagger.'.$documentation.'.oauth2_callback', [], $useAbsolutePath) }}",

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

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

            plugins: [
                SwaggerUIBundle.plugins.DownloadUrl
            ],

            layout: "StandaloneLayout",
            docExpansion : "{!! config('l5-swagger.defaults.ui.display.doc_expansion', 'none') !!}",
            deepLinking: true,
            filter: {!! config('l5-swagger.defaults.ui.display.filter') ? 'true' : 'false' !!},
            persistAuthorization: "{!! config('l5-swagger.defaults.ui.authorization.persist_authorization') ? 'true' : 'false' !!}",

        })

        window.ui = ui

        @if(in_array('oauth2', array_column(config('l5-swagger.defaults.securityDefinitions.securitySchemes'), 'type')))
        ui.initOAuth({
            usePkceWithAuthorizationCodeGrant: "{!! (bool)config('l5-swagger.defaults.ui.authorization.oauth2.use_pkce_with_authorization_code_grant') !!}"
        })
        @endif
    }
</script>
</body>
</html>
return [
    'default' => 'default',
    'documentations' => [
        'default' => [
            'api' => [
                'title' => 'L5 Swagger UI',
            ],
            .
            .
            .
            .
        ],
    ],
    'defaults' => [
        'routes' => [
            'docs' => 'docs',
            'oauth2_callback' => 'api/oauth2-callback',
            'middleware' => [
                'api' => [],
                'asset' => [],
                'docs' => [],
                'oauth2_callback' => [],
            ],
            'group_options' => [],
        ],

        'paths' => [
            'docs' => storage_path('api-docs'),
            'views' => base_path('resources/views/vendor/l5-swagger'),      <----------- HERE
            'base' => env('L5_SWAGGER_BASE_PATH', null),
            'swagger_ui_assets_path' => env('L5_SWAGGER_UI_ASSETS_PATH', 'vendor/swagger-api/swagger-ui/dist/'),
            'excludes' => [],
        ],

        .
        .
        .
        .
      
        ],
    ],
];

config-file

<?php

Route::prefix('v1')->group(function () {

    .
    .
    .
    .

    // Protected routes by Sanctum
    Route::middleware('auth:sanctum')->group(function ()
    {
          .
          .
          . ( For the routes defined here under the middleware 'auth:sanctum', the Swagger returns a 405 )
          .
          .
          
    });

    // Middleware de fallback to return 401 for unauthenticated requests
    Route::fallback(function () {
        return response()->json(['error' => 'Unauthorized'], 401);      <------- To solve this problem just add this HERE
    });
});

api-route

Every time we change something on the Swagger annotations we need to tun:

Demonstration

( Click on the image to watch the demo video )

Demonstration video