lexik/LexikJWTAuthenticationBundle

How to rename tag "Login Check" on swagger ui

SimonDevelop opened this issue · 7 comments

Hi,
I can't find anything about naming the lexik Login Check tag on swagger ui.

Change this: image
To get this: image

Hello @SimonDevelop,
Add or update this part of the code, in your controller -

#[OA\Tag(name: 'Auth')]

probably in your case is -

#[OA\Tag(name: 'Login Check')]

This is the bundle controller, so how can you add an attribute if it's not accessible?

Same question on my mind.
I use Api Platform 3 lexik integration with auto generated login route enpoint in swagger.
Nice feature, however it will be great to have the possibility to override the swagger without to do it manually.

Hey,

You can use OpenApi Decorator for that https://api-platform.com/docs/core/openapi/

Important is to change priority that login_check_post is already added to schema.
so works something like.
#[AsDecorator('api_platform.openapi.factory', -25)]

I realize this is several months after the fact, but I recently wanted to make this particular change and found this discussion. Here's a fully working solution.

<?php

declare(strict_types=1);

namespace App\ApiPlatform;

use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\Model\Operation;
use ApiPlatform\OpenApi\Model\PathItem;
use ApiPlatform\OpenApi\OpenApi;
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
use Symfony\Component\DependencyInjection\ContainerInterface;

#[AsDecorator(
    decorates: 'api_platform.openapi.factory',
    priority: -25,
    onInvalid: ContainerInterface::IGNORE_ON_INVALID_REFERENCE,
)]
class OpenApiFactoryDecorator implements OpenApiFactoryInterface
{
    public function __construct(
        private readonly OpenApiFactoryInterface $decorated,
        private readonly string $loginCheck,
    ) {
    }

    /**
     * @param array<string, mixed> $context
     */
    public function __invoke(array $context = []): OpenApi
    {
        $openApi = ($this->decorated)($context);
        $authPath = $openApi->getPaths()->getPath($this->loginCheck);

        if ($authPath instanceof PathItem && $authPath->getPost() instanceof Operation) {
            $post = $authPath->getPost()->withTags(['Auth']);

            $openApi->getPaths()->addPath(
                $this->loginCheck,
                (new PathItem())->withPost($post),
            );
        }

        return $openApi;
    }
}

I realize this is several months after the fact, but I recently wanted to make this particular change and found this discussion. Here's a fully working solution.

<?php

declare(strict_types=1);

namespace App\ApiPlatform;

use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\Model\Operation;
use ApiPlatform\OpenApi\Model\PathItem;
use ApiPlatform\OpenApi\OpenApi;
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
use Symfony\Component\DependencyInjection\ContainerInterface;

#[AsDecorator(
    decorates: 'api_platform.openapi.factory',
    priority: -25,
    onInvalid: ContainerInterface::IGNORE_ON_INVALID_REFERENCE,
)]
class OpenApiFactoryDecorator implements OpenApiFactoryInterface
{
    public function __construct(
        private readonly OpenApiFactoryInterface $decorated,
        private readonly string $loginCheck,
    ) {
    }

    /**
     * @param array<string, mixed> $context
     */
    public function __invoke(array $context = []): OpenApi
    {
        $openApi = ($this->decorated)($context);
        $authPath = $openApi->getPaths()->getPath($this->loginCheck);

        if ($authPath instanceof PathItem && $authPath->getPost() instanceof Operation) {
            $post = $authPath->getPost()->withTags(['Auth']);

            $openApi->getPaths()->addPath(
                $this->loginCheck,
                (new PathItem())->withPost($post),
            );
        }

        return $openApi;
    }
}

What is $this->loginCheck. API platform can't find this. Did you add this one some where ? Cannot autowire service "App\OpenApi\Staff\LoginDecorator": argument "$loginCheck" of method "__construct()" is type-hinted "string", you should configure its value explicitly.

Yes, I'm injecting the path as a service parameter so I can configure the path once and not hard code it in the decorator. You'll have to explicitly define the service if you want to do the same. Otherwise, you'll get an error when Symfony autowires it.

I've made several other changes, but my configuration looks roughly like this:

# config/services.yaml
parameters:
  # ...
  login_check_path: '/api/login_check'

services:
  # ...
  App\ApiPlatform\OpenApiFactoryDecorator:
    arguments:
      $loginCheck: '%login_check_path%'
# config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
  # ...
  api_platform:
    enabled: true
    check_path: '%login_check_path%'
  # ...
# config/routes/jwt_login_check.yaml
jwt_login_check:
  path: '%login_check_path%'

If you don't want to do that and want to use the default configuration in the documentation, you can safely remove $loginCheck from the OpenApiFactoryDecorator constructor and hard code the login check path. Just replace $this->loginCheck with whatever you're using as a login check path.

This path will need to match the path you've defined in config/routes.yaml (or wherever you've defined it) and config/security.yaml.

For example:

<?php

declare(strict_types=1);

namespace App\ApiPlatform;

use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\Model\Operation;
use ApiPlatform\OpenApi\Model\PathItem;
use ApiPlatform\OpenApi\OpenApi;
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
use Symfony\Component\DependencyInjection\ContainerInterface;

#[AsDecorator(
    decorates: 'api_platform.openapi.factory',
    priority: -25,
    onInvalid: ContainerInterface::IGNORE_ON_INVALID_REFERENCE,
)]
class OpenApiFactoryDecorator implements OpenApiFactoryInterface
{
    public function __construct(
        private readonly OpenApiFactoryInterface $decorated,
    ) {
    }

    /**
     * @param array<string, mixed> $context
     */
    public function __invoke(array $context = []): OpenApi
    {
        $openApi = ($this->decorated)($context);
        $authPath = $openApi->getPaths()->getPath('/api/login_check');

        if ($authPath instanceof PathItem && $authPath->getPost() instanceof Operation) {
            $post = $authPath->getPost()->withTags(['Auth']);

            $openApi->getPaths()->addPath(
                '/api/login_check',
                (new PathItem())->withPost($post),
            );
        }

        return $openApi;
    }
}