使用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
相关产品推荐
相关产品推荐

