You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用thephpleague OAuth2服务,授权码换令牌时如何验证客户端密钥?

问题描述

我已通过thephpleague客户端+服务端,基于授权码模式+PKCE实现了access token和refresh token的生成。但当前在使用授权码交换access token时,即使设置随机的客户端标识和密钥,仍能生成令牌。我希望仅允许授权列表内的客户端通过验证,恳请指引正确的实现方向!


ClientEntity.php

class ClientEntity implements \League\OAuth2\Server\Entities\ClientEntityInterface
{
    use \League\OAuth2\Server\Entities\Traits\ClientTrait;
    use \League\OAuth2\Server\Entities\Traits\EntityTrait;

    public function __construct()
    {
        $this->name = 'The client App';
        $this->setIdentifier("MyApp");
        $this->redirectUri = "link to process-auth-code.php";
    }
}

ClientRepository.php(我猜测需要在access-token.php中调用validateClient()?)

class ClientRepository implements \League\OAuth2\Server\Repositories\ClientRepositoryInterface
{

    /**
     * @inheritDoc
     */
    public function getClientEntity($clientIdentifier)
    {
        $theClient = new ClientEntity();
        $theClient->setIdentifier($clientIdentifier);

        return $theClient;
    }

    /**
     * @inheritDoc
     */
    public function validateClient($clientIdentifier, $clientSecret, $grantType)
    {
        return true;
    }
}

access-token.php 端点

require_once 'bootstrap.php';

use League\OAuth2\Server\Exception\OAuthServerException;

try {

    $response = $server->respondToAccessTokenRequest($request, $response);

} catch (\League\OAuth2\Server\Exception\OAuthServerException $exception) {

    $response = $exception->generateHttpResponse($response);

} catch (\Exception $exception) {

    $body = new Stream(fopen('php://temp', 'r+'));
    $body->write($exception->getMessage());

    $response = $response->withStatus(500)->withBody($body);
}

require_once 'output.php';

解决方案

问题核心在于你的ClientRepository未做真实的客户端校验:validateClient直接返回true,getClientEntity不管传入什么标识都返回新的客户端实体。按以下步骤修改即可实现授权列表校验:

  • 维护授权客户端列表
    先定义合法客户端的数据源(实际项目建议存储在数据库,以下用数组示例):

    // 示例:授权客户端列表,可替换为数据库查询逻辑
    private $authorizedClients = [
        'MyApp' => [
            'secret' => '$2y$10$...', // 客户端密钥的哈希值,禁止存储明文
            'name' => 'The client App',
            'redirect_uri' => 'link to process-auth-code.php',
            'allowed_grants' => ['authorization_code', 'refresh_token'],
            'is_public' => false // 是否为公共客户端(PKCE模式下公共客户端无需密钥)
        ],
        // 其他授权客户端配置...
    ];
    
  • 修改ClientEntity类
    让它支持从授权数据初始化,替代硬编码配置:

    class ClientEntity implements \League\OAuth2\Server\Entities\ClientEntityInterface
    {
        use \League\OAuth2\Server\Entities\Traits\ClientTrait;
        use \League\OAuth2\Server\Entities\Traits\EntityTrait;
    
        public function __construct(array $clientData)
        {
            $this->name = $clientData['name'];
            $this->setIdentifier($clientData['id']);
            $this->redirectUri = $clientData['redirect_uri'];
            $this->isPublic = $clientData['is_public'];
        }
    }
    
  • 修改getClientEntity方法
    仅当客户端标识在授权列表中时,才返回对应ClientEntity,否则返回null(会触发OAuthServerException):

    public function getClientEntity($clientIdentifier)
    {
        if (!isset($this->authorizedClients[$clientIdentifier])) {
            return null;
        }
    
        $clientData = $this->authorizedClients[$clientIdentifier];
        $clientData['id'] = $clientIdentifier;
    
        return new ClientEntity($clientData);
    }
    
  • 修改validateClient方法
    校验客户端密钥合法性,同时验证授权类型是否允许:

    public function validateClient($clientIdentifier, $clientSecret, $grantType)
    {
        if (!isset($this->authorizedClients[$clientIdentifier])) {
            return false;
        }
    
        $clientConfig = $this->authorizedClients[$clientIdentifier];
    
        // 公共客户端跳过密钥校验(PKCE模式下适用)
        if (!$clientConfig['is_public'] && !password_verify($clientSecret, $clientConfig['secret'])) {
            return false;
        }
    
        // 校验当前授权类型是否在客户端允许范围内
        if (!in_array($grantType, $clientConfig['allowed_grants'])) {
            return false;
        }
    
        return true;
    }
    
  • 额外注意事项

    • 客户端密钥必须用password_hash()生成哈希值后存储,禁止明文存储。
    • access-token.php无需手动调用validateClient,respondToAccessTokenRequest会自动触发ClientRepository的校验逻辑。

内容的提问来源于stack exchange,提问作者A KHAN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 22:00:56