benjaminfavre / oauth2-http-client
一个轻量级的 OAuth 2 装饰器,用于 Symfony HTTP 客户端。
Requires
- php: ^8.0
- ext-json: *
- symfony/cache-contracts: ^3.0
- symfony/http-client-contracts: ^3.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.28
- phpunit/phpunit: ^9.4.3
- sylius-labs/coding-standard: ^4.2
- symfony/cache: ^6.0
- symfony/http-client: ^6.0
- vimeo/psalm: ^4.26
Provides
This package is auto-updated.
Last update: 2024-08-31 00:31:34 UTC
README
这是一个用于 Symfony HTTP 客户端 的装饰器,它可以帮助您调用受 OAuth 2 保护的 API 端点。它处理与 OAuth 2 服务器的所有认证协议,让您专注于仅进行业务 API 调用。
设计得简约轻量,实际上没有依赖性,当然除了 Symfony Contracts 之外;并且它只需要 PHP JSON 扩展。
OAuth 2 是一个相对复杂的协议,提供了多种认证方式(在 OAuth 术语中称为“授权类型”)。这个装饰器的目标是提供所有标准授权类型。然而,您将要认证的 OAuth 2 服务器可能不会遵循严格的 OAuth 2 标准。这就是为什么装饰器被设计成可以自定义认证过程的每一步。
安装
composer require benjaminfavre/oauth2-http-client
用法
use Symfony\Component\HttpClient\HttpClient; use BenjaminFavre\OAuthHttpClient\OAuthHttpClient; use BenjaminFavre\OAuthHttpClient\GrantType\ClientCredentialsGrantType; $httpClient = HttpClient::create(); // Here we will use the client credentials grant type but it could be any other grant type $grantType = new ClientCredentialsGrantType( $httpClient, 'https://github.com/login/oauth/access_token', // The OAuth server token URL 'the-client-id', 'the-client-password' ); $httpClient = new OAuthHttpClient($httpClient, $grantType); // Then use $httpClient to make your API calls: // $httpClient->request(...);
它的工作原理
每次您发出 HTTP 请求时,装饰器将
- 从缓存中获取访问令牌,如果缓存中没有,则从 OAuth 服务器获取;
- 修改您的请求以添加访问令牌(通常在头部中);
- 进行 API 调用并返回响应;
- 如果第一次 API 调用失败是由于令牌过期,则可以尝试使用新的访问令牌再次尝试。
自定义
实现以下接口之一以自定义相关的认证步骤,并通过相关的装饰器设置器传递您类的实例。
GrantTypeInterface
一个负责从 OAuth 服务器获取访问令牌的类。装饰器自带四种标准授权类型
- AuthorizationCodeGrantType;
- ClientCredentialsGrantType;
- PasswordGrantType;
- RefreshTokenGrantType.
TokensCacheInterface
一个负责在缓存中存储和检索令牌的类。默认情况下,装饰器使用 MemoryTokensCache,它将令牌缓存在内存中。
RequestSignerInterface
一个负责修改 API 请求以添加访问令牌的类。默认情况下,装饰器使用 BearerHeaderRequestSigner,它在 Authorization 头部中添加访问令牌。您可以使用 HeaderRequestSigner 在其他头部中添加访问令牌,或者您可以实现接口进行更多自定义。
ResponseCheckerInterface
一个负责检查 API 调用是否由于令牌过期而失败的类。默认情况下,装饰器使用 StatusCode401ResponseChecker,它将 401 响应代码识别为需要续订访问令牌的信号。这可能导致误报(401 响应代码可能由于令牌过期之外的原因返回),因此如果您希望您的 OAuth 服务器返回可利用的细粒度错误原因,则可以实现该接口。
完整的 Symfony 特定示例
以下是如何在 Symfony 应用程序中使用此库的完整示例。
- 自定义授权类型
- 使用 Redis 作为缓存层
- 使用 范围 HTTP 客户端定义
- 使用 OAuth 服务器和 API 的不同 URL
首先,我们需要定义2个HTTP客户端:一个用于OAuth服务器,另一个用于API。
framework: http_client: scoped_clients: sharepoint_oauth.client: scope: '%env(resolve:SHAREPOINT_OAUTH_URL)%' headers: Accept: 'application/json;odata=verbose' # other specific headers or settings if needed sharepoint_api.client: scope: '%env(resolve:SHAREPOINT_API_URL)%' headers: Accept: 'application/json;odata=verbose' # other specific headers or settings if needed
其次,我们需要定义一个自定义的授权类型(Grant Type),它将从OAuth服务器获取访问令牌以连接到SharePoint,并使用上面定义的sharepoint_oauth.client
。
故意与内置的ClientCredentialsGrantType
不同,以展示我们如何自定义认证过程。
<?php declare(strict_types=1); namespace App\Sharepoint; use BenjaminFavre\OAuthHttpClient\GrantType\GrantTypeInterface; use BenjaminFavre\OAuthHttpClient\GrantType\Tokens; use BenjaminFavre\OAuthHttpClient\GrantType\TokensExtractor; use Symfony\Component\HttpFoundation\Request; use Symfony\Contracts\HttpClient\HttpClientInterface; final class CustomClientCredentialsGrantType implements GrantTypeInterface { use TokensExtractor; public function __construct( private HttpClientInterface $client, private string $sharepointOauthClientId, private string $sharepointOauthClientSecret, private string $sharepointOauthUrl, private string $sharepointOauthResource, ) { } public function getTokens(): Tokens { $response = $this->client->request(Request::METHOD_POST, $this->sharepointOauthUrl, [ 'body' => http_build_query([ 'grant_type' => 'client_credentials', 'client_id' => $this->sharepointOauthClientId, 'client_secret' => $this->sharepointOauthClientSecret, 'resource' => $this->sharepointOauthResource, ]), ]); return $this->extractTokens($response); } }
为了传递所需的参数给授权类型,我们需要在service.yaml
中定义服务并绑定参数。
services: App\Sharepoint\CustomClientCredentialsGrantType: bind: $client: '@sharepoint_oauth.client' string $sharepointOauthClientId: '%env(SHAREPOINT_OAUTH_CLIENT_ID)%' string $sharepointOauthClientSecret: '%env(SHAREPOINT_OAUTH_CLIENT_SECRET)%' string $sharepointOauthResource: '%env(SHAREPOINT_OAUTH_RESOURCE)%' string $sharepointOauthUrl: '%env(SHAREPOINT_OAUTH_URL)%'
然后,我们需要定义我们的缓存层而不是默认的in-memory
,通过在services.yaml
中添加以下服务定义来实现。
BenjaminFavre\OAuthHttpClient\TokensCache\SymfonyTokensCacheAdapter: bind: $cache: '@cache.app' $cacheKey: 'sharepoint'
@cache.app
是一个应用程序缓存,配置在你的系统中。在我们的例子中,这是一个Redis缓存。
framework: cache: app: cache.adapter.redis default_redis_provider: '%env(REDIS_URL)%'
最后,我们可以在services.yaml
中定义我们的OAuthHttpClient
服务,该服务使用sharepoint_api.client
并设置配置的Redis缓存。
BenjaminFavre\OAuthHttpClient\OAuthHttpClient: bind: $client: '@sharepoint_api.client' $grant: '@App\Sharepoint\CustomClientCredentialsGrantType' calls: - [ setCache, [ '@BenjaminFavre\OAuthHttpClient\TokensCache\SymfonyTokensCacheAdapter' ] ]
之后,OAuthHttpClient
服务就准备好在应用程序的任何其他类中使用。
public function __construct( private OAuthHttpClient $sharepointClient, ) { }