Skip to content

Commit 564a431

Browse files
committed
initial commit
0 parents  commit 564a431

File tree

7 files changed

+308
-0
lines changed

7 files changed

+308
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
vendor/
2+
.php_cs.cache
3+
.idea
4+
composer.lock

.php_cs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
$config = new \HF\CS\Config();
4+
$config->getFinder()->in(__DIR__)->exclude(['data', 'docs', 'etc', 'templates']);
5+
6+
$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;
7+
8+
$config->setCacheFile($cacheDir . '/.php_cs.cache');
9+
10+
return $config;

README.md

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# PLHW OAuth2 Client Provider
2+
3+
OAuth2 Client provides means to retrieve and cache an access token to be used whilst communicating with our API.
4+
5+
Currently this library only supports the `ClientCredentialGrant` OAuth2 methodology.
6+
7+
8+
## Installation
9+
10+
```bash
11+
composer require plhw/hf-oauth2-client:^0.1
12+
```
13+
14+
## Usage
15+
16+
Your application needs to authenticate all requests to our api with a Bearer access token. This is done by adding a Authorization header.
17+
18+
There are many ways to do this, so we'll leave that part to you (for now)
19+
20+
Getting a token : see the example directory
21+
22+
see api [documentation](https://api.plhw.nl/docs) for end points.
23+
24+
## ClientCredentialsGrant
25+
26+
For machine to machine communication OAuth2 ClientCredentialGrant is appropiate.
27+
You must obtain the following information from us.
28+
29+
1. client ID
30+
2. client secret
31+
3. scope
32+
33+
Your client side application should communicate via a proxy to our server.
34+
35+
```
36+
37+
38+
[webbrowser] <----- internet -----> [application] <----- internet -----> [api.plhw.nl]
39+
40+
1. request data -> request -> do we have valid access token?
41+
42+
2. NO, get access token -> request -> id,secret,scope
43+
44+
3. validate request
45+
46+
4. store token in cache <- response <- access token
47+
48+
5. YES, request & token. -> request -> validate token
49+
50+
6. data <- response <- data
51+
52+
7. data <- response <- data
53+
54+
```

composer.json

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "plhw/hf-oauth2-client",
3+
"description": "Provides means to obtain token from our OAuth2 server",
4+
"type": "library",
5+
"license": "proprietary",
6+
"authors": [
7+
{
8+
"name": "Bas Kamer",
9+
"email": "[email protected]"
10+
}
11+
],
12+
"config": {
13+
"optimize-autoloader": true,
14+
"sort-packages": true
15+
},
16+
"require": {
17+
"php": "^5.6 || ^7.0",
18+
"league/oauth2-client": "^2.2",
19+
"roave/security-advisories": "dev-master",
20+
"zendframework/zend-cache": "^2.7",
21+
"zendframework/zend-serializer": "^2.8"
22+
},
23+
"require-dev": {
24+
"plhw/hf-cs-fixer-config": "^1.0"
25+
},
26+
"autoload": {
27+
"psr-4": {
28+
"HF\\OAuth2\\Client\\": "src/"
29+
}
30+
},
31+
"autoload-dev": {
32+
"psr-4": {
33+
"HFTest\\OAuth2\\Client\\": "test/src/"
34+
}
35+
}
36+
}

data/cache/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore

example/get-client-credentials.php

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
declare(strict_types = 1);
5+
6+
$autoloadFiles = [
7+
__DIR__ . '/../../../vendor/autoload.php',
8+
__DIR__ . '/../../vendor/autoload.php',
9+
__DIR__ . '/../vendor/autoload.php',
10+
__DIR__ . '/vendor/autoload.php',
11+
];
12+
13+
chdir(__DIR__ . '/..');
14+
15+
foreach ($autoloadFiles as $autoloadFile) {
16+
if (file_exists($autoloadFile)) {
17+
require_once $autoloadFile;
18+
}
19+
}
20+
21+
use HF\OAuth2\Client\Provider\PLHWProvider;
22+
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
23+
use League\OAuth2\Client\Token\AccessToken;
24+
use Zend\Cache\StorageFactory;
25+
26+
$env = 'development';
27+
$clientId = 'get from us';
28+
$clientSecret = 'get from us';
29+
$scope = 'get_from_us';
30+
31+
$provider = new PLHWProvider([
32+
'clientId' => $clientId,
33+
'clientSecret' => $clientSecret,
34+
], [], $env);
35+
36+
// setup a cache
37+
$cache = StorageFactory::factory([
38+
'adapter' => [
39+
'name' => 'filesystem',
40+
'options' => [
41+
'cache_dir' => './data/cache',
42+
],
43+
],
44+
'plugins' => ['serializer'],
45+
]);
46+
47+
$cacheKey = sha1('plhw-oauth2-token' . $scope);
48+
49+
/** @var AccessToken $accessToken */
50+
try {
51+
// try to get a token from the cache
52+
$accessToken = $cache->getItem($cacheKey, $success);
53+
54+
if ($accessToken === null || ! $success || $accessToken->hasExpired()) {
55+
// try to get a new access token
56+
$accessToken = $provider->getAccessToken('client_credentials', [
57+
'scope' => $scope,
58+
]);
59+
60+
$cache->setItem($cacheKey, $accessToken);
61+
}
62+
} catch (IdentityProviderException $e) {
63+
print_r($e->getResponseBody());
64+
65+
exit(1);
66+
}
67+
68+
print $accessToken->getToken() . PHP_EOL;
69+
print_r($accessToken->getValues()) . PHP_EOL;
70+
71+
return $accessToken;

src/Provider/PLHWProvider.php

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace HF\OAuth2\Client\Provider;
6+
7+
use League\OAuth2\Client\Provider\AbstractProvider;
8+
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
9+
use League\OAuth2\Client\Provider\GenericResourceOwner;
10+
use League\OAuth2\Client\Token\AccessToken;
11+
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
12+
use Psr\Http\Message\ResponseInterface;
13+
14+
class PLHWProvider extends AbstractProvider
15+
{
16+
use BearerAuthorizationTrait;
17+
18+
private $env = 'production';
19+
20+
/**
21+
* @var array|null
22+
*/
23+
private $defaultScopes = null;
24+
25+
/**
26+
* @var string
27+
*/
28+
private $scopeSeparator = ' ';
29+
30+
/**
31+
* @var string
32+
*/
33+
private $responseError = 'error';
34+
35+
/**
36+
* @var string
37+
*/
38+
private $responseCode;
39+
40+
/**
41+
* @var string
42+
*/
43+
private $responseResourceOwnerId = 'user_id';
44+
45+
public function __construct(array $options = [], array $collaborators = [], $env = 'production')
46+
{
47+
$this->env = $env;
48+
49+
if (! in_array($this->env, ['production', 'testing', 'development'])) {
50+
throw new \InvalidArgumentException(sprintf("Not a valid environment '%s' given", $this->env));
51+
}
52+
53+
parent::__construct($options, $collaborators);
54+
}
55+
56+
/**
57+
* Get authorization url to begin OAuth flow
58+
*
59+
* @return string
60+
*/
61+
public function getBaseAuthorizationUrl()
62+
{
63+
return sprintf('https://api%s.plhw.nl/oauth2/authorize', $this->env === 'production' ? '' : '-' . $this->env);
64+
}
65+
66+
/**
67+
* Get access token url to retrieve token
68+
*
69+
* @return string
70+
*/
71+
public function getBaseAccessTokenUrl(array $params)
72+
{
73+
return sprintf('https://api%s.plhw.nl/oauth2/token', $this->env === 'production' ? '' : '-' . $this->env);
74+
}
75+
76+
/**
77+
* Get provider url to fetch user details
78+
*
79+
* @param AccessToken $token
80+
*
81+
* @return string
82+
*/
83+
public function getResourceOwnerDetailsUrl(AccessToken $token)
84+
{
85+
return sprintf('https://api%s.plhw.nl/identity/me', $this->env === 'production' ? '' : '-' . $this->env);
86+
}
87+
88+
/**
89+
* {@inheritdoc}
90+
*/
91+
protected function checkResponse(ResponseInterface $response, $data)
92+
{
93+
if (! empty($data[$this->responseError])) {
94+
$error = $data[$this->responseError];
95+
$code = $this->responseCode ? $data[$this->responseCode] : 0;
96+
throw new IdentityProviderException($error, $code, $data);
97+
}
98+
}
99+
100+
/**
101+
* {@inheritdoc}
102+
*/
103+
protected function createResourceOwner(array $response, AccessToken $token)
104+
{
105+
return new GenericResourceOwner($response, $this->responseResourceOwnerId);
106+
}
107+
108+
/**
109+
* Returns the default scopes used by this provider.
110+
*
111+
* This should only be the scopes that are required to request the details
112+
* of the resource owner, rather than all the available scopes.
113+
*
114+
* @return array|null
115+
*/
116+
protected function getDefaultScopes()
117+
{
118+
return $this->defaultScopes;
119+
}
120+
121+
/**
122+
* Returns the string that should be used to separate scopes when building
123+
* the URL for requesting an access token.
124+
*
125+
* @return string Scope separator, defaults to ','
126+
*/
127+
protected function getScopeSeparator()
128+
{
129+
return $this->scopeSeparator;
130+
}
131+
}

0 commit comments

Comments
 (0)