<?php

declare(strict_types=1);

/*
 * Copyright (c) 2017-2023 François Kooman <fkooman@tuxed.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

namespace fkooman\OAuth\Server;

use fkooman\OAuth\Server\Exception\InvalidTokenException;

/**
 * Perform additional tests on the AccessToken to make sure it is valid for
 * us. This *Local* verifier checks two things:.
 *
 * 1. Make sure the OAuth client that requested the token still exists;
 * 2. Make sure the *authorization* still exists;
 *
 * This verifier is only useful when the API endpoint runs on the same system
 * as the OAuth server. If this is distributed, other verifiers SHOULD be
 * created, e.g. remote token introspection.
 */
class LocalAccessTokenVerifier implements AccessTokenVerifierInterface
{
    private ClientDbInterface $clientDb;
    private StorageInterface $storage;

    public function __construct(ClientDbInterface $clientDb, StorageInterface $storage)
    {
        $this->clientDb = $clientDb;
        $this->storage = $storage;
    }

    public function verify(AccessToken $accessToken): void
    {
        // the client MUST still be there
        if (null === $this->clientDb->get($accessToken->clientId())) {
            throw new InvalidTokenException(sprintf('client "%s" no longer registered', $accessToken->clientId()));
        }

        // the authorization MUST exist in the DB as well, otherwise it was
        // revoked...
        if (null === $this->storage->getAuthorization($accessToken->authKey())) {
            throw new InvalidTokenException(sprintf('authorization for client "%s" with scope "%s" no longer exists', $accessToken->clientId(), (string) $accessToken->scope()));
        }
    }
}
