Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+454
View File
@@ -0,0 +1,454 @@
<?php
/**
* @brief OAuth Server Authorize Endpoint
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 29 Apr 2017
*/
define('REPORT_EXCEPTIONS', TRUE);
require '../../init.php';
class oAuthServerAuthorizationRequest
{
/**
* @brief Client
*/
protected $client;
/**
* @brief Redirect URI to use
*/
protected $redirectUri;
/**
* @brief Redirect URI provided
*/
protected $providedRedirectUri;
/**
* @brief State
*/
protected $state;
/**
* @brief Response Type
*/
protected $responseType;
/**
* @brief Scope
*/
protected $scope = array();
/**
* Init
*
* @param string $clientId Client ID
* @param string $redirectUri Redirect URI, if provided
* @param string|NULL $state The state, if provided
* @return void
*/
public static function init( $clientId, $redirectUri = NULL, $state = NULL )
{
$obj = new static;
/* Get the client */
try
{
$obj->client = \IPS\Api\OAuthClient::load( $clientId );
if ( !$obj->client->enabled )
{
throw new \OutOfRangeException;
}
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Login\Handler\OAuth2\InitException('oauth_err_invalid_client');
}
/* Set the Redirect URI */
$allowedRedirectUris = json_decode( $obj->client->redirect_uris );
if ( $redirectUri )
{
if ( !in_array( $redirectUri, $allowedRedirectUris ) )
{
throw new \IPS\Login\Handler\OAuth2\InitException('oauth_err_invalid_redirect_uri');
}
else
{
$obj->redirectUri = \IPS\Http\Url::external( $redirectUri );
}
}
elseif ( count( $allowedRedirectUris ) === 1 )
{
$obj->redirectUri = \IPS\Http\Url::external( array_shift( $allowedRedirectUris ) );
}
else
{
throw new \IPS\Login\Handler\OAuth2\InitException('oauth_err_invalid_redirect_uri');
}
$obj->providedRedirectUri = $redirectUri;
/* Set the state, if appliable */
if ( $state )
{
$obj->state = $state;
}
return $obj;
}
/**
* Set the response type
*
* @param string $responseType The response type
* @return void
*/
public function setResponseType( $responseType )
{
if ( $responseType === 'code' )
{
if ( !in_array( 'authorization_code', explode( ',', $this->client->grant_types ) ) )
{
throw new \IPS\Login\Handler\OAuth2\Exception('unsupported_response_type');
}
}
elseif ( $responseType === 'token' )
{
if ( !in_array( 'implicit', explode( ',', $this->client->grant_types ) ) )
{
throw new \IPS\Login\Handler\OAuth2\Exception('unsupported_response_type');
}
}
else
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_request', "response_type parameter is required" );
}
$this->responseType = $responseType;
}
/**
* Set the requested scopes
*
* @param string $scope Scopes
* @return void
*/
public function setScope( $scope )
{
$availableScopes = json_decode( $this->client->scopes, TRUE );
$scopes = explode( ' ', $scope );
foreach ( $scopes as $requestedScope )
{
if ( !array_key_exists( $requestedScope, $availableScopes ) )
{
throw new \IPS\Login\Handler\OAuth2\Exception('invalid_scope');
}
}
$this->scope = $scopes;
}
/**
* Get URL to redirect the user back to the client after successful authorization
*
* @param \IPS\Member $member The member
* @param array $scopes The authorized scopes
* @return void
*/
public function authorized( \IPS\Member $member, $scopes )
{
$scopes = $this->client->choose_scopes ? $scopes : $this->scope;
if ( $this->responseType === 'code' )
{
do
{
$authorizationCode = \IPS\Login::generateRandomString( 64 );
}
while ( \IPS\Db::i()->select( 'COUNT(*)', 'core_oauth_server_authorization_codes', array( 'client_id=? AND code=?', $this->client->client_id, $authorizationCode ) )->first() );
\IPS\Db::i()->insert( 'core_oauth_server_authorization_codes', array(
'client_id' => $this->client->client_id,
'redirect_uri' => $this->providedRedirectUri ?: NULL,
'member_id' => $member->member_id,
'expires' => time() + 60,
'code' => $authorizationCode,
'scope' => $scopes ? json_encode( $scopes ) : NULL
) );
return $this->redirect( array( 'code' => $authorizationCode ) );
}
else
{
$accessToken = $this->client->generateAccessToken( $member, $scopes, 'implicit', TRUE );
$response = array( 'access_token' => $accessToken['access_token'], 'token_type' => 'bearer' );
if ( $accessToken['access_token_expires'] )
{
$response['expires_in'] = $accessToken['access_token_expires'] - time();
}
return $this->redirect( $response );
}
}
/**
* Get URL to redirect the user back to the client
*
* @param array $response Response parameters
* @return void
*/
public function redirect( array $response )
{
if ( $this->state )
{
$response['state'] = $this->state;
}
if ( $this->responseType === 'token' )
{
return $this->redirectUri->setFragment( http_build_query( $response ) );
}
else
{
return $this->redirectUri->setQueryString( $response );
}
}
/**
* Do we need to be prompted?
*
* @param string $requestedPromptType Requested prompt type, if provided
* @return bool|array
*/
public function promptRequired( $requestedPromptType )
{
/* If we're not logged in, we definitely do, unless we cancelled */
if ( !\IPS\Member::loggedIn()->member_id and ( !isset( \IPS\Request::i()->allow ) or \IPS\Request::i()->allow ) )
{
return TRUE;
}
/* Have we gone through it already? */
if ( isset( \IPS\Request::i()->allow ) and \IPS\Login::compareHashes( (string) \IPS\Session::i()->csrfKey, (string) \IPS\Request::i()->csrfKey ) )
{
if ( !\IPS\Request::i()->allow )
{
throw new \IPS\Login\Handler\OAuth2\Exception('access_denied');
}
return FALSE;
}
/* Does the client require it? */
if ( $this->client->prompt !== 'automatic' )
{
return TRUE;
}
/* Did we request it? */
if ( $requestedPromptType === 'login' or $requestedPromptType === 'reauthorize' )
{
return TRUE;
}
/* Do we already have an access token with these scopes? */
$accessToken = $this->client->getAccessToken( \IPS\Member::loggedIn(), $this->scope );
if ( $accessToken )
{
\IPS\Request::i()->grantedScope = array_combine( json_decode( $accessToken['scope'], TRUE ), array_fill( 0, count( json_decode( $accessToken['scope'], TRUE ) ), TRUE ) );
return FALSE;
}
/* No? We need a new token */
return TRUE;
}
/**
* Show authorization form
*
* @param string $requestedPromptType Requested prompt type, if provided
* @param bool $loggedIn Has the user logged in?
* @return void
*/
public function prompt( $requestedPromptType, $loggedIn )
{
/* We mustn't have the redirect_uri in the URL when displaying the page as this needs to be handled securely (it will
probably include a client-issued CSRF key) and if we use it in the URL, any third party scripts that may be being
used on the community (tracking or advertisements, for example) will have access to it - this also makes the URI
a bit cleaner */
if ( isset( \IPS\Request::i()->client_id ) )
{
\IPS\Db::i()->insert( 'core_oauth_authorize_prompts', array(
'session_id' => \IPS\Session::i()->id,
'client_id' => $this->client->client_id,
'response_type' => $this->responseType,
'redirect_uri' => $this->providedRedirectUri ?: NULL,
'scope' => implode( ' ', $this->scope ),
'state' => $this->state,
'timestamp' => time(),
'logged_in' => FALSE
), TRUE );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'oauth/authorize/', 'interface' ) );
}
/* Construct the URL for this page */
$url = \IPS\Http\Url::internal( 'oauth/authorize/', 'interface' );
/* Get the scope definitions */
$scopes = array();
$availableScopes = json_decode( $this->client->scopes, TRUE );
foreach ( $this->scope as $scope )
{
$scopes[ $scope ] = $availableScopes[ $scope ]['description'];
}
/* Do we need them to login? */
if ( !\IPS\Member::loggedIn()->member_id or ( ( $this->client->prompt === 'login' or $requestedPromptType === 'login' ) and !$loggedIn ) )
{
$login = new \IPS\Login( $url );
$member = NULL;
$error = NULL;
try
{
if ( $success = $login->authenticate() )
{
\IPS\Db::i()->update( 'core_oauth_authorize_prompts', array( 'logged_in' => TRUE ), array( 'session_id=?', \IPS\Session::i()->id ) );
if ( $success->mfa() )
{
$_SESSION['processing2FA'] = array( 'memberId' => $success->member->member_id, 'anonymous' => $success->anonymous, 'remember' => $success->rememberMe, 'destination' => (string) $url, 'handler' => $success->handler->id );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=system&controller=login", 'front', 'login' )->setQueryString( '_mfaLogin', 1 ) );
}
$success->process();
\IPS\Output::i()->redirect( $url );
}
}
catch ( \IPS\Login\Exception $e )
{
if ( $e->getCode() === \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT )
{
$e->member = $e->member->member_id;
$e->handler = $e->handler->id;
$_SESSION['linkAccounts'] = json_encode( $e );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=login&do=link', 'front', 'login' )->setQueryString( 'ref', base64_encode( $url ) ) );
}
$error = $e->getMessage();
}
if ( $member === NULL )
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'login', 'core', 'global' )->oauthLogin( $url, $this->client, $scopes, $login, $error );
\IPS\Dispatcher::i()->finish();
}
}
/* Still here? Show an authorization screen */
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'login', 'core', 'global' )->oauthAuthorize( $url, $this->client, $scopes );
\IPS\Dispatcher::i()->finish();
}
}
/* Init */
\IPS\Session\Front::i();
\IPS\Dispatcher\External::i();
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimal';
\IPS\Output::i()->bodyClasses[] = 'ipsLayout_minimalNoHome';
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'oauth_authorize', FALSE, array( 'sprintf' => array( \IPS\Settings::i()->board_name ) ) );
\IPS\Output::i()->httpHeaders['X-Frame-Options'] = 'DENY';
/* Check we are not IP banned */
$ipBanned = \IPS\Request::i()->ipAddressIsBanned();
if ( $ipBanned )
{
\IPS\Output::i()->showBanned();
}
/* Handle the OAuth request */
try
{
/* Get our params */
$loggedIn = FALSE;
if ( !isset( \IPS\Request::i()->client_id ) )
{
try
{
$row = \IPS\Db::i()->select( '*', 'core_oauth_authorize_prompts', array( 'session_id=?', \IPS\Session::i()->id ) )->first();
$clientId = $row['client_id'];
$responseType = $row['response_type'];
$redirectUri = $row['redirect_uri'];
$scope = $row['scope'];
$state = $row['state'];
$loggedIn = $row['logged_in'];
}
catch ( \UnderflowException $e )
{
throw new \IPS\Login\Handler\OAuth2\InitException('oauth_err_invalid_client');
}
}
else
{
$clientId = \IPS\Request::i()->client_id;
$responseType = \IPS\Request::i()->response_type;
$redirectUri = \IPS\Request::i()->redirect_uri;
$scope = \IPS\Request::i()->scope;
$state = \IPS\Request::i()->state;
}
/* Init, validating client_id and redirect_uri */
$request = oAuthServerAuthorizationRequest::init( $clientId, $redirectUri, $state );
/* If site is offline, return temporarily_unavailable */
if ( ( isset( \IPS\Settings::i()->setup_in_progress ) AND \IPS\Settings::i()->setup_in_progress ) or !\IPS\Settings::i()->site_online )
{
throw new \IPS\Login\Handler\OAuth2\Exception('temporarily_unavailable');
}
/* HTTPs only */
if ( \IPS\OAUTH_REQUIRES_HTTPS and !\IPS\Request::i()->isSecure() )
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_request', "request must be made with https" );
}
/* Set data */
$request->setResponseType( $responseType );
if ( $scope )
{
$request->setScope( $scope );
}
/* Do we need them to be prompted? */
$authorizedUrl = NULL;
if ( $request->promptRequired( \IPS\Request::i()->prompt ) )
{
$request->prompt( \IPS\Request::i()->prompt, $loggedIn );
}
/* Still here? Go ahead */
\IPS\Db::i()->delete( 'core_oauth_authorize_prompts', array( 'session_id=?', \IPS\Session::i()->id ) );
\IPS\Output::i()->redirect( $request->authorized( \IPS\Member::loggedIn(), isset( \IPS\Request::i()->grantedScope ) ? array_keys( \IPS\Request::i()->grantedScope ) : array() ), NULL, 302 );
}
catch ( \IPS\Login\Handler\OAuth2\InitException $e )
{
\IPS\Output::i()->error( $e->getMessage(), '3S361/1', 403 );
}
catch ( \IPS\Login\Handler\OAuth2\Exception $e )
{
$response = array( 'error' => $e->getMessage() );
if ( $e->description )
{
$response['error_description'] = $e->description;
}
\IPS\Output::i()->redirect( $request->redirect( $response ), NULL, 302 );
}
catch ( Exception $e )
{
\IPS\Output::i()->redirect( $request->redirect( array( 'error' => 'server_error', 'error_description' => $e->getMessage() ) ), NULL, 302 );
}
+76
View File
@@ -0,0 +1,76 @@
<?php
/**
* @brief OAuth Client Redirection Endpoint
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 31 May 2017
*/
define('REPORT_EXCEPTIONS', TRUE);
require '../../init.php';
\IPS\Session\Front::i();
if ( isset( \IPS\Request::i()->state ) and $explodedData = explode( '-', \IPS\Request::i()->state ) and count( $explodedData ) === 4 and $destination = @base64_decode( $explodedData[1] ) )
{
try
{
$destination = \IPS\Http\Url::createFromString( $destination )->setQueryString( array(
'_processLogin' => $explodedData[0],
'csrfKey' => $explodedData[2],
'ref' => $explodedData[3],
) );
if ( !( $destination instanceof \IPS\Http\Url\Internal ) )
{
throw new \Exception;
}
if ( isset( \IPS\Request::i()->error ) )
{
foreach ( array( 'error', 'error_description', 'error_uri' ) as $k )
{
if ( isset( \IPS\Request::i()->$k ) )
{
$destination = $destination->setQueryString( $k, \IPS\Request::i()->$k );
}
}
}
if ( isset( \IPS\Request::i()->access_token ) )
{
foreach ( array( 'access_token', 'token_type', 'expires_in', 'scope', 'state' ) as $k )
{
if ( isset( \IPS\Request::i()->$k ) )
{
$destination = $destination->setQueryString( $k, \IPS\Request::i()->$k );
}
}
}
elseif ( isset( \IPS\Request::i()->code ) )
{
$destination = $destination->setQueryString( 'code', \IPS\Request::i()->code );
}
\IPS\Output::i()->redirect( $destination );
exit;
}
catch ( \Exception $e ) {}
}
$url = (string) \IPS\Http\Url::internal( 'oauth/callback/', 'none' );
?><!DOCTYPE html>
<html>
<head>
<title><?php echo \IPS\Member::loggedIn()->language()->get( 'loading' ); ?></title>
<script>
if ( window.location.hash ) {
var hash = window.location.hash.substr( 0, 1 ) == '#' ? window.location.hash.substr( 1 ) : window.location.hash;
window.location = "<?php echo $url; ?>?" + hash;
}
</script>
</head>
<body>
<noscript><?php echo \IPS\Member::loggedIn()->language()->get( 'oauth_implicit_no_js' ); ?></noscript>
</body>
</html>
+357
View File
@@ -0,0 +1,357 @@
<?php
/**
* @brief OAuth Server Generate Access Token Endpoint
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 29 Apr 2017
*/
define('REPORT_EXCEPTIONS', TRUE);
require '../../init.php';
class oAuthServerTokenRequest
{
/**
* @brief Client
*/
public $client;
/**
* Init
*
* @param string $clientId Client ID
* @param string $clientSecret Client Secret
* @return void
*/
public static function init( $clientId, $clientSecret )
{
$obj = new static;
/* Get the client */
try
{
$obj->client = \IPS\Api\OAuthClient::load( $clientId );
if ( !$obj->client->enabled )
{
throw new \OutOfRangeException;
}
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_client' );
}
/* Validate the secret */
if ( $obj->client->client_secret )
{
$bruteForce = $obj->client->brute_force ? json_decode( $obj->client->brute_force, TRUE ) : array();
if ( isset( $bruteForce[ \IPS\Request::i()->ipAddress() ] ) and $bruteForce[ \IPS\Request::i()->ipAddress() ] >= 3 )
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_client', "blocked for too many authentication failures" );
}
if ( password_verify( $clientSecret, $obj->client->client_secret ) )
{
if ( isset( $bruteForce[ \IPS\Request::i()->ipAddress() ] ) )
{
unset( $bruteForce[ \IPS\Request::i()->ipAddress() ] );
$obj->client->brute_force = json_encode( $bruteForce );
$obj->client->save();
}
}
else
{
if ( !isset( $bruteForce[ \IPS\Request::i()->ipAddress() ] ) )
{
$bruteForce[ \IPS\Request::i()->ipAddress() ] = 0;
}
$bruteForce[ \IPS\Request::i()->ipAddress() ]++;
$obj->client->brute_force = json_encode( $bruteForce );
$obj->client->save();
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_client' );
}
}
return $obj;
}
/**
* Validate the grant type
*
* @param string $grantType The Authorization Code
* @return string
*/
public function grantType( $grantType )
{
if ( !in_array( $grantType, array( 'authorization_code', 'implicit', 'client_credentials', 'password', 'refresh_token' ) ) )
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'unsupported_grant_type' );
}
if ( $grantType === 'refresh_token' )
{
if ( !$this->client->use_refresh_tokens )
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'unsupported_grant_type' );
}
}
elseif ( !in_array( $grantType, explode( ',', $this->client->grant_types ) ) )
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'unauthorized_client' );
}
return $grantType;
}
/**
* Validate Authorization Code
*
* @param string $authorizationCode The Authorization Code
* @return array|NULL
*/
public function validateAuthorizationCode( $authorizationCode, $redirectUri = NULL )
{
try
{
$authorizationCode = \IPS\Db::i()->select( '*', 'core_oauth_server_authorization_codes', array( 'client_id=? AND code=?', $this->client->client_id, $authorizationCode ) )->first();
/* If it's expired, delete it and do not validate */
if ( $authorizationCode['expires'] < time() )
{
\IPS\Db::i()->delete( 'core_oauth_server_authorization_codes', array( 'client_id=? AND code=?', $authorizationCode['client_id'], $authorizationCode['code'] ) );
return;
}
/* If it's already been used, this should be treated as an attack: revoke any access tokens already generated and do not validate */
if ( $authorizationCode['used'] )
{
\IPS\Db::i()->delete( 'core_oauth_server_access_tokens', array( 'client_id=? AND authorization_code=?', $this->client->client_id, $authorizationCode['code'] ) );
return;
}
/* If the redirect URI does not match, do not validate */
if ( $redirectUri !== $authorizationCode['redirect_uri'] )
{
return;
}
/* Mark it used */
\IPS\Db::i()->update( 'core_oauth_server_authorization_codes', array( 'used' => 1 ), array( 'client_id=? AND code=?', $authorizationCode['client_id'], $authorizationCode['code'] ) );
/* Return access token */
return $this->client->generateAccessToken( \IPS\Member::load( $authorizationCode['member_id'] ), $authorizationCode['scope'] ? json_decode( $authorizationCode['scope'] ) : NULL, 'authorization_code', FALSE, $authorizationCode['code'] );
}
catch ( \UnderflowException $e )
{
return;
}
}
/**
* Validate Password
*
* @param string $username Username
* @param string $password Password
* @param array|null $scope Scopes
* @return array|NULL
*/
public function validatePassword( $username, $password, $scope )
{
$member = NULL;
$accessToken = NULL;
$fails = array();
$login = new \IPS\Login();
foreach ( $login->usernamePasswordMethods() as $method )
{
try
{
$member = $method->authenticateUsernamePassword( $login, $username, $password );
\IPS\Login::checkIfAccountIsLocked( $member, TRUE );
$accessToken = $this->client->generateAccessToken( $member, $scope, 'password' );
break;
}
catch ( \IPS\Login\Exception $e )
{
if ( $e->getCode() === \IPS\Login\Exception::BAD_PASSWORD and $e->member )
{
$fails[ $e->member->member_id ] = $e->member;
}
}
catch ( \Exception $e ) { }
}
foreach ( $fails as $failedMember )
{
if ( !$member or $failedMember->member_id != $failedMember->member_id )
{
$failedLogins = is_array( $failedMember->failed_logins ) ? $failedMember->failed_logins : array();
$failedLogins[ \IPS\Request::i()->ipAddress() ][] = time();
$failedMember->failed_logins = $failedLogins;
$failedMember->save();
}
}
return $accessToken;
}
/**
* Validate Client Credentials
*
* @param array|null $scope Scopes
* @return array|NULL
*/
public function validateClientCredentials( $scope )
{
if ( $this->client->client_secret )
{
return $this->client->generateAccessToken( NULL, $scope, 'client_credentials', TRUE );
}
}
/**
* Validate Refresh Token
*
* @param string $refreshToken The refresh token
* @param array|null $scope Scopes
* @return array|NULL
*/
public function validateRefreshToken( $refreshToken, $newScope )
{
$accessToken = $this->client->validateRefreshToken( $refreshToken );
if ( $accessToken )
{
$member = NULL;
if ( $accessToken['member_id'] )
{
$member = \IPS\Member::load( $accessToken['member_id'] );
if ( !$member->member_id )
{
return;
}
}
$originalScope = $accessToken['scope'] ? json_decode( $accessToken['scope'], TRUE ) : array();
$scope = $newScope ? array_intersect( $originalScope, $newScope ) : $originalScope;
return $this->client->generateAccessToken( $member, $scope, 'refresh_token', TRUE );
}
}
}
/* Handle it */
try
{
/* TLS only */
if ( \IPS\OAUTH_REQUIRES_HTTPS and !\IPS\Request::i()->isSecure() )
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_request', "request must be made with https" );
}
/* POST only */
if ( \IPS\Request::i()->requestMethod() !== 'POST' )
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_request', "request must be a POST request" );
}
/* Check we are not IP banned */
$ipBanned = \IPS\Request::i()->ipAddressIsBanned();
if ( $ipBanned )
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_client', "IP Address banned" );
}
/* Get the client id and secret */
$clientId = NULL;
$clientSecret = NULL;
if ( isset( $_POST['client_id'] ) )
{
$clientId = $_POST['client_id'];
$clientSecret = isset( $_POST['client_secret'] ) ? $_POST['client_secret'] : NULL;
}
elseif ( isset( $_SERVER['PHP_AUTH_USER'] ) )
{
$clientId = $_SERVER['PHP_AUTH_USER'];
$clientSecret = isset( $_SERVER['PHP_AUTH_PW'] ) ? $_SERVER['PHP_AUTH_PW'] : NULL;
}
else
{
foreach ( $_SERVER as $k => $v )
{
if ( mb_substr( $k, -18 ) == 'HTTP_AUTHORIZATION' )
{
$exploded = explode( ':', base64_decode( mb_substr( $v, 6 ) ) );
if ( isset( $exploded[0] ) and isset( $exploded[1] ) )
{
$clientId = $exploded[0];
$clientSecret = isset( $exploded[1] ) ? $exploded[1] : NULL;
}
}
}
}
/* Initiate request */
$request = oAuthServerTokenRequest::init( $clientId, $clientSecret );
/* Validate grant */
$accessToken = NULL;
switch ( $request->grantType( \IPS\Request::i()->grant_type ) )
{
case 'authorization_code':
$accessToken = $request->validateAuthorizationCode( \IPS\Request::i()->code, \IPS\Request::i()->redirect_uri );
break;
case 'password':
$accessToken = $request->validatePassword( \IPS\Request::i()->username, \IPS\Request::i()->password, isset( \IPS\Request::i()->scope ) ? explode( ' ', \IPS\Request::i()->scope ) : NULL );
break;
case 'client_credentials':
$accessToken = $request->validateClientCredentials( isset( \IPS\Request::i()->scope ) ? explode( ' ', \IPS\Request::i()->scope ) : NULL );
break;
case 'refresh_token':
$accessToken = $request->validateRefreshToken( \IPS\Request::i()->refresh_token, isset( \IPS\Request::i()->scope ) ? explode( ' ', \IPS\Request::i()->scope ) : NULL );
break;
}
/* Return */
if ( $accessToken )
{
$response = array( 'access_token' => $accessToken['access_token'], 'token_type' => 'bearer' );
if ( $accessToken['access_token_expires'] )
{
$response['expires_in'] = $accessToken['access_token_expires'] - time();
}
if ( $accessToken['refresh_token'] )
{
$response['refresh_token'] = $accessToken['refresh_token'];
}
if ( $accessToken['scope'] )
{
$response['scope'] = implode( ' ', json_decode( $accessToken['scope'], TRUE ) );
}
\IPS\Output::i()->sendOutput( json_encode( $response ), 200, 'application/json', array( 'Cache-Control' => 'no-store', 'Pragma' => 'no-cache' ), FALSE, FALSE, FALSE );
}
else
{
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_grant', 400 );
}
}
catch ( \IPS\Login\Handler\OAuth2\Exception $e )
{
$response = array( 'error' => $e->getMessage() );
if ( $e->description )
{
$response['error_description'] = $e->description;
}
\IPS\Output::i()->sendOutput( json_encode( $response ), $e->getMessage() === 'invalid_client' ? 401 : 400, 'application/json', array(), FALSE, FALSE, FALSE );
}
catch ( Exception $e )
{
\IPS\Output::i()->sendOutput( json_encode( array( 'error' => 'server_error', 'error_description' => $e->getMessage() ) ), 500, 'application/json', array(), FALSE, FALSE, FALSE );
}