facile-it/php-openid-client

OpenID (OIDC) 客户端

0.3.5 2023-11-21 10:07 UTC

README

完整的 OpenID 客户端实现。

Latest Stable Version Total Downloads License codecov Build Status

大多数库代码基于出色的 node-openid-client

可能需要 PHP 扩展 gmp

实现规范和功能

支持以下草案规范

安装

要求

  • psr/http-client-implementation 实现
  • psr/http-factory-implementation 实现
  • psr/http-message-implementation 实现
composer require facile-it/php-openid-client

RSA 签名算法已从 JWT 框架包中包含。如果您需要其他算法,您应手动安装它。

基本用法

对于基本用法,您不需要其他依赖项包。

每个构建器都有方法可以自定义带有其他依赖项的实例。

use Facile\OpenIDClient\Client\ClientBuilder;
use Facile\OpenIDClient\Issuer\IssuerBuilder;
use Facile\OpenIDClient\Client\Metadata\ClientMetadata;
use Facile\OpenIDClient\Service\Builder\AuthorizationServiceBuilder;
use Facile\OpenIDClient\Service\Builder\UserInfoServiceBuilder;
use Psr\Http\Message\ServerRequestInterface;

$issuer = (new IssuerBuilder())
    ->build('https://example.com/.well-known/openid-configuration');
$clientMetadata = ClientMetadata::fromArray([
    'client_id' => 'client-id',
    'client_secret' => 'my-client-secret',
    'token_endpoint_auth_method' => 'client_secret_basic', // the auth method tor the token endpoint
    'redirect_uris' => [
        'https://my-rp.com/callback',    
    ],
]);
$client = (new ClientBuilder())
    ->setIssuer($issuer)
    ->setClientMetadata($clientMetadata)
    ->build();

// Authorization

$authorizationService = (new AuthorizationServiceBuilder())->build();
$redirectAuthorizationUri = $authorizationService->getAuthorizationUri(
    $client,
    ['login_hint' => 'user_username'] // custom params
);
// you can use this uri to redirect the user


// Get access token

/** @var ServerRequestInterface::class $serverRequest */
$serverRequest = null; // get your server request
$callbackParams = $authorizationService->getCallbackParams($serverRequest, $client);
$tokenSet = $authorizationService->callback($client, $callbackParams);

$idToken = $tokenSet->getIdToken(); // Unencrypted id_token, if returned
$accessToken = $tokenSet->getAccessToken(); // Access token, if returned
$refreshToken = $tokenSet->getRefreshToken(); // Refresh token, if returned

// check if we have an authenticated user
if ($idToken) {
    $claims = $tokenSet->claims(); // IdToken claims
} else {
    throw new \RuntimeException('Unauthorized')
}


// Refresh token
$tokenSet = $authorizationService->refresh($client, $tokenSet->getRefreshToken());


// Get user info
$userInfoService = (new UserInfoServiceBuilder())->build();
$userInfo = $userInfoService->getUserInfo($client, $tokenSet);

客户端注册

请参阅 OpenID Connect 动态客户端注册 1.0RFC7591 OAuth 2.0 动态客户端注册协议

use Facile\OpenIDClient\Service\Builder\RegistrationServiceBuilder;

$registration = (new RegistrationServiceBuilder())->build();

// registration
$metadata = $registration->register(
    $issuer,
    [
        'client_name' => 'My client name',
        'redirect_uris' => ['https://my-rp.com/callback'],
    ],
    'my-initial-token'
);

// read
$metadata = $registration->read($metadata['registration_client_uri'], $metadata['registration_access_token']);

// update
$metadata = $registration->update(
    $metadata['registration_client_uri'],
    $metadata['registration_access_token'],
    array_merge($metadata, [
        // new metadata
    ])
);

// delete
$registration->delete($metadata['registration_client_uri'], $metadata['registration_access_token']);

令牌洞察

请参阅 RFC7662 - OAuth 2.0 令牌洞察

use Facile\OpenIDClient\Service\Builder\IntrospectionServiceBuilder;

$service = (new IntrospectionServiceBuilder())->build();

$params = $service->introspect($client, $token);

令牌撤销

请参阅 RFC7009 - OAuth 2.0 令牌撤销

use Facile\OpenIDClient\Service\Builder\RevocationServiceBuilder;

$service = (new RevocationServiceBuilder())->build();

$params = $service->revoke($client, $token);

请求对象

您可以使用 Facile\OpenIDClient\RequestObject\RequestObjectFactory 类创建授权请求的 请求对象

这将创建基于您的客户端元数据的签名(可选加密)JWT 令牌。

use Facile\OpenIDClient\RequestObject\RequestObjectFactory;

$factory = new RequestObjectFactory();
$requestObject = $factory->create($client, [/* custom claims to include in the JWT*/]);

然后您可以使用它来创建 AuthRequest

use Facile\OpenIDClient\Authorization\AuthRequest;

$authRequest = AuthRequest::fromParams([
    'client_id' => $client->getMetadata()->getClientId(),
    'redirect_uri' => $client->getMetadata()->getRedirectUris()[0],
    'request' => $requestObject,
]);

聚合和分布式声明

该库可以处理 聚合和分布式声明

use Facile\OpenIDClient\Claims\AggregateParser;
use Facile\OpenIDClient\Claims\DistributedParser;

$aggregatedParser = new AggregateParser();

$claims = $aggregatedParser->unpack($client, $userInfo);

$distributedParser = new DistributedParser();
$claims = $distributedParser->fetch($client, $userInfo);

使用中间件

有一些中间件和处理程序可用

SessionCookieMiddleware

此中间件应始终位于中间件链的顶部,以提供用于 statenonce 参数的会话。

要使用它,您应该安装 dflydev/fig-cookies 包。

$ composer require "dflydev/fig-cookies:^2.0"
use Facile\OpenIDClient\Middleware\SessionCookieMiddleware;
use Psr\SimpleCache\CacheInterface;

// Use your PSR-16 simple-cache implementation to persist sessions
/** @var CacheInterface $cache */
$middleware = new SessionCookieMiddleware($cache/* , $cookieName = "openid", $ttl = 300 */);

该中间件提供了一个带有 Facile\OpenIDClient\Session\AuthSessionInterface 状态实例的 Facile\OpenIDClient\Session\AuthSessionInterface 属性,用于持久化会话数据。

使用其他会话存储

如果您有其他会话存储,您可以在 Facile\OpenIDClient\Session\AuthSessionInterface 属性中处理它并提供一个 Facile\OpenIDClient\Session\AuthSessionInterface 实例。

ClientProviderMiddleware

此中间件应始终位于中间件链的顶部,以向其他中间件提供客户端。

use Facile\OpenIDClient\Middleware\ClientProviderMiddleware;

$client = $container->get('openid.clients.default');
$middleware = new ClientProviderMiddleware($client);

AuthRequestProviderMiddleware

此中间件提供用于与 AuthRedirectHandler 一起使用的身份验证请求。

use Facile\OpenIDClient\Middleware\AuthRequestProviderMiddleware;
use Facile\OpenIDClient\Authorization\AuthRequest;

$authRequest = AuthRequest::fromParams([
    'scope' => 'openid',
    // other params...
]);
$middleware = new AuthRequestProviderMiddleware($authRequest);

AuthRedirectHandler

此处理器将用户重定向到 OpenID 授权页面。

use Facile\OpenIDClient\Middleware\AuthRedirectHandler;
use Facile\OpenIDClient\Service\AuthorizationService;

/** @var AuthorizationService $authorizationService */
$authorizationService = $container->get(AuthorizationService::class);
$middleware = new AuthRedirectHandler($authorizationService);

CallbackMiddleware

此中间件将处理来自 OpenID 提供者的回调。

它将提供一个带有最终 TokenSet 对象的 Facile\OpenIDClient\Token\TokenSetInterface 属性。

use Facile\OpenIDClient\Middleware\CallbackMiddleware;
use Facile\OpenIDClient\Service\AuthorizationService;

/** @var AuthorizationService $authorizationService */
$authorizationService = $container->get(AuthorizationService::class);
$middleware = new CallbackMiddleware($authorizationService);

UserInfoMiddleware

此中间件将从 userinfo 端点获取用户数据,并将用户信息作为数组提供 Facile\OpenIDClient\Middleware\UserInfoMiddleware 属性。

use Facile\OpenIDClient\Middleware\UserInfoMiddleware;
use Facile\OpenIDClient\Service\UserInfoService;

/** @var UserInfoService $userInfoService */
$userInfoService = $container->get(UserInfoService::class);
$middleware = new UserInfoMiddleware($userInfoService);

生产环境中的性能改进

使用缓存以避免在每次请求时都检索发行者配置和密钥是很重要的。

use Psr\SimpleCache\CacheInterface;
use Facile\OpenIDClient\Issuer\IssuerBuilder;
use Facile\OpenIDClient\Issuer\Metadata\Provider\MetadataProviderBuilder;
use Facile\JoseVerifier\JWK\JwksProviderBuilder;

/** @var CacheInterface $cache */
$cache = $container->get(CacheInterface::class); // get your simple-cache implementation

$metadataProviderBuilder = (new MetadataProviderBuilder())
    ->setCache($cache)
    ->setCacheTtl(86400*30); // Cache metadata for 30 days 
$jwksProviderBuilder = (new JwksProviderBuilder())
    ->setCache($cache)
    ->setCacheTtl(86400); // Cache JWKS for 1 day
$issuerBuilder = (new IssuerBuilder())
    ->setMetadataProviderBuilder($metadataProviderBuilder)
    ->setJwksProviderBuilder($jwksProviderBuilder);

$issuer = $issuerBuilder->build('https://example.com/.well-known/openid-configuration');

使用 Psalm

如果您需要使用 Psalm,您可以在 psalm.xml 中包含插件。

<plugins>
    <pluginClass class="Facile\JoseVerifier\Psalm\Plugin" />
</plugins>