diff --git a/src/Components/DfOAuthTwoOboProvider.php b/src/Components/DfOAuthTwoOboProvider.php new file mode 100644 index 0000000..4d8b140 --- /dev/null +++ b/src/Components/DfOAuthTwoOboProvider.php @@ -0,0 +1,157 @@ +usesState()) { + $state = Str::random(40); + $this->state = $state; + \Cache::put($state, $state, 180); + } + + return new RedirectResponse($this->getAuthUrl($state)); + } + + /** + * Returns state identifier. + * + * @return null|string + */ + public function getState() + { + return $this->state; + } + + /** + * {@inheritdoc} + */ + protected function hasInvalidState() + { + if ($this->isStateless()) { + return false; + } + $urlState = $this->request->input('state'); + $cacheState = \Cache::pull($urlState); + + return !(strlen($cacheState) > 0 && $urlState === $cacheState); + } + + /** + * {@inheritdoc} + */ + public function user() + { + if ($this->hasInvalidState()) { + throw new InvalidStateException(); + } + $token_a_response = $this->getAccessTokenResponse($this->getCode()); + + $tokenA = $this->parseAccessToken($token_a_response); + if(empty($tokenA)) { + throw new \InvalidArgumentException('Recieved invalid access_token from initial OAuth oauth2/v2.0/token request.'); + }else{ + // exchange OAuth access_token "Token A" for OBO access_token "Token B" + // Need separate access_token for each Microsoft Entra API resource + $graph_token_response = $this->getGraphTokenResponse($tokenA); + $user = $this->createUserFromGraphTokenResponse($graph_token_response); + + $database_token_response = $this->getDatabaseTokenResponse($tokenA); + return $this->setUserAccessTokenFromResponse($user, $database_token_response); + } + } + + /** + * Retrieve user using token response. + * + * @param $response + * + * @return $this + */ + public function createUserFromGraphTokenResponse($response) + { + return $this->mapUserToObject($this->getUserByToken($this->parseAccessToken($response))); + } + + /** + * Retrieve user using token response. + * + * @param $response + * + * @return $this + */ + public function setUserAccessTokenFromResponse($user, $response) + { + $token = $this->parseAccessToken($response); + + $this->credentialsResponseBody = $response; + + if ($user instanceof User) { + $user->setAccessTokenResponseBody($this->credentialsResponseBody); + } + + return $user->setToken($token) + ->setRefreshToken($this->parseRefreshToken($response)) + ->setExpiresIn($this->parseExpiresIn($response)); + } + + /** + * {@inheritdoc} + */ + protected function parseAccessToken($body) + { + return Arr::get($body, 'access_token'); + } + + /** + * {@inheritdoc} + */ + protected function parseRefreshToken($body) + { + return Arr::get($body, 'refresh_token'); + } + + /** + * {@inheritdoc} + */ + protected function parseExpiresIn($body) + { + return Arr::get($body, 'expires_in'); + } +} \ No newline at end of file diff --git a/src/Services/BaseOAuthService.php b/src/Services/BaseOAuthService.php index 54c0fb7..ba70253 100644 --- a/src/Services/BaseOAuthService.php +++ b/src/Services/BaseOAuthService.php @@ -6,6 +6,7 @@ use DreamFactory\Core\Models\User; use DreamFactory\Core\OAuth\Components\DfOAuthOneProvider; use DreamFactory\Core\OAuth\Components\DfOAuthTwoProvider; +use DreamFactory\Core\OAuth\Components\DfOAuthTwoOboProvider; use DreamFactory\Core\OAuth\Models\OAuthTokenMap; use DreamFactory\Core\Services\BaseRestService; use DreamFactory\Core\Utility\Session; @@ -109,9 +110,10 @@ public function handleLogin($request) /** @var RedirectResponse $response */ $response = $this->provider->redirect(); $traitsUsed = class_uses($this->provider); + $traitTwoObo = DfOAuthTwoOboProvider::class; $traitTwo = DfOAuthTwoProvider::class; $traitOne = DfOAuthOneProvider::class; - if (isset($traitsUsed[$traitTwo])) { + if (isset($traitsUsed[$traitTwo]) || isset($traitsUsed[$traitTwoObo])) { $state = $this->provider->getState(); if (!empty($state)) { $key = static::CACHE_KEY_PREFIX . $state;