'https://appcenter.intuit.com/connect/oauth2', 'tokenendpoint' => 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer', 'local' => 'https://sandbox-quickbooks.api.intuit.com', 'production' => 'https://quickbooks.api.intuit.com', ]; protected $scopes = ['com.intuit.quickbooks.accounting']; /* ABSTRACT */ protected function getAuthUrl($state) { return $this->buildAuthUrlFromBase( Arr::get(self::hosts,'authorise'), $state ); } protected function getUserByToken($token) { $response = []; $access = $this->getAccessTokenResponse($token); if ((! $x=Arr::get($access,'token_type')) || ($x !== 'bearer')) { $response['reject'] = TRUE; $response['reject_reason'] = 'No bearer token?'; return $response; } return $response ?: $access; } protected function getTokenUrl() { return Arr::get(self::hosts,'tokenendpoint'); } protected function mapUserToObject(array $user) { return (new SocialUser)->setRaw(Auth::user()->toArray()); } /* OVERRIDES */ public function user() { if ($this->hasInvalidState()) { throw new InvalidStateException; } if (! $this->getCode()) return NULL; $details = $this->getUserByToken($this->getCode()); if ((! $details) || Arr::get($details,'reject')) abort(403, sprintf('Authentication Failed with %s',Arr::get($details,'reject_reason','*no reason given*'))); $user = $this->mapUserToObject($details); $user->refresh_token_expires_in = Arr::get($details,'x_refresh_token_expires_in'); $user->realmid = $this->request->get('realmId'); return $user->setToken(Arr::get($details,'access_token')) ->setRefreshToken(Arr::get($details,'refresh_token')) ->setExpiresIn(Arr::get($details,'expires_in')); } /* METHODS */ public function getAuthorisationHeader(ProviderOauth $po): array { return [ 'Accept' => 'application/json', 'Authorization' => sprintf('Basic %s',base64_encode($this->clientId.':'.$this->clientSecret)), 'Content-Type' => 'application/x-www-form-urlencoded' ]; } public function getRefreshTokenFields(string $refreshtoken): array { return [ 'grant_type' => 'refresh_token', 'refresh_token' => $refreshtoken, ]; } public function refreshToken($to) { Log::debug(sprintf('%s:= Refreshing token for [%d]',self::LOGKEY,$to->id)); $response = $this ->getHttpClient() ->post($this->getTokenUrl(),[ RequestOptions::HEADERS => $this->getAuthorisationHeader($to->provider), RequestOptions::FORM_PARAMS => $this->getRefreshTokenFields($to->refresh_token), ]); switch ($response->getStatusCode()) { case '200': $body = json_decode($response->getBody(), true); if (Arr::get($body,'token_type') !== 'bearer') throw new InvalidAuthTokenException(sprintf('Invalid response [%d] didnt get a bearer token for [%s] (%s)',$response->getStatusCode(),$to->user->email,$body)); $to->access_token = Arr::get($body,'access_token'); $to->access_token_expires_at = Carbon::now()->addSeconds(Arr::get($body,'expires_in')); // If the refresh token changed if (($x=Arr::get($body,'refresh_token')) !== $to->refresh_token) { $to->refresh_token = $x; $to->refresh_token_expires_at = Carbon::now()->addSeconds(Arr::get($body,'x_refresh_token_expires_in')); Log::debug(sprintf('%s:= Refresh token updated.',self::LOGKEY)); } return $to->save(); default: throw new InvalidAuthTokenException(sprintf('Invalid response [%d] refreshing token for [%s] (%s)',$response->getStatusCode(),$to->user->email,$response->getBody())); } } }