Full OpenID client implementation.
Most of the library code is based on the awesome node-openid-client
.
The PHP extension gmp
could be required.
- OAuth 2.0 RFC 6749 & OpenID Connect Core 1.0
- Authorization (Authorization Code Flow, Implicit Flow, Hybrid Flow)
- UserInfo Endpoint and ID Tokens including Signing and Encryption (using the JWT Framework library)
- Passing a Request Object by Value or Reference including Signing and Encryption
- Offline Access / Refresh Token Grant
- Client Credentials Grant
- Client Authentication incl.
client_secret_jwt
andprivate_key_jwt
methods
- OpenID Connect Discovery 1.0
- OpenID Connect Dynamic Client Registration 1.0 and RFC7591 OAuth 2.0 Dynamic Client Registration Protocol
- OAuth 2.0 Form Post Response Mode
- RFC7009 - OAuth 2.0 Token Revocation
- RFC7662 - OAuth 2.0 Token Introspection
- RFC7592 - OAuth 2.0 Dynamic Client Registration Management Protocol
- JWT Response for OAuth Token Introspection - draft 03
- JWT Secured Authorization Response Mode for OAuth 2.0 (JARM) - draft 02
- OAuth 2.0 JWT Secured Authorization Request (JAR)
- OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound Access Tokens (MTLS) - draft 15
Requirements:
psr/http-client-implementation
implementationpsr/http-factory-implementation
implementationpsr/http-message-implementation
implementation
composer require facile-it/php-openid-client
RSA
signing algorithms are already included from the JWT Framework package`. If you need other algorithms you should install it manually.
For a basic usage you shouldn't require any other dependency package.
Every builder have methods to customize instances with other dependencies.
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);
See OpenID Connect Dynamic Client Registration 1.0 and RFC7591 OAuth 2.0 Dynamic Client Registration Protocol.
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']);
See RFC7662 - OAuth 2.0 Token Introspection.
use Facile\OpenIDClient\Service\Builder\IntrospectionServiceBuilder; $service = (new IntrospectionServiceBuilder())->build(); $params = $service->introspect($client, $token);
See RFC7009 - OAuth 2.0 Token Revocation.
use Facile\OpenIDClient\Service\Builder\RevocationServiceBuilder; $service = (new RevocationServiceBuilder())->build(); $params = $service->revoke($client, $token);
You can create a request object authorization request with the Facile\OpenIDClient\RequestObject\RequestObjectFactory
class.
This will create a signed (and optionally encrypted) JWT token based on your client metadata.
use Facile\OpenIDClient\RequestObject\RequestObjectFactory; $factory = new RequestObjectFactory(); $requestObject = $factory->create($client, [/* custom claims to include in the JWT*/]);
Then you can use it to create the AuthRequest:
use Facile\OpenIDClient\Authorization\AuthRequest; $authRequest = AuthRequest::fromParams([ 'client_id' => $client->getMetadata()->getClientId(), 'redirect_uri' => $client->getMetadata()->getRedirectUris()[0], 'request' => $requestObject, ]);
The library can handle aggregated and distributed claims:
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);
There are some middlewares and handles available:
This middleware should always be on top of middlewares chain to provide a session for state
and nonce
parameters.
To use it you should install the dflydev/fig-cookies
package:
$ 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 */);
The middleware provides a Facile\OpenIDClient\Session\AuthSessionInterface
attribute with an Facile\OpenIDClient\Session\AuthSessionInterface
stateful instance used to persist session data.
If you have another session storage, you can handle it and provide a Facile\OpenIDClient\Session\AuthSessionInterface
instance in the Facile\OpenIDClient\Session\AuthSessionInterface
attribute.
This middleware should always be on top of middlewares chain to provide the client to the other middlewares.
use Facile\OpenIDClient\Middleware\ClientProviderMiddleware; $client = $container->get('openid.clients.default'); $middleware = new ClientProviderMiddleware($client);
This middleware provide the auth request to use with the AuthRedirectHandler
.
use Facile\OpenIDClient\Middleware\AuthRequestProviderMiddleware; use Facile\OpenIDClient\Authorization\AuthRequest; $authRequest = AuthRequest::fromParams([ 'scope' => 'openid', // other params... ]); $middleware = new AuthRequestProviderMiddleware($authRequest);
This handler will redirect the user to the OpenID authorization page.
use Facile\OpenIDClient\Middleware\AuthRedirectHandler; use Facile\OpenIDClient\Service\AuthorizationService; /** @var AuthorizationService $authorizationService */ $authorizationService = $container->get(AuthorizationService::class); $middleware = new AuthRedirectHandler($authorizationService);
This middleware will handle the callback from the OpenID provider.
It will provide a Facile\OpenIDClient\Token\TokenSetInterface
attribute with the final TokenSet object.
use Facile\OpenIDClient\Middleware\CallbackMiddleware; use Facile\OpenIDClient\Service\AuthorizationService; /** @var AuthorizationService $authorizationService */ $authorizationService = $container->get(AuthorizationService::class); $middleware = new CallbackMiddleware($authorizationService);
This middleware will fetch user data from the userinfo endpoint and will provide an Facile\OpenIDClient\Middleware\UserInfoMiddleware
attribute with user infos as array.
use Facile\OpenIDClient\Middleware\UserInfoMiddleware; use Facile\OpenIDClient\Service\UserInfoService; /** @var UserInfoService $userInfoService */ $userInfoService = $container->get(UserInfoService::class); $middleware = new UserInfoMiddleware($userInfoService);
It's important to use a cache to avoid to fetch issuer configuration and keys on every request.
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');
If you need to use Psalm you can include the plugin in your psalm.xml
.
<plugins> <pluginClass class="Facile\JoseVerifier\Psalm\Plugin" /> </plugins>