Create ZF Client Authentication for Apigility Oauth with ApigilityConsumer
If you have Apigility as API builder in API side, and client app that consume it using Zend Framework 2/3 or ZF Expressive, you can create an authentication from the client application that call oauth in apigility side.
Zend\Authentication
has AbstractAdapter that you can extends to create custom adapter for its need. Let’s assume the applications are like the following diagram:
[CLIENT - A ZF Application] [API - An Apigility Application] | | AuthController ZF\MvcAuth\Authentication\OAuth2Adapter | | | authenticateAction() | | ------------------------------------> | | identity json | | <------------------------------------ |
On oauth result call, you may get the following result:
{ "access_token": "8e4b0e5ddc874a6f1500514ef530dbea3976ae77", "expires_in": 3600, "token_type": "Bearer", "scope": null, "refresh_token": "d19b79cd376924409c14ee46e5230617482fb169" }
The ApigilityConsumer
ApigilityConsumer is a ZF2/ZF3 Apigility Client module (can also be used in ZF Expressive) to consume Apigility API Services.
You can install by run composer command:
composer require samsonasik/apigility-consumer
For full configurations and features, you can read at its README, for this post’s need, you can do something like this:
<?php // config/autoload/apigility-consumer.local.php return [ 'apigility-consumer' => [ // your apigility host url 'api-host-url' => 'https://your.apigilty.api.host', // your apigility oauth setting 'oauth' => [ 'grant_type' => 'password', 'client_id' => 'your client id', 'client_secret' => 'your client secret', ], ], ];
and register the module into config/application.config.php
or config/modules.config.php
:
<?php // config/application.config.php or config/modules.config.php return [ 'ApigilityConsumer', // <-- register here 'Application', ],
Create Adapter
You need to extends Zend\Authentication\Adapter\AbstractAdapter
and implements Zend\Authentication\Adapter\AdapterInterface
. So, You can have the class:
<?php namespace Application\Adapter; use ApigilityConsumer\Service\ClientAuthService; use Zend\Authentication\Adapter\AbstractAdapter; use Zend\Authentication\Adapter\AdapterInterface; use Zend\Authentication\Result; class ApigilityAuthenticationAdapter extends AbstractAdapter implements AdapterInterface { /** * @var ClientAuthService */ private $clientAuthService; /** * @param ClientAuthService $clientAuthService */ public function __construct(ClientAuthService $clientAuthService) { $this->clientAuthService = $clientAuthService; } /** * @return Result */ public function authenticate() { $clientResult = $this->clientAuthService->callAPI( [ // your oauth registered route segment in apigility. 'api-route-segment' => '/oauth', 'form-data' => [ 'username' => $this->getIdentity(), 'password' => $this->getCredential(), ], 'form-request-method' => 'POST', ] ); if (! $clientResult->success) { return new Result(Result::FAILURE, null, $clientResult::$messages); } return new Result(RESULT::SUCCESS, $clientResult->data); } }
Your can now build a factory from it:
<?php namespace Application\Adapter; use ApigilityConsumer\Service\ClientAuthService; class ApigilityAuthenticationAdapterFactory { public function __invoke($container) { return new ApigilityAuthenticationAdapter( $container->get(ClientAuthService::class) ); } }
You can then register at service_manager
:
<?php // module/Application/config/module.config.php namespace Application; 'service_manager' => [ 'factories' => [ // ... Adapter\ApigilityAuthenticationAdapter::class => Adapter\ApigilityAuthenticationAdapterFactory::class, ], ],
For ZF Expressive, you can register under ‘dependencies’ key.
Set Adapter into AuthenticationService
You need to set authentication service’s adapter with defined adapter above with factory:
<?php namespace Application\Factory; use Application\Adapter\ApigilityAuthenticationAdapter; use Zend\Authentication\AuthenticationService; use Zend\Authentication\Storage\Session; class AuthenticationServiceFactory { public function __invoke($container) { $adapter = $container->get(ApigilityAuthenticationAdapter::class); return new AuthenticationService( new Session(), // or your own storage implementing Zend\Authentication\Storage\StorageInterface $adapter ); } }
You can then register also at service_manager
:
<?php // module/Application/config/module.config.php namespace Application; use Zend\Authentication\AuthenticationService; 'service_manager' => [ 'factories' => [ // ... AuthenticationService::class => Factory\AuthenticationServiceFactory::class, ], ],
For ZF Expressive, you can register under ‘dependencies’ key.
The AuthController::authenticate()
I assume that you already inject controler with login form, use “username” and “password” as field names, and fill the data, so, your AuthController::authenticate()
can be like the following:
<?php namespace Application\Controller; use Application\Form\LoginForm; use Zend\Authentication\AuthenticationService; class AuthController { public function __construct( AuthenticationService $authenticationService, LoginForm $loginForm, ) { /* ...*/ } public function authenticateAction() { /* * check request and form validity here */ $formData = $this->loginForm->getData(); $this->authenticationService->getAdapter() ->setIdentity($formData['username']) ->setCredential($formData['password']); $result = $this->authenticationService->authenticate(); if (!$result->isValid()) { /** * For security reason, you should not show user the reason of failure, * However, if it actually needed for specific purpose, you can pull by call: * * $result->getMessages(); * */ return $this->redirect()->toRoute('/auth'); } return $this->redirect()->toRoute('/account'); } }
For ZF Expressive, you can create routed Authentication middleware.
That’s it, you’re now have successfully created a client authentication for your ZF2/ZF3 or ZF Expressive application that consume Apigility oauth.
8 comments