1
votes

I'm trying to document my Symfony 3.4 application API using nelmio/api-doc-bundle but fail to create a security scheme.

Generating the documentation itself works as expected with the following configuration:

nelmio_api_doc:
    documentation:
        info:
            description: FooBar API
            title: FooBar
            version: 1.0.0
    routes:
        path_patterns:
            - ^/api/

And the following annotations:

/**
 * @SWG\Get(
 *     security={
 *         {"ApiKeyAuth":{}}
 *     },
 *     @SWG\Response(
 *         response=200,
 *         description="Returns all [Foo]",
 *         @SWG\Schema(
 *             type="array",
 *             @Model(type=App\Entity\Foo::class)
 *         )
 *     ),
 *     @SWG\Response(
 *         response=404,
 *         description="Returns an error when no [Foo] were found"
 *     )
 * )
 */
public function cgetAction(): Response
{
    // ...
}

So I get a proper JSON file like this:

{
    "swagger" : "2.0",
    "info" : {
        "title" : "FooBar",
        "description" : "FooBar API",
        "version" : "1.0.0"
    },
    "paths" : {
        "\/api\/foo" : {
            "get" : {
                "responses" : {
                    "200" : {
                        "description" : "Returns all [Foo]",
                        "schema" : {
                            "items" : {
                                "$ref" : "#\/definitions\/Foo"
                            },
                            "type" : "array"
                        }
                    },
                    "404" : {
                        "description" : "Returns an error when no [Foo] were found"
                    }
                },
                "security" : [
                    {
                        "ApiKeyAuth" : [ ]
                    }
                ]
            }
        }
    },
    "definitions" : {
        "Foo" : {
            "properties" : {
                "id" : {
                    "type" : "integer"
                }
            },
            "type" : "object"
        }
    }
}

Now the problem is that I need to define ApiKeyAuthanywhere. Based on the examples I found ...

https://github.com/zircote/swagger-php/blob/master/Examples/petstore.swagger.io/controllers/StoreController.php

https://github.com/zircote/swagger-php/blob/master/Examples/petstore.swagger.io/security.php

https://swagger.io/docs/specification/2-0/authentication/api-keys/

... that might look like the following:

/**
 * @SWG\SecurityScheme(
 *     name="X-API-KEY",
 *     type="apiKey",
 *     in="header",
 *     securityDefinition="ApiKeyAuth"
 * )
 */

But regardless of where I put this in the controller it is not recognized.

So where is the right place for it?

Can I configure the api-doc-bundle to recognize a file with global definitions?

Do I need to create the definition in the config and not as annotation?

Does it work at all?

1

1 Answers

1
votes

A small change was required to make it work ...

/**
 * @SWG\Get(
 *     security={
 *         {"ApiKeyAuth":{}}
 *     },
 *     ...
 *     @SWG\Swagger(
 *         schemes={"https"},
 *         @SWG\SecurityScheme(
 *             name="X-API-KEY",
 *             type="apiKey",
 *             in="header",
 *             securityDefinition="ApiKeyAuth",
 *             description="API key"
 *         )
 *     )
 * )
 */
public function cgetAction(): Response
{
    // ...
}

Instead of adding the @SWG\SecurityScheme annotation at class level, or alongside @SWG\Get, placing it inside the request annotation block and wrapping it in a @SWG\Swagger block made the security definition show up.

Nevertheless, this was not sufficient as it involves a lot of duplication, and, moreover swagger-php fails with a duplicate definition error.

Thus I created a generic index controller which does nothing else but providing the security scheme annotation. Though this feels far from being the actual solution it solved the issue for now.

Here is the dummy controller:

<?php

namespace App\Controller\Api;

use FOS\RestBundle\Controller\Annotations\Route;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Routing\ClassResourceInterface;
use Swagger\Annotations as SWG;
use Symfony\Component\HttpFoundation\Response;

class IndexController extends FOSRestController implements ClassResourceInterface
{

    /**
     * @Route("/")
     * @SWG\Get(
     *     security={
     *         {"ApiKeyAuth":{}}
     *     },
     *     @SWG\Response(
     *         response=200,
     *         description="Returns 200 if the request was authorized",
     *     ),
     *     @SWG\Response(
     *         response=401,
     *         description="Returns 401 if the X-API-KEY header is missing or the provided token is not valid"
     *     ),
     *     @SWG\Swagger(
     *         schemes={"https"},
     *         @SWG\SecurityScheme(
     *             name="X-API-KEY",
     *             type="apiKey",
     *             in="header",
     *             securityDefinition="ApiKeyAuth",
     *             description="API key"
     *         )
     *     )
     * )
     */
    public function getAction(): Response
    {
        return $this->handleView($this->view(null, Response::HTTP_OK));
    }

}