192 lines
7.9 KiB
C++
192 lines
7.9 KiB
C++
#include "homeconnect.h"
|
||
#include "extern-plugininfo.h"
|
||
|
||
#include <QJsonDocument>
|
||
#include <QJsonObject>
|
||
#include <QJsonArray>
|
||
#include <QUrlQuery>
|
||
|
||
HomeConnect::HomeConnect(NetworkAccessManager *networkmanager, const QByteArray &clientKey, const QByteArray &clientSecret, QObject *parent) :
|
||
QObject(parent),
|
||
m_clientKey(clientKey),
|
||
m_clientSecret(clientSecret),
|
||
m_networkManager(networkmanager)
|
||
{
|
||
if(!m_tokenRefreshTimer) {
|
||
m_tokenRefreshTimer = new QTimer(this);
|
||
m_tokenRefreshTimer->setSingleShot(true);
|
||
connect(m_tokenRefreshTimer, &QTimer::timeout, this, &HomeConnect::onRefreshTimeout);
|
||
}
|
||
}
|
||
|
||
QUrl HomeConnect::getLoginUrl(const QUrl &redirectUrl, const QString &scope)
|
||
{
|
||
if (m_clientKey.isEmpty()) {
|
||
qWarning(dcHomeConnect) << "Client key not defined!";
|
||
return QUrl("");
|
||
}
|
||
|
||
if (redirectUrl.isEmpty()){
|
||
qWarning(dcHomeConnect) << "No redirect uri defined!";
|
||
}
|
||
m_redirectUri = QUrl::toPercentEncoding(redirectUrl.toString());
|
||
|
||
QUrl url(m_baseAuthorizationUrl);
|
||
QUrlQuery queryParams;
|
||
queryParams.addQueryItem("client_id", m_clientKey);
|
||
queryParams.addQueryItem("redirect_uri", m_redirectUri);
|
||
queryParams.addQueryItem("response_type", "code");
|
||
queryParams.addQueryItem("scope", "TODO");
|
||
queryParams.addQueryItem("state", QUuid::createUuid().toString());
|
||
queryParams.addQueryItem("nonce", QUuid::createUuid().toString());
|
||
//queryParams.addQueryItem("code_challenge", QUuid::createUuid().toString());
|
||
//queryParams.addQueryItem("code_challenge_method", QUuid::createUuid().toString());
|
||
url.setQuery(queryParams);
|
||
|
||
return url;
|
||
}
|
||
|
||
void HomeConnect::onRefreshTimeout()
|
||
{
|
||
qCDebug(dcHomeConnect) << "Refresh authentication token";
|
||
getAccessTokenFromRefreshToken(m_refreshToken);
|
||
}
|
||
|
||
void HomeConnect::checkStatusCode(int status, const QByteArray &payload)
|
||
{
|
||
QJsonDocument jsonDoc = QJsonDocument::fromJson(payload);
|
||
|
||
switch (status){
|
||
case 400:
|
||
if(!jsonDoc.toVariant().toMap().contains("error")) {
|
||
if(jsonDoc.toVariant().toMap().value("error").toString() == "invalid_client") {
|
||
qWarning(dcHomeConnect()) << "Client token provided doesn’t correspond to client that generated auth code.";
|
||
}
|
||
if(jsonDoc.toVariant().toMap().value("error").toString() == "invalid_redirect_uri") {
|
||
qWarning(dcHomeConnect()) << "Missing redirect_uri parameter.";
|
||
}
|
||
if(jsonDoc.toVariant().toMap().value("error").toString() == "invalid_code") {
|
||
qWarning(dcHomeConnect()) << "Expired authorization code.";
|
||
}
|
||
}
|
||
return;
|
||
case 401:
|
||
qWarning(dcHomeConnect()) << "Client does not have permission to use this API.";
|
||
return;
|
||
case 405:
|
||
qWarning(dcHomeConnect()) << "Wrong HTTP method used.";
|
||
return;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
void HomeConnect::getAccessTokenFromRefreshToken(const QByteArray &refreshToken)
|
||
{
|
||
if (refreshToken.isEmpty()) {
|
||
qWarning(dcHomeConnect) << "No refresh token given!";
|
||
emit authenticationStatusChanged(false);
|
||
return;
|
||
}
|
||
|
||
QUrl url(m_baseAuthorizationUrl);
|
||
QUrlQuery query;
|
||
query.clear();
|
||
query.addQueryItem("grant_type", "refresh_token");
|
||
query.addQueryItem("refresh_token", refreshToken);
|
||
url.setQuery(query);
|
||
|
||
QNetworkRequest request(url);
|
||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded; charset=UTF-8");
|
||
|
||
QByteArray auth = QByteArray(m_clientKey + ':' + m_clientSecret).toBase64(QByteArray::Base64Encoding | QByteArray::KeepTrailingEquals);
|
||
request.setRawHeader("Authorization", QString("Basic %1").arg(QString(auth)).toUtf8());
|
||
|
||
QNetworkReply *reply = m_networkManager->post(request, QByteArray());
|
||
connect(reply, &QNetworkReply::finished, this, [this, reply](){
|
||
reply->deleteLater();
|
||
|
||
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll());
|
||
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||
if (status != 200 || reply->error() != QNetworkReply::NoError) {
|
||
if(jsonDoc.toVariant().toMap().contains("error_description")) {
|
||
qWarning(dcHomeConnect()) << "Access token error:" << jsonDoc.toVariant().toMap().value("error_description").toString();
|
||
}
|
||
emit authenticationStatusChanged(false);
|
||
return;
|
||
}
|
||
if(!jsonDoc.toVariant().toMap().contains("access_token")) {
|
||
emit authenticationStatusChanged(false);
|
||
return;
|
||
}
|
||
m_accessToken = jsonDoc.toVariant().toMap().value("access_token").toByteArray();
|
||
|
||
if (jsonDoc.toVariant().toMap().contains("expires_in")) {
|
||
int expireTime = jsonDoc.toVariant().toMap().value("expires_in").toInt();
|
||
qCDebug(dcHomeConnect) << "Access token expires at" << QDateTime::currentDateTime().addSecs(expireTime).toString();
|
||
if (!m_tokenRefreshTimer) {
|
||
qWarning(dcHomeConnect()) << "Access token refresh timer not initialized";
|
||
return;
|
||
}
|
||
m_tokenRefreshTimer->start((expireTime - 20) * 1000);
|
||
}
|
||
emit authenticationStatusChanged(true);;
|
||
});
|
||
}
|
||
|
||
void HomeConnect::getAccessTokenFromAuthorizationCode(const QByteArray &authorizationCode)
|
||
{
|
||
// Obtaining access token
|
||
if(authorizationCode.isEmpty())
|
||
qWarning(dcHomeConnect) << "No auhtorization code given!";
|
||
if(m_clientKey.isEmpty())
|
||
qWarning(dcHomeConnect) << "Client key not set!";
|
||
if(m_clientSecret.isEmpty())
|
||
qWarning(dcHomeConnect) << "Client secret not set!";
|
||
|
||
QUrl url = QUrl(m_baseAuthorizationUrl);
|
||
QUrlQuery query;
|
||
query.clear();
|
||
query.addQueryItem("grant_type", "authorization_code");
|
||
query.addQueryItem("code", authorizationCode);
|
||
query.addQueryItem("redirect_uri", m_redirectUri);
|
||
url.setQuery(query);
|
||
|
||
QNetworkRequest request(url);
|
||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded;charset=utf-8");
|
||
|
||
QByteArray auth = QByteArray(m_clientKey + ':' + m_clientSecret).toBase64(QByteArray::Base64Encoding | QByteArray::KeepTrailingEquals);
|
||
request.setRawHeader("Authorization", QString("Basic %1").arg(QString(auth)).toUtf8());
|
||
|
||
QNetworkReply *reply = m_networkManager->post(request, QByteArray());
|
||
connect(reply, &QNetworkReply::finished, this, [this, reply](){
|
||
reply->deleteLater();
|
||
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll());
|
||
|
||
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||
|
||
qCDebug(dcHomeConnect()) << "HomeConnect accessToken reply:" << this << reply->error() << reply->errorString() << jsonDoc.toJson();
|
||
if(!jsonDoc.toVariant().toMap().contains("access_token") || !jsonDoc.toVariant().toMap().contains("refresh_token") ) {
|
||
emit authenticationStatusChanged(false);
|
||
return;
|
||
}
|
||
qCDebug(dcHomeConnect()) << "Access token:" << jsonDoc.toVariant().toMap().value("access_token").toString();
|
||
m_accessToken = jsonDoc.toVariant().toMap().value("access_token").toByteArray();
|
||
|
||
qCDebug(dcHomeConnect()) << "Refresh token:" << jsonDoc.toVariant().toMap().value("refresh_token").toString();
|
||
m_refreshToken = jsonDoc.toVariant().toMap().value("refresh_token").toByteArray();
|
||
|
||
if (jsonDoc.toVariant().toMap().contains("expires_in")) {
|
||
int expireTime = jsonDoc.toVariant().toMap().value("expires_in").toInt();
|
||
qCDebug(dcHomeConnect()) << "expires at" << QDateTime::currentDateTime().addSecs(expireTime).toString();
|
||
if (!m_tokenRefreshTimer) {
|
||
qWarning(dcHomeConnect()) << "Token refresh timer not initialized";
|
||
emit authenticationStatusChanged(false);
|
||
return;
|
||
}
|
||
m_tokenRefreshTimer->start((expireTime - 20) * 1000);
|
||
}
|
||
emit authenticationStatusChanged(true);
|
||
});
|
||
}
|