Source of file OAuthServerController.php
Size: 9,317 Bytes - Last Modified: 2021-12-24T05:17:09+00:00
/var/www/docs.ssmods.com/process/src/code/OAuthServerController.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 | <?php /** * @author Ian Simpson <ian@iansimpson.nz> * @copyright Copyright (c) Ian Simpson */ namespace IanSimpson\OAuth2; use DateInterval; use Exception; use GuzzleHttp\Psr7\ServerRequest; use function GuzzleHttp\Psr7\stream_for; use IanSimpson\OAuth2\Entities\UserEntity; use IanSimpson\OAuth2\Repositories\AccessTokenRepository; use IanSimpson\OAuth2\Repositories\AuthCodeRepository; use IanSimpson\OAuth2\Repositories\ClientRepository; use IanSimpson\OAuth2\Repositories\RefreshTokenRepository; use IanSimpson\OAuth2\Repositories\ScopeRepository; use League\OAuth2\Server\AuthorizationServer; use League\OAuth2\Server\Exception\OAuthServerException; use League\OAuth2\Server\Grant\AuthCodeGrant; use League\OAuth2\Server\Grant\RefreshTokenGrant; use League\OAuth2\Server\ResourceServer; use Psr\Http\Message\ServerRequestInterface; use Robbie\Psr7\HttpRequestAdapter; use Robbie\Psr7\HttpResponseAdapter; use SilverStripe\Control\HTTPRequest; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Injector\Injector; use SilverStripe\Control\Controller; use SilverStripe\ORM\ValidationResult; use SilverStripe\Security\Member; use Silverstripe\Security\Security; class OauthServerController extends Controller { private static $privateKey = '../private.key'; private static $publicKey = '../public.key'; private static $encryptionKey = ''; private static $allowed_actions = [ 'authorize', 'accessToken', ]; private static $url_handlers = [ 'authorize' => 'authorize', 'access_token' => 'accessToken', ]; protected $server; protected $myRequest; protected $myResponse; private $myRequestAdapter; private $myResponseAdapter; private $myRepositories; /** * @var \Psr\Log\LoggerInterface */ protected $logger; public function __construct() { $privateKey = BASE_PATH . DIRECTORY_SEPARATOR . $this->config()->get('privateKey'); $this->myRepositories = [ 'client' => new ClientRepository(), 'scope' => new ScopeRepository(), 'accessToken' => new AccessTokenRepository(), 'authCode' => new AuthCodeRepository(), 'refreshToken' => new RefreshTokenRepository(), ]; $encryptionKey = $this->config()->get('encryptionKey'); if (empty($encryptionKey)) { throw new Exception('OauthServerController::encryptionKey must not be empty!'); } // Muting errors with @ to stop notice about key permissions $this->server = @new AuthorizationServer( $this->myRepositories['client'], $this->myRepositories['accessToken'], $this->myRepositories['scope'], $privateKey, $encryptionKey ); // Enable the authentication code grant on the server $grant = new AuthCodeGrant( $this->myRepositories['authCode'], $this->myRepositories['refreshToken'], new DateInterval('PT10M') // authorization codes will expire after 10 minutes ); $grant->setRefreshTokenTTL(new DateInterval('P1M')); // refresh tokens will expire after 1 month $this->server->enableGrantType( $grant, new DateInterval('PT1H') // access tokens will expire after 1 hour ); // Enable the refresh code grant on the server $grant = new RefreshTokenGrant( $this->myRepositories['refreshToken'] ); $grant->setRefreshTokenTTL(new DateInterval('P1M')); // new refresh tokens will expire after 1 month $this->server->enableGrantType( $grant, new DateInterval('PT1H') // new access tokens will expire after 1 hour ); $this->logger = Injector::inst()->get('IanSimpson\\OAuth2\\Logger'); parent::__construct(); } public function handleRequest(HTTPRequest $request) { $this->myRequestAdapter = new HttpRequestAdapter(); $this->myRequest = $this->myRequestAdapter->toPsr7($request); $this->myResponseAdapter = new HttpResponseAdapter(); $this->myResponse = $this->myResponseAdapter->toPsr7($this->getResponse()); return parent::handleRequest($request); } public function authorize() { try { // Validate the HTTP request and return an AuthorizationRequest object. $authRequest = $this->server->validateAuthorizationRequest($this->myRequest); $client = $authRequest->getClient(); $member = Security::getCurrentUser(); // The auth request object can be serialized and saved into a user's session. if (!$member || !$member->exists()) { // You will probably want to redirect the user at this point to a login endpoint. Security::singleton()->setSessionMessage( _t( 'OAuth.AUTHENTICATE_MESSAGE', 'Please log in to access {originatingSite}.', ['originatingSite' => $client->ClientName] ), ValidationResult::TYPE_GOOD ); return $this->redirect(Config::inst()->get(Security::class, 'login_url') . "?BackURL=" . urlencode($_SERVER['REQUEST_URI'])); } // Once the user has logged in set the user on the AuthorizationRequest $authRequest->setUser(new UserEntity()); // an instance of UserEntityInterface // At this point you should redirect the user to an authorization page. // This form will ask the user to approve the client and the scopes requested. // TODO Implement authorisation step. For now, authorize implicitly, this is fine if you don't use scopes, // and everything falls into one global bucket, e.g. when you have only one resource endpoint. // Once the user has approved or denied the client update the status // (true = approved, false = denied) $authRequest->setAuthorizationApproved(true); $this->logger->info(sprintf( '%s authorised %s (%s) to access scopes "%s" on their behalf', $member->Email, $client->ClientName, $client->ClientIdentifier, implode(', ', array_map(function($entity) { return $entity->ScopeIdentifier; }, $authRequest->getScopes())) )); // Return the HTTP redirect response $this->myResponse = $this->server->completeAuthorizationRequest($authRequest, $this->myResponse); } catch (OAuthServerException $exception) { // All instances of OAuthServerException can be formatted into a HTTP response $this->myResponse = $exception->generateHttpResponse($this->myResponse); } catch (Exception $exception) { $this->myResponse = $this->myResponse->withStatus(500)->withBody( stream_for($exception->getMessage()) ); } return $this->myResponseAdapter->fromPsr7($this->myResponse); } public function accessToken() { try { // Try to respond to the request $this->myResponse = $this->server->respondToAccessTokenRequest($this->myRequest, $this->myResponse); } catch (OAuthServerException $exception) { // All instances of OAuthServerException can be formatted into a HTTP response $this->myResponse = $exception->generateHttpResponse($this->myResponse); } catch (Exception $exception) { $this->myResponse = $this->myResponse->withStatus(500)->withBody( stream_for($exception->getMessage()) ); } return $this->myResponseAdapter->fromPsr7($this->myResponse); } /** * @param $controller * @return bool|ServerRequestInterface */ public static function authenticateRequest($controller) { $publicKey = BASE_PATH . DIRECTORY_SEPARATOR . Config::inst()->get(self::class, 'publicKey'); //Muting errors with @ to stop notice about key permissions $server = @new ResourceServer( new AccessTokenRepository(), $publicKey ); $request = ServerRequest::fromGlobals(); $auth = $request->getHeader('Authorization'); if ((!$auth || !sizeof($auth)) && $_SERVER['AUTHORIZATION']) { $request = $request->withAddedHeader('Authorization', $_SERVER['AUTHORIZATION']); } try { $request = $server->validateAuthenticatedRequest($request); } catch (Exception $exception) { return false; } return $request; } /** * @return bool|Member */ public static function getMember($controller) { $request = self::authenticateRequest($controller); if (!$request) { return false; } $members = Member::get()->filter([ "ID" => $request->getAttributes()['oauth_user_id'] ]); /** @var Member $member */ $member = $members->first(); return $member; } } |