3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CakePHP4 で 認証を乗り越えて Swagger3 を使ってAPIを呼び出す

Last updated at Posted at 2020-05-04

CakePHP4 で Swagger3 を使って API ドキュメントを作る」で APIドキュメントを作ったんですが、「CakePHP4 の認証処理 cakephp/authentication で Auth0 を使った認証を行う」を行ったら、Swagger UI からの API 実行で認証エラーが発生するようになりました。

認証があっても Swagger UI から API 実行を行えるように対応します。

この記事でわかること

  • CakePHP4 で認証を行った Swagger3 のAPI呼び出し方法。
  • CakePHP4 cakephp/authentication で複数の Authenticator の指定方法。
  • この記事内のソースは以下で公開しています。

バージョン情報

バージョン
CakePHP4 4.0.6
cakephp/authentication 2.1.0
zircote/swagger-php 3.0.3

事前準備

docker-compose up -d

APIドキュメント

認証の記述追加

共通のドキュメント内容を記述している ./src/Controller/Api/swagger.php に認証の記述を追加します。

SecurityScheme を記述して、HTTPヘッダ「X-Api-User-Local」に JSON形式でユーザー情報を渡せるようにします。
あくまで、ローカルで実行した場合の対応です。本番環境ではこのようなユーザー情報の渡し方は動かないようにします(無効化します)。

そのあと、すべての API に SecurityScheme で定義した認証を利用するように OpenApi security を記述します。
記述方法については、以下の issue が参考になりました。

src/Controller/Api/swagger.php
// ... snip
/**
 * @OA\SecurityScheme(
 *   securityScheme="api_user_local",
 *   type="apiKey",
 *   in="header",
 *   name="X-Api-User-Local",
 *   description="ローカルで実行した場合のユーザー情報 ({""sub"":""DUMMY""})",
 * ),
 * @OA\OpenApi(
 *   security={
 *     {
 *       "api_user_local":{}
 *     },
 *   },
 * ),
 */

CORS の対応

X-Api-User-Local ヘッダの追加

ローカル開発環境で実行された場合、 CORS に対応したヘッダ情報を付与してレスポンスを返す ./src/Middleware/CorsMiddleware.php を変更します。
Access-Control-Allow-Headers ヘッダとして返却する値に、 X-Api-User-Local を追加して、 X-Api-User-Local ヘッダを API呼び出し時に指定できるようにします。

src/Middleware/CorsMiddleware.php
class CorsMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $response = $handler->handle($request);

        // ローカル開発モードのときのみ CORS に対応する
        if (filter_var(env('SERVER', false))) {
            return $response;
        }

        $response = $response->withHeader('Access-Control-Allow-Origin', '*');
        $response = $response->withHeader('Access-Control-Allow-Methods', '*');
        $response = $response->withHeader('Access-Control-Allow-Headers', 'Content-Type, X-Api-User-Local');
        $response = $response->withHeader('Access-Control-Max-Age', '172800');

        return $response;
    }
}

OPTIONS リクエスト時は認証を行わないようにする

OPTIONS リクエストの処理を行っている ./src/Controller/Api/CorsController.php を変更します。
OPTIONS リクエストは、認証を行わないようにするために beforeFilter()$this->Authentication->addUnauthenticatedActions() を指定します。

src/Controller/Api/CorsController.php
class CorsController extends AppController
{
    public function beforeFilter(EventInterface $event): void
    {
        parent::beforeFilter($event);
        $this->Authentication->addUnauthenticatedActions(['options']);
    }

    public function options(): void
    {
        // ローカル開発モードのときのみ CORS に対応する
        if (filter_var(env('SERVER', false))) {
            throw new NotFoundException('Not support CORS.');
        }

        $this->viewBuilder()->setOption('serialize', []);
    }
}

Authenticator の追加

SwaggerAuthenticator の作成

ローカル開発環境のときのみ、ヘッダ「X-Api-User-Local」による認証を認めます。
ユーザー情報は、ヘッダ「X-Api-User-Local」に JSON 形式でセットされていることを想定しています。

src/Authenticator/SwaggerAuthenticator.php
class SwaggerAuthenticator extends AbstractAuthenticator
{
    protected $_defaultConfig = [];

    public function authenticate(ServerRequestInterface $request): ResultInterface
    {
        // ローカル開発モードのときのみ Swagger によるアクセスを許可するため
        // ローカル開発モードのとき以外は、ユーザー情報なしで返却する
        if (filter_var(env('SERVER', false))) {
            return new Result(null, Result::FAILURE_OTHER, ['Not support Swagger.']);
        }

        $user = $request->getHeaderLine('x-api-user-local');
        if (!$user) {
            return new Result(null, Result::FAILURE_CREDENTIALS_MISSING);
        }

        return new Result(json_decode($user, true), Result::SUCCESS);
    }
}

Application.php の修正

上記で作った SwaggerAuthenticator を利用するように ./src/Application.php を修正します。

API のときのみ SwaggerAuthenticator を利用するように $authenticationService->loadAuthenticator('Swagger') を行います。

Authenticator は記述された順番で呼び出されます。
Auth0Authenticator より前に SwaggerAuthenticator を呼び出すようにします(今回のケースだと、どちらの Authenticator が先に処理されても問題なく動作します)。

src/Application.php
    public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
    {
        $path = $request->getUri()->getPath();
        $isApi = (strpos($path, '/api/') === 0);

        if ($isApi) {
            return $this->getAuthenticationServiceForApi();
        } else {
            return $this->getAuthenticationServiceForPage();
        }
    }

    private function getAuthenticationServiceForApi(): AuthenticationServiceInterface
    {
        $authenticationService = new AuthenticationService([
            'unauthenticatedRedirect' => null,
            'queryParam' => 'redirect',
            'identityClass' => function ($identityData) {
                return new Identity($identityData, [
                    'fieldMap' => [
                        'id' => 'sub',
                    ],
                ]);
            },
        ]);

        $authenticationService->loadAuthenticator('Swagger');
        $authenticationService->loadAuthenticator('Auth0');

        return $authenticationService;
    }

    private function getAuthenticationServiceForPage(): AuthenticationServiceInterface
    {
        $authenticationService = new AuthenticationService([
            'unauthenticatedRedirect' => '/users/login',
            'queryParam' => 'redirect',
            'identityClass' => function ($identityData) {
                return new Identity($identityData, [
                    'fieldMap' => [
                        'id' => 'sub',
                    ],
                ]);
            },
        ]);

        $authenticationService->loadAuthenticator('Auth0');

        return $authenticationService;
    }

動作確認

上記の対応で Swagger UI (僕は、 VSCode の Swagger Viewer
を使っています) からは認証を経由して API を呼び出すことができます。

デフォルトのテンプレートの場合、右上にある Authorize ボタンからユーザー情報を JSON形式(例えば、 {"sub":"DUMMY"})で入力すると、各 API を呼び出すことで確認できます。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?