381 lines
11 KiB
PHP
381 lines
11 KiB
PHP
<?php
|
|
|
|
use NSL\Persistent\Persistent;
|
|
|
|
require_once NSL_PATH . '/includes/auth.php';
|
|
|
|
|
|
abstract class NextendSocialOauth2 extends NextendSocialAuth {
|
|
|
|
const CSRF_LENGTH = 32;
|
|
|
|
protected $state = false;
|
|
|
|
protected $client_id;
|
|
protected $client_secret;
|
|
protected $redirect_uri;
|
|
|
|
protected $endpointAuthorization;
|
|
protected $endpointAccessToken;
|
|
protected $endpointRestAPI;
|
|
|
|
protected $defaultRestParams = array();
|
|
|
|
protected $scopes = array();
|
|
|
|
public function checkError() {
|
|
if (isset($_REQUEST['error']) && isset($_REQUEST['error_description'])) {
|
|
if ($this->validateState()) {
|
|
throw new NSLSanitizedRequestErrorMessageException($_REQUEST['error'] . ': ' . htmlspecialchars_decode($_REQUEST['error_description']));
|
|
}
|
|
}
|
|
}
|
|
|
|
public function getTestUrl() {
|
|
return $this->endpointAccessToken;
|
|
}
|
|
|
|
public function hasAuthenticateData() {
|
|
return isset($_REQUEST['code']);
|
|
}
|
|
|
|
/**
|
|
* @param string $client_id
|
|
*/
|
|
public function setClientId($client_id) {
|
|
$this->client_id = $client_id;
|
|
}
|
|
|
|
/**
|
|
* @param string $client_secret
|
|
*/
|
|
public function setClientSecret($client_secret) {
|
|
$this->client_secret = $client_secret;
|
|
}
|
|
|
|
/**
|
|
* @param string $redirect_uri
|
|
*/
|
|
public function setRedirectUri($redirect_uri) {
|
|
$this->redirect_uri = $redirect_uri;
|
|
}
|
|
|
|
public function getEndpointAuthorization() {
|
|
return $this->endpointAuthorization;
|
|
}
|
|
|
|
/*
|
|
* Adds response_type, client_id, redirect_uri and state as query parameter in the Authorization Url.
|
|
* client_id can be found in the App when you create one
|
|
* redirect_uri is the url you wish to be redirected after you entered you login credentials
|
|
* state is a randomly generated string
|
|
*/
|
|
public function createAuthUrl() {
|
|
|
|
$args = array(
|
|
'response_type' => 'code',
|
|
'client_id' => urlencode($this->client_id),
|
|
'redirect_uri' => urlencode($this->redirect_uri),
|
|
'state' => urlencode($this->getState())
|
|
);
|
|
|
|
$scopes = apply_filters('nsl_' . $this->providerID . '_scopes', $this->scopes);
|
|
if (count($scopes)) {
|
|
$args['scope'] = urlencode($this->formatScopes($scopes));
|
|
}
|
|
|
|
$args = apply_filters('nsl_' . $this->providerID . '_auth_url_args', $args);
|
|
|
|
return add_query_arg($args, $this->getEndpointAuthorization());
|
|
}
|
|
|
|
/**
|
|
* @param $scopes
|
|
* Connects an array of scopes with whitespace.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function formatScopes($scopes) {
|
|
return implode(' ', array_unique($scopes));
|
|
}
|
|
|
|
/**
|
|
* @return bool|false|string
|
|
* If the code that was sent by the selected provider and the state is valid,
|
|
* we can make a request for an accessToken with wp_remote_post().
|
|
* The result contains HTTP headers and content.
|
|
*
|
|
* Returns the accessToken with which we can make certain requests for their user profile data.
|
|
* @throws Exception
|
|
*/
|
|
public function authenticate() {
|
|
|
|
if (isset($_GET['code'])) {
|
|
if (!$this->validateState()) {
|
|
throw new Exception('Unable to validate CSRF state');
|
|
}
|
|
|
|
$http_args = array(
|
|
'timeout' => 15,
|
|
'user-agent' => 'WordPress',
|
|
'body' => array(
|
|
'grant_type' => 'authorization_code',
|
|
'code' => $_GET['code'],
|
|
'redirect_uri' => $this->redirect_uri,
|
|
'client_id' => $this->client_id,
|
|
'client_secret' => $this->client_secret
|
|
)
|
|
);
|
|
|
|
$request = wp_remote_post($this->endpointAccessToken, $this->extendAuthenticateHttpArgs($this->extendAllHttpArgs($http_args)));
|
|
|
|
if (is_wp_error($request)) {
|
|
|
|
throw new NSLSanitizedRequestErrorMessageException($request->get_error_message());
|
|
} else if (wp_remote_retrieve_response_code($request) !== 200) {
|
|
|
|
$this->errorFromResponse(json_decode(wp_remote_retrieve_body($request), true));
|
|
}
|
|
|
|
$accessTokenData = json_decode(wp_remote_retrieve_body($request), true);
|
|
|
|
if (!is_array($accessTokenData)) {
|
|
throw new NSLSanitizedRequestErrorMessageException(sprintf(__('Unexpected response: %s', 'nextend-facebook-connect'), wp_remote_retrieve_body($request)));
|
|
}
|
|
|
|
$accessTokenData = $this->extendAccessTokenData($accessTokenData);
|
|
|
|
$accessTokenData['created'] = time();
|
|
|
|
$this->access_token_data = $accessTokenData;
|
|
|
|
return wp_json_encode($accessTokenData);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param $response
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
protected function errorFromResponse($response) {
|
|
if (isset($response['error'])) {
|
|
throw new NSLSanitizedRequestErrorMessageException($response['error'] . ': ' . $response['error_description']);
|
|
}
|
|
}
|
|
|
|
public function deleteLoginPersistentData() {
|
|
Persistent::delete($this->providerID . '_state');
|
|
}
|
|
|
|
/**
|
|
* If the stored state is the same as the state we have received from the remote Provider, it is valid.
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function validateState() {
|
|
$this->state = Persistent::get($this->providerID . '_state');
|
|
if ($this->state === false) {
|
|
return false;
|
|
}
|
|
|
|
if (empty($_GET['state'])) {
|
|
return false;
|
|
}
|
|
|
|
if ($_GET['state'] == $this->state) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the stored state for the current provider.
|
|
*
|
|
* @return bool|mixed|null|string
|
|
*/
|
|
protected function getState() {
|
|
$this->state = Persistent::get($this->providerID . '_state');
|
|
if ($this->state === null) {
|
|
$this->state = $this->generateRandomState();
|
|
|
|
Persistent::set($this->providerID . '_state', $this->state);
|
|
}
|
|
|
|
return $this->state;
|
|
}
|
|
|
|
/**
|
|
* Generates a random string, which will be needed for the remote provider.
|
|
* It will be stored for a time.
|
|
*
|
|
* @return bool|string
|
|
*/
|
|
protected function generateRandomState() {
|
|
|
|
if (function_exists('random_bytes')) {
|
|
return $this->bytesToString(random_bytes(self::CSRF_LENGTH));
|
|
}
|
|
|
|
if (function_exists('mcrypt_create_iv')) {
|
|
/** @noinspection PhpDeprecationInspection */
|
|
$binaryString = mcrypt_create_iv(self::CSRF_LENGTH, MCRYPT_DEV_URANDOM);
|
|
|
|
if ($binaryString !== false) {
|
|
return $this->bytesToString($binaryString);
|
|
}
|
|
}
|
|
|
|
if (function_exists('openssl_random_pseudo_bytes')) {
|
|
$wasCryptographicallyStrong = false;
|
|
|
|
$binaryString = openssl_random_pseudo_bytes(self::CSRF_LENGTH, $wasCryptographicallyStrong);
|
|
|
|
if ($binaryString !== false && $wasCryptographicallyStrong === true) {
|
|
return $this->bytesToString($binaryString);
|
|
}
|
|
}
|
|
|
|
return $this->randomStr(self::CSRF_LENGTH);
|
|
}
|
|
|
|
private function bytesToString($binaryString) {
|
|
return substr(bin2hex($binaryString), 0, self::CSRF_LENGTH);
|
|
}
|
|
|
|
private function randomStr($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
|
|
$str = '';
|
|
$max = strlen($keyspace) - 1;
|
|
for ($i = 0; $i < $length; ++$i) {
|
|
$str .= $keyspace[random_int(0, $max)];
|
|
}
|
|
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* @param $path
|
|
* @param array $data
|
|
* @param $endpoint
|
|
*
|
|
* @return array
|
|
* @throws Exception
|
|
*/
|
|
public function get($path, $data = array(), $endpoint = false) {
|
|
|
|
$http_args = array(
|
|
'timeout' => 15,
|
|
'user-agent' => 'WordPress',
|
|
'body' => array_merge($this->defaultRestParams, $data)
|
|
);
|
|
if (!$endpoint) {
|
|
$endpoint = $this->endpointRestAPI;
|
|
}
|
|
$request = wp_remote_get($endpoint . $path, $this->extendHttpArgs($this->extendAllHttpArgs($http_args)));
|
|
|
|
if (is_wp_error($request)) {
|
|
|
|
throw new NSLSanitizedRequestErrorMessageException($request->get_error_message());
|
|
} else if (wp_remote_retrieve_response_code($request) !== 200) {
|
|
|
|
$this->errorFromResponse(json_decode(wp_remote_retrieve_body($request), true));
|
|
}
|
|
|
|
$result = json_decode(wp_remote_retrieve_body($request), true);
|
|
|
|
if (!is_array($result)) {
|
|
throw new NSLSanitizedRequestErrorMessageException(sprintf(__('Unexpected response: %s', 'nextend-facebook-connect'), wp_remote_retrieve_body($request)));
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param $path
|
|
* @param array $data
|
|
* @param $endpoint
|
|
*
|
|
* @return array
|
|
* @throws Exception
|
|
*/
|
|
public function post($path, $data = array(), $endpoint = false) {
|
|
|
|
$http_args = array(
|
|
'timeout' => 15,
|
|
'user-agent' => 'WordPress',
|
|
'body' => array_merge($this->defaultRestParams, $data)
|
|
);
|
|
if (!$endpoint) {
|
|
$endpoint = $this->endpointRestAPI;
|
|
}
|
|
|
|
|
|
$request = wp_remote_post($endpoint . $path, $this->extendHttpArgs($this->extendAllHttpArgs($http_args)));
|
|
|
|
if (is_wp_error($request)) {
|
|
|
|
throw new NSLSanitizedRequestErrorMessageException($request->get_error_message());
|
|
} else if (wp_remote_retrieve_response_code($request) !== 200) {
|
|
$this->errorFromResponse(json_decode(wp_remote_retrieve_body($request), true));
|
|
}
|
|
|
|
$result = json_decode(wp_remote_retrieve_body($request), true);
|
|
|
|
if (!is_array($result)) {
|
|
throw new NSLSanitizedRequestErrorMessageException(sprintf(__('Unexpected response: %s', 'nextend-facebook-connect'), wp_remote_retrieve_body($request)));
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param $http_args
|
|
* Puts additional data into the http header.
|
|
* Used for getting access to the resources with a bearer token.
|
|
*
|
|
* @return mixed
|
|
*/
|
|
protected function extendHttpArgs($http_args) {
|
|
if (isset($this->access_token_data['access_token'])) {
|
|
$http_args['headers'] = array(
|
|
'Authorization' => 'Bearer ' . $this->access_token_data['access_token']
|
|
);
|
|
}
|
|
|
|
return $http_args;
|
|
}
|
|
|
|
protected function extendAllHttpArgs($http_args) {
|
|
|
|
return $http_args;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $http_args
|
|
*
|
|
* Can be used for adding additional data into the authentication request arguments only.
|
|
*
|
|
* @return mixed
|
|
*/
|
|
protected function extendAuthenticateHttpArgs($http_args) {
|
|
|
|
return $http_args;
|
|
}
|
|
|
|
/**
|
|
* @param $access_token_data
|
|
*
|
|
* Can be used for adding additional data into the access_token_data array or modifying its structure
|
|
* if the format is wrong.
|
|
*
|
|
* @return mixed
|
|
*/
|
|
protected function extendAccessTokenData($access_token_data) {
|
|
|
|
return $access_token_data;
|
|
}
|
|
} |