diff --git a/Api/Data/ServiceInterface.php b/Api/Data/ServiceInterface.php
index 3e011b5..1b7ba41 100644
--- a/Api/Data/ServiceInterface.php
+++ b/Api/Data/ServiceInterface.php
@@ -16,7 +16,7 @@ interface ServiceInterface
const PORT = 'port';
const SECURE = 'secure';
const AUTH = 'auth';
- const TOKEN = 'token';
+ const TOKEN_ID = 'token_id';
const KEY = 'key';
const REMOVE = 'remove';
@@ -120,7 +120,7 @@ public function getAuth();
/**
* @return mixed
*/
- public function getToken();
+ public function getTokenId();
/**
* Get key
@@ -225,10 +225,10 @@ public function setSecure($secure);
public function setAuth($auth);
/**
- * @param string $token
+ * @param int $tokenId
* @return \Swissup\Email\Api\Data\ServiceInterface
*/
- public function setToken($token);
+ public function setTokenId($tokenId);
/**
* Set key
diff --git a/Controller/Adminhtml/Email/Service/Save.php b/Controller/Adminhtml/Email/Service/Save.php
index 162217d..444aa14 100644
--- a/Controller/Adminhtml/Email/Service/Save.php
+++ b/Controller/Adminhtml/Email/Service/Save.php
@@ -76,8 +76,8 @@ public function execute()
$this->session->setFormData(false);
if ($model->hasData('callback_url')) {
- $flowUrl = $model->getData('callback_url');
- return $resultRedirect->setUrl($flowUrl);
+ $callbackUrl = $model->getData('callback_url');
+ return $resultRedirect->setUrl($callbackUrl);
}
if ($this->getRequest()->getParam('back')) {
diff --git a/Controller/Gmail/GetOAuth2Token.php b/Controller/Gmail/GetOAuth2Token.php
deleted file mode 100644
index 5b4ae28..0000000
--- a/Controller/Gmail/GetOAuth2Token.php
+++ /dev/null
@@ -1,155 +0,0 @@
-session = $session;
- $this->serviceRepository = $serviceRepository;
- $this->urlDecoder = $urldecoder;
- $this->tokenValidator = $tokenValidator;
- }
-
- private function isLoggedIn()
- {
- $request = $this->getRequest();
- $serviceId = $request->getParam('id', false);
- $sessionIdKey = self::SESSION_ID_KEY;
-
- if (!empty($serviceId)) {
- if ($this->tokenValidator->validateRequest($request)) {
- $this->session->setData($sessionIdKey, $serviceId);
- } else {
- $this->session->setData($sessionIdKey, null);
- }
- }
-
- return (bool) $this->session->getData($sessionIdKey);
- }
-
- private function getServiceId()
- {
- $sessionIdKey = self::SESSION_ID_KEY;
- return (int) $this->session->getData($sessionIdKey);
- }
-
- private function redirectReferer()
- {
- $refererUrl = $this->session->getData('referer');
- $refererUrl = empty($refererUrl) ? $this->_redirect->getRedirectUrl() : $refererUrl;
- return $this->_redirect($refererUrl);
- }
-
- /**
- * Post user question
- *
- * @inherit
- */
- public function execute()
- {
- if (!$this->isLoggedIn()) {
- return $this->redirectReferer();
- }
-
- $id = $this->getServiceId();
- $service = $this->serviceRepository->getById($id);
-
- $request = $this->getRequest();
- $refererUrl = $request->getParam('referer');
- $refererUrl = $this->urlDecoder->decode($refererUrl);
- if (!empty($refererUrl)) {
- $this->session->setData('referer', $refererUrl);
- }
-
- /* @var \League\OAuth2\Client\Provider\Google $provider */
- $provider = new \League\OAuth2\Client\Provider\Google([
- 'clientId' => $service->getUser(),
- 'clientSecret' => $service->getPassword(),
- 'redirectUri' => $this->_url->getUrl('*/*/*'),
-// 'hostedDomain' => 'example.com', // optional; used to restrict access to users on your G Suite/Google Apps for Business accounts
- 'scopes' => ['https://mail.google.com/'],
- 'accessType' => 'offline'
- ]);
-
- $errorParam = $request->getParam('error');
- $codeParam = $request->getParam('code');
- $stateParam = $request->getParam('state');
-
- if (!empty($errorParam)) {
- $this->messageManager->addErrorMessage(
- htmlspecialchars($errorParam, ENT_QUOTES, 'UTF-8')
- );
- return $this->redirectReferer();
- } elseif (empty($codeParam)) {
-
- // If we don't have an authorization code then get one
- $authUrl = $provider->getAuthorizationUrl(/*['scope' => ['https://mail.google.com/']]*/);
- $this->session->setData(self::FLOW_STATE_KEY, $provider->getState());
-
- $resultRedirect = $this->resultRedirectFactory->create();
- $resultRedirect->setUrl($authUrl);
-
- return $resultRedirect;
- // Check given state against previously stored one to mitigate CSRF attack
- } elseif (empty($stateParam) || ($stateParam !== $this->session->getData(self::FLOW_STATE_KEY))) {
- $this->session->setData(self::FLOW_STATE_KEY, null);
- $this->messageManager->addErrorMessage(
- __('Invalid state')
- );
- return $this->redirectReferer();
- } else {
- // Try to get an access token (using the authorization code grant)
- $token = $provider->getAccessToken('authorization_code', [
- 'code' => $codeParam
- ]);
-
- $refreshToken = $token->getRefreshToken();
- if (empty($refreshToken)) {
- $authUrl = $provider->getAuthorizationUrl(['prompt' => 'consent', 'access_type' => 'offline']);
- $this->session->setData(self::FLOW_STATE_KEY, $provider->getState());
-
- $resultRedirect = $this->resultRedirectFactory->create();
- $resultRedirect->setUrl($authUrl);
- return $resultRedirect;
- }
- $this->session->setData(self::FLOW_STATE_KEY, null);
-
- $tokenOptions = array_merge($token->jsonSerialize(), ['refresh_token' => $refreshToken]);
- $service->setToken($tokenOptions);
- $this->serviceRepository->save($service);
- }
-
- return $this->redirectReferer();
- }
-}
diff --git a/Mail/Transport/GmailOAuth2.php b/Mail/Transport/GmailOAuth2.php
index d4ac96e..3702268 100644
--- a/Mail/Transport/GmailOAuth2.php
+++ b/Mail/Transport/GmailOAuth2.php
@@ -24,8 +24,8 @@ public function __construct(
SmtpOptions $options = null
) {
if (! $options instanceof SmtpOptions) {
- $host = 'smtp.gmail.com';
- $port = 587;
+ $host = isset($config['host']) ? $config['host'] : 'smtp.gmail.com';
+ $port = isset($config['port']) ? (int) $config['port'] : 587;
if (!isset($config['token']) || !isset($config['token']['access_token'])) {
$phrase = new \Magento\Framework\Phrase('token is broken');
diff --git a/Model/Service.php b/Model/Service.php
index e93fdae..4040708 100644
--- a/Model/Service.php
+++ b/Model/Service.php
@@ -161,11 +161,11 @@ public function getAuth()
}
/**
- * @return string
+ * @return int
*/
- public function getToken()
+ public function getTokenId()
{
- return $this->getData(self::TOKEN);
+ return $this->getData(self::TOKEN_ID);
}
/**
@@ -309,9 +309,9 @@ public function setAuth($auth)
return $this->setData(self::AUTH, $auth);
}
- public function setToken($token)
+ public function setTokenId($tokenId)
{
- return $this->setData(self::TOKEN, $token);
+ return $this->setData(self::TOKEN_ID, $tokenId);
}
/**
diff --git a/Model/Service/Encryptor.php b/Model/Service/Encryptor.php
index 46c7db7..ad30a20 100644
--- a/Model/Service/Encryptor.php
+++ b/Model/Service/Encryptor.php
@@ -13,18 +13,12 @@ class Encryptor implements ServiceEncryptorInterface
*/
private $encryptor;
- /**
- * @var SerializerInterface
- */
- private $serializer;
-
/**
* @param EncryptorInterface $encryptor
*/
- public function __construct(EncryptorInterface $encryptor, SerializerInterface $serializer)
+ public function __construct(EncryptorInterface $encryptor)
{
$this->encryptor = $encryptor;
- $this->serializer = $serializer;
}
/**
@@ -32,24 +26,7 @@ public function __construct(EncryptorInterface $encryptor, SerializerInterface $
*/
private function getAttributes()
{
- return ['password', 'token'];
- }
-
- /**
- * @return string[]
- */
- private function getSerializableAttributes()
- {
- return ['token'];
- }
-
- /**
- * @param $attribute
- * @return bool
- */
- private function isSerializableAttribute($attribute)
- {
- return in_array($attribute, $this->getSerializableAttributes());
+ return ['password'];
}
/**
@@ -60,9 +37,6 @@ public function encrypt(ServiceInterface $object): void
{
foreach ($this->getAttributes() as $attributeCode) {
$value = $object->getData($attributeCode);
- if ($this->isSerializableAttribute($attributeCode)) {
- $value = $this->serializer->serialize($value);
- }
if ($value) {
$object->setData($attributeCode, $this->encryptor->encrypt($value));
}
@@ -80,9 +54,6 @@ public function decrypt(ServiceInterface $object): void
if ($value) {
try {
$value = $this->encryptor->decrypt($value);
- if ($this->isSerializableAttribute($attributeCode)) {
- $value = $this->serializer->unserialize($value);
- }
$object->setData($attributeCode, $value);
} catch (\Exception $e) {
// value is not encrypted or something wrong with encrypted data
diff --git a/Plugin/Model/ServiceOAuth2TokenPlugin.php b/Plugin/Model/ServiceOAuth2TokenPlugin.php
index 99001b9..72031ba 100644
--- a/Plugin/Model/ServiceOAuth2TokenPlugin.php
+++ b/Plugin/Model/ServiceOAuth2TokenPlugin.php
@@ -3,8 +3,8 @@
namespace Swissup\Email\Plugin\Model;
-use Magento\Framework\DataObject;
use Swissup\Email\Model\Service;
+use Swissup\OAuth2Client\Model\AccessTokenRepository;
class ServiceOAuth2TokenPlugin
{
@@ -15,19 +15,9 @@ class ServiceOAuth2TokenPlugin
private $serviceRepository;
/**
- * @var \Magento\Framework\Url
+ * @var AccessTokenRepository
*/
- private $urlBuilder;
-
- /**
- * @var \Magento\Framework\Url\EncoderInterface
- */
- private $urlEncoder;
-
- /**
- * @var \Swissup\OAuth2Client\Model\Data\FlowToken
- */
- private $flowToken;
+ private $accessTokenRepository;
/**
* @var \Psr\Log\LoggerInterface $logger
@@ -36,81 +26,78 @@ class ServiceOAuth2TokenPlugin
public function __construct(
\Swissup\Email\Model\ServiceRepository $serviceRepository,
- \Magento\Framework\Url $urlBuilder,
- \Magento\Framework\Url\EncoderInterface $urlEncoder,
- \Swissup\OAuth2Client\Model\Data\FlowToken $flowToken,
+ \Swissup\OAuth2Client\Model\AccessTokenRepository $accessTokenRepository,
\Psr\Log\LoggerInterface $logger
) {
$this->serviceRepository = $serviceRepository;
- $this->urlBuilder = $urlBuilder;
- $this->urlEncoder = $urlEncoder;
- $this->flowToken = $flowToken;
+ $this->accessTokenRepository = $accessTokenRepository;
$this->logger = $logger;
}
+ private function isGoogleOAuth2(Service $service): bool
+ {
+ return $service->getAuth() === Service::AUTH_TYPE_XOAUTH2 && $service->getType() === Service::TYPE_GMAILOAUTH2;
+ }
+
public function afterAfterCommitCallback(Service $subject): void
{
- if ($subject->getAuth() !== Service::AUTH_TYPE_XOAUTH2 ||
- $subject->getType() !== Service::TYPE_GMAILOAUTH2) {
+ if (!$this->isGoogleOAuth2($subject)) {
return;
}
+ $tokenId = $subject->getTokenId();
+ /** @var $accessToken \Swissup\OAuth2Client\Model\AccessToken */
+ $accessToken = $this->accessTokenRepository->getById($tokenId);
- $tokenOptions = $subject->getToken();
- if (empty($tokenOptions)) {
- $urlBuilder = $this->urlBuilder;
- $refererUrl = $urlBuilder->getCurrentUrl();
- $refererUrl = $this->urlEncoder->encode($refererUrl);
- $callbackUrl = $urlBuilder->getUrl(
- 'swissup_email/gmail/getOAuth2Token',
- [
- '_nosid' => true,
- '_query' => [
- 'id' => $subject->getId(),
- 'referer' => $refererUrl,
- 'token' => $this->flowToken->getToken(),
- ]
- ]
- );
+ if (!$accessToken->isInitialized()) {
+ $callbackUrl = $accessToken->getCallbackUrl();
$subject->setData('callback_url', $callbackUrl);
}
}
public function afterAfterLoad(Service $subject): void
{
- if ($subject->getAuth() !== Service::AUTH_TYPE_XOAUTH2 ||
- $subject->getType() !== Service::TYPE_GMAILOAUTH2) {
+ if (!$this->isGoogleOAuth2($subject)) {
return;
}
- $tokenOptions = $subject->getToken();
- if (empty($tokenOptions)) {
- return;
+ // create new access token record
+ $tokenId = $subject->getTokenId();
+ if (empty($tokenId)) {
+ /** @var \Swissup\OAuth2Client\Model\AccessToken $accessToken */
+ $accessToken = $this->accessTokenRepository->create();
+ $accessToken->setHasDataChanges(true);
+ $accessToken = $this->accessTokenRepository->save($accessToken);
+ $tokenId = $accessToken->getId();
+
+ $subject->setTokenId($tokenId);
+ $this->serviceRepository->save($subject);
}
- $storedToken = new \League\OAuth2\Client\Token\AccessToken($tokenOptions);
- $refreshToken = $storedToken->getRefreshToken();
- if ($storedToken->hasExpired() && !empty($refreshToken)) {
- $urlBuilder = $this->urlBuilder;
- $redirectUri = $urlBuilder->getUrl('swissup_email/gmail/getOAuth2Token');
- /* @var \League\OAuth2\Client\Provider\Google $provider */
- $provider = new \League\OAuth2\Client\Provider\Google([
- 'clientId' => $subject->getUser(),
- 'clientSecret' => $subject->getPassword(),
- 'redirectUri' => $redirectUri,
-// 'hostedDomain' => 'example.com', // optional; used to restrict access to users on your G Suite/Google Apps for Business accounts
- 'scopes' => ['https://mail.google.com/'],
- 'accessType' => 'offline'
- ]);
- try {
- $token = $provider->getAccessToken('refresh_token', [
- 'refresh_token' => $refreshToken
- ]);
- $tokenOptions = array_merge($token->jsonSerialize(), ['refresh_token' => $refreshToken]);
- $subject->setToken($tokenOptions);
- $this->serviceRepository->save($subject);
- } catch (\Exception $e) {
- $this->logger->critical($e->getMessage());
- }
+ // refresh creadential
+ $tokenId = $subject->getTokenId();
+ /** @var \Swissup\OAuth2Client\Model\AccessToken $accessToken */
+ $accessToken = $this->accessTokenRepository->getById($tokenId);
+ $storedCredentialHash = $accessToken->getCredentialHash();
+ $clientId = $subject->getUser();
+ $clientSecret = $subject->getPassword();
+ $scope = implode(' ', ['https://mail.google.com/']);
+ $credential = $accessToken->getCredential();
+ $credential->setClientId($clientId)
+ ->setClientSecret($clientSecret)
+ ->setScope($scope);
+ $hash = $credential->getHash();
+ if ($storedCredentialHash !== $hash || $credential->isExpired()) {
+ $credential->save();
+ $accessToken->setCredentialHash($hash);
+ $this->accessTokenRepository->save($accessToken);
}
+
+ // refresh access_token is expired
+ $accessToken = $accessToken->runRefreshToken();
+ if ($accessToken) {
+ $this->accessTokenRepository->save($accessToken);
+ }
+
+ $subject->setData('token', $accessToken->getData());
}
}
diff --git a/README.md b/README.md
index 0a96149..2534f6c 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ If `Type` selects `Gmail`. Use an [App Password](https://security.google.com/set
>
> - [Transition from less secure apps to OAuth](https://support.google.com/a/answer/14114704?hl=en)
-If the `Type` field is set to `Gmail OAuth 2.0`, please follow the [Google instructions](https://developers.google.com/identity/openid-connect/openid-connect#registeringyourapp) to create the required credentials. In your credentials, you need to add `Authorized redirect URIs` with at least one URI, such as `https://localhost/swissup_email/gmail/getOAuth2Token/` (replace `localhost` with your Magento store URL).
+If the `Type` field is set to `Gmail OAuth 2.0`, please follow the [Google instructions](https://developers.google.com/identity/openid-connect/openid-connect#registeringyourapp) to create the required credentials. In your credentials, you need to add `Authorized redirect URIs` with at least one URI, such as `https://localhost/swissup_oauth2client/google/getToken/` (replace `localhost` with your Magento store URL).
![Gmail OAuth2 Credential](https://github.com/swissup/module-email/assets/412612/47802486-2725-4642-91e2-8ff8ead58389)
###### Customize the User Consent Screen
diff --git a/composer.json b/composer.json
index 264ab13..250ed36 100644
--- a/composer.json
+++ b/composer.json
@@ -22,7 +22,7 @@
"magento/module-config": "^101.1",
"laminas/laminas-mail": "^2.9.0",
"laminas/laminas-mime": "^2.5.0",
- "swissup/module-oauth2-client": "*"
+ "swissup/module-oauth2-client": "^1.0"
},
"require-dev": {
"slm/mail": "~1.5",
diff --git a/etc/db_schema.xml b/etc/db_schema.xml
index 88269c9..e7f9e74 100644
--- a/etc/db_schema.xml
+++ b/etc/db_schema.xml
@@ -12,7 +12,6 @@
-
diff --git a/etc/frontend/routes.xml b/etc/frontend/routes.xml
deleted file mode 100644
index e12f1c6..0000000
--- a/etc/frontend/routes.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/etc/module.xml b/etc/module.xml
index 55520b0..f39f469 100644
--- a/etc/module.xml
+++ b/etc/module.xml
@@ -1,6 +1,6 @@
-
+