Skip to content

Added client credentials grant + implemented token revocation #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/**
* @package thinkingmik/api-proxy-laravel
* @author Michele Andreoli <michi.andreoli[at]gmail.com>
* @copyright Copyright (c) Michele Andreoli
* @license http://mit-license.org/
* @link https://github.com/thinkingmik/api-proxy-laravel
*/

namespace ThinKingMik\ApiProxy\Exceptions;

/**
* Exception class
*/
class ResponseParseErrorException extends ProxyException {

public function __construct() {
$this->httpStatusCode = 500;
$this->errorType = 'proxy_response_parse_error';
parent::__construct(\Lang::get('api-proxy-laravel::messages.proxy_response_parse_error'));
}

}
85 changes: 82 additions & 3 deletions src/ThinKingMik/ApiProxy/Managers/RequestManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
use ThinKingMik\ApiProxy\Models\MixResponse;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Exception\ParseException;
use ThinKingMik\ApiProxy\Exceptions\MissingClientSecretException;
use ThinKingMik\ApiProxy\Exceptions\ResponseParseErrorException;

class RequestManager {

Expand Down Expand Up @@ -51,6 +54,9 @@ public function executeRequest($inputs, $parsedCookie) {
case ProxyAux::MODE_TOKEN:
$mixed = $this->execRefresh($inputs, $parsedCookie);
break;
case ProxyAux::MODE_REVOKE:
$mixed = $this->execRevoke($inputs, $parsedCookie);
break;
default:
$proxyResponse = $this->replicateRequest($this->method, $this->uri, $inputs);
$mixed = new MixResponse($proxyResponse, null);
Expand All @@ -71,6 +77,9 @@ private function execAccess(Array $inputs) {
if ($proxyResponse->getStatusCode() === 200) {
$clientId = (array_key_exists(ProxyAux::CLIENT_ID, $inputs)) ? $inputs[ProxyAux::CLIENT_ID] : null;
$content = $proxyResponse->getContent();
if (!is_array($content)) {
throw new ResponseParseErrorException();
}
$content = ProxyAux::addQueryValue($content, ProxyAux::COOKIE_URI, $this->uri);
$content = ProxyAux::addQueryValue($content, ProxyAux::COOKIE_METHOD, $this->method);
$content = ProxyAux::addQueryValue($content, ProxyAux::CLIENT_ID, $clientId);
Expand Down Expand Up @@ -108,6 +117,37 @@ private function execRefresh(Array $inputs, $parsedCookie) {
return $mixed;
}

/**
* @param array $inputs
* @param $parsedCookie
* @return MixResponse
*/
private function execRevoke(Array $inputs, $parsedCookie) {
$inputs = $this->addTokenExtraParams($inputs, $parsedCookie);
$inputs = $this->addRevokeExtraParams($inputs, $parsedCookie);
$proxyResponse = $this->replicateRequest($this->method, $this->uri, $inputs);

$cookie = null;
$mixed = new MixResponse($proxyResponse, $cookie);

if ($proxyResponse->getStatusCode() === 200) {
if (array_key_exists(ProxyAux::REVOKE_TOKEN_TYPE_HINT, $inputs) && $inputs[ProxyAux::REVOKE_TOKEN_TYPE_HINT] == ProxyAux::REFRESH_TOKEN) {
// Replace cookie without refresh token
if (isset($parsedCookie[ProxyAux::REFRESH_TOKEN])) {
unset($parsedCookie[ProxyAux::REFRESH_TOKEN]);
}
$cookie = $this->cookieManager->createCookie($parsedCookie);
$mixed->setCookie($cookie);
} else if (!array_key_exists(ProxyAux::REVOKE_TOKEN_TYPE_HINT, $inputs) || $inputs[ProxyAux::REVOKE_TOKEN_TYPE_HINT] == ProxyAux::ACCESS_TOKEN) {
// Destroy cookie
$cookie = $this->cookieManager->destroyCookie();
$mixed->setCookie($cookie);
}
}

return $mixed;
}

/**
* @param $inputs
* @param $parsedCookie
Expand Down Expand Up @@ -150,7 +190,12 @@ private function tryRefreshToken($inputs, $parsedCookie) {
*/
private function replicateRequest($method, $uri, $inputs) {
$guzzleResponse = $this->sendGuzzleRequest($method, $uri, $inputs);
$proxyResponse = new ProxyResponse($guzzleResponse->getStatusCode(), $guzzleResponse->getReasonPhrase(), $guzzleResponse->getProtocolVersion(), $this->getResponseContent($guzzleResponse));
$proxyResponse = new ProxyResponse(
$guzzleResponse->getStatusCode(),
$guzzleResponse->getReasonPhrase(),
$guzzleResponse->getProtocolVersion(),
$this->getResponseContent($guzzleResponse)
);

return $proxyResponse;
}
Expand All @@ -162,10 +207,22 @@ private function replicateRequest($method, $uri, $inputs) {
private function getResponseContent($response) {
switch ($response->getHeader('content-type')) {
case 'application/json':
return $response->json();
try {
$responseContent = $response->json();
}
catch (ParseException $ex) {
throw new ResponseParseErrorException();
}
return $responseContent;
case 'text/xml':
case 'application/xml':
return $response->xml();
try {
$responseContent = $response->xml();
}
catch (ParseException $ex) {
throw new ResponseParseErrorException();
}
return $responseContent;
default:
return $response->getBody();
}
Expand Down Expand Up @@ -202,6 +259,9 @@ private function sendGuzzleRequest($method, $uriVal, $inputs) {
catch (ClientException $ex) {
$response = $ex->getResponse();
}
catch (ServerException $ex) {
$response = $ex->getResponse();
}

return $response;
}
Expand Down Expand Up @@ -283,6 +343,25 @@ private function addRefreshExtraParams($inputs, $parsedCookie) {
return $inputs;
}

/**
* @param $inputs
* @param $parsedCookie
* @return array
*/
private function addRevokeExtraParams($inputs, $parsedCookie) {
if (array_key_exists(ProxyAux::REVOKE_TOKEN_TYPE_HINT, $inputs) && $inputs[ProxyAux::REVOKE_TOKEN_TYPE_HINT] == ProxyAux::REFRESH_TOKEN) {
if (isset($parsedCookie[ProxyAux::REFRESH_TOKEN])) {
$inputs = ProxyAux::addQueryValue($inputs, ProxyAux::REVOKE_TOKEN, $parsedCookie[ProxyAux::REFRESH_TOKEN]);
}
} else if (!array_key_exists(ProxyAux::REVOKE_TOKEN_TYPE_HINT, $inputs) || $inputs[ProxyAux::REVOKE_TOKEN_TYPE_HINT] == ProxyAux::ACCESS_TOKEN) {
if (isset($parsedCookie[ProxyAux::ACCESS_TOKEN])) {
$inputs = ProxyAux::addQueryValue($inputs, ProxyAux::REVOKE_TOKEN, $parsedCookie[ProxyAux::ACCESS_TOKEN]);
}
}

return $inputs;
}

/**
* @param $inputs
* @return array
Expand Down
9 changes: 8 additions & 1 deletion src/ThinKingMik/ApiProxy/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Proxy {
private $callMode = null;
private $uriParam = null;
private $skipParam = null;
private $revokeParam = null;
private $redirectUri = null;
private $clientSecrets = null;
private $cookieManager = null;
Expand All @@ -33,6 +34,7 @@ class Proxy {
public function __construct($params) {
$this->uriParam = $params['uri_param'];
$this->skipParam = $params['skip_param'];
$this->revokeParam = $params['revoke_param'];
$this->redirectUri = $params['redirect_login'];
$this->clientSecrets = $params['client_secrets'];
$this->useHeader = $params['use_header'];
Expand All @@ -57,6 +59,7 @@ public function makeRequest($method, Array $inputs) {
//Remove parameters from inputs
$inputs = ProxyAux::removeQueryValue($inputs, $this->uriParam);
$inputs = ProxyAux::removeQueryValue($inputs, $this->skipParam);
$inputs = ProxyAux::removeQueryValue($inputs, $this->revokeParam);

//Read the cookie if exists
$parsedCookie = null;
Expand Down Expand Up @@ -99,16 +102,20 @@ private function checkMandatoriesInputParams(Array $inputs) {
private function getRequestMode($inputs) {
$grantType = ProxyAux::getQueryValue($inputs, ProxyAux::GRANT_TYPE);
$skip = ProxyAux::getQueryValue($inputs, $this->skipParam);
$revoke = ProxyAux::getQueryValue($inputs, $this->revokeParam);
$mode = ProxyAux::MODE_TOKEN;

if (isset($grantType)) {
if ($grantType === ProxyAux::PASSWORD_GRANT) {
if ($grantType === ProxyAux::PASSWORD_GRANT || $grantType === ProxyAux::CLIENT_CREDENTIALS_GRANT) {
$mode = ProxyAux::MODE_LOGIN;
}
}
else if (isset($skip) && strtolower($skip) === 'true') {
$mode = ProxyAux::MODE_SKIP;
}
else if (isset($revoke) && strtolower($revoke) === 'true') {
$mode = ProxyAux::MODE_REVOKE;
}

return $mode;
}
Expand Down
4 changes: 4 additions & 0 deletions src/ThinKingMik/ApiProxy/ProxyAux.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ class ProxyAux {
const COOKIE_URI = 'uri';
const COOKIE_METHOD = 'method';
const PASSWORD_GRANT = 'password';
const CLIENT_CREDENTIALS_GRANT = 'client_credentials';
const HEADER_AUTH = "Authorization";
const MODE_SKIP = '0';
const MODE_LOGIN = '1';
const MODE_TOKEN = '2';
const MODE_REFRESH = '3';
const MODE_REVOKE = '4';
const REVOKE_TOKEN = 'token';
const REVOKE_TOKEN_TYPE_HINT = 'token_type_hint';

/**
* @param $array
Expand Down
11 changes: 11 additions & 0 deletions src/config/proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@
*/
'skip_param' => 'skip',

/*
|--------------------------------------------------------------------------
| Proxy input: define the revoke attribute
|--------------------------------------------------------------------------
|
| When you call the proxy helper with this attribute set as true, this will be
| the last call after which the cookie will be destroyed.
|
*/
'revoke_param' => 'revoke',

/*
|--------------------------------------------------------------------------
| Set redirect URI
Expand Down
3 changes: 2 additions & 1 deletion src/lang/en/messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
'proxy_missing_param' => 'Missing mandatory parameter <b>:param</b> in the request call',
'missing_client_secret' => 'Missing secret key for client id <b>:client</b>',
'proxy_cookie_expired' => 'Cookie expired or not found. Return to the login form.',
'proxy_cookie_invalid' => 'Cookie format not valid. Missing attribute <b>:param</b>.'
'proxy_cookie_invalid' => 'Cookie format not valid. Missing attribute <b>:param</b>.',
'proxy_response_parse_error' => 'Response to proxy was malformed.',
);
3 changes: 2 additions & 1 deletion src/lang/it/messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
'proxy_missing_param' => 'Manca il parametro obbligatorio <b>:param</b> nella richiesta',
'missing_client_secret' => 'Manca la chiave segreta per il client <b>:client</b>',
'proxy_cookie_expired' => 'Cookie scaduto o non trovato. Ritorna al form di login.',
'proxy_cookie_invalid' => 'Formato del cookie non valido. Manca l\'attributo <b>:param</b>.'
'proxy_cookie_invalid' => 'Formato del cookie non valido. Manca l\'attributo <b>:param</b>.',
'proxy_response_parse_error' => 'Risposta a proxy era valido.',
);