diff --git a/Linphone/model/auth/OIDCModel.cpp b/Linphone/model/auth/OIDCModel.cpp index a57554964a1ae09a88ef8c93f02ea6e64028249e..e502222fe488583e0c3fa6c5a52215d45b989803 100644 --- a/Linphone/model/auth/OIDCModel.cpp +++ b/Linphone/model/auth/OIDCModel.cpp @@ -27,7 +27,7 @@ #include "tool/Utils.hpp" // ============================================================================= -static constexpr char OIDCClientId[] = "linphone"; + static constexpr char OIDCScope[] = "offline_access"; static constexpr char OIDCWellKnown[] = "/.well-known/openid-configuration"; @@ -48,21 +48,55 @@ QString OAuthHttpServerReplyHandler::callback() const { } OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObject *parent) { - auto replyHandler = new OAuthHttpServerReplyHandler(0, this); + auto port = CoreModel::getInstance()->getCore()->getConfig()->getInt("app", "oidc_redirect_uri_port", 0); + qDebug() << "OIDC Redirect URI Port set to [" << port << "]"; + auto replyHandler = new OAuthHttpServerReplyHandler(port, this); + if (!replyHandler->isListening()) { + qWarning() << "OAuthHttpServerReplyHandler is not listening on port" << port; + emit requestFailed(tr("OAuthHttpServerReplyHandler is not listening")); + emit finished(); + return; + } mAuthInfo = authInfo; mOidc.setReplyHandler(replyHandler); - mOidc.setAuthorizationUrl(QUrl(Utils::coreStringToAppString(authInfo->getAuthorizationServer()))); + auto autorizationUrl = QUrl(Utils::coreStringToAppString(authInfo->getAuthorizationServer())); + mOidc.setAuthorizationUrl(autorizationUrl); mOidc.setNetworkAccessManager(new QNetworkAccessManager(&mOidc)); - mOidc.setClientIdentifier(OIDCClientId); - mAuthInfo->setClientId(OIDCClientId); - mOidc.setScope(OIDCScope); + QString clientid = QString::fromStdString(CoreModel::getInstance()->getCore()->getConfig()->getString( + "app", "oidc_client_id", QCoreApplication::applicationName().toStdString())); + if (autorizationUrl.hasQuery()) { + QUrlQuery query(autorizationUrl); + if (query.hasQueryItem("client_id")) { + clientid = query.queryItemValue("client_id"); + } + } + mOidc.setClientIdentifier(clientid); + mAuthInfo->setClientId(clientid.toStdString()); + qDebug() << "OIDC Client ID set to [" << clientid << "]"; + + // find an auth info from LinphoneCore where username = clientid + auto clientSecret = CoreModel::getInstance()->getCore()->findAuthInfo("", clientid.toStdString(), ""); + if (clientSecret != nullptr) { + qDebug() << "client secret found for client id [" << clientid << "]"; + mOidc.setClientIdentifierSharedKey(clientSecret->getPassword().c_str()); + } + + QString scope = OIDCScope; + ; + if (autorizationUrl.hasQuery()) { + QUrlQuery query(autorizationUrl); + if (query.hasQueryItem("scope")) { + scope = query.queryItemValue("scope"); + } + } + mOidc.setScope(scope); mTimeout.setInterval(1000 * 60 * 2); // 2minutes connect(&mTimeout, &QTimer::timeout, [this]() { qWarning() << log().arg("Timeout reached for OpenID connection."); dynamic_cast<OAuthHttpServerReplyHandler *>(mOidc.replyHandler())->close(); CoreModel::getInstance()->getCore()->abortAuthentication(mAuthInfo); - emit statusChanged("Timeout: Not authenticated"); + emit statusChanged(tr("Timeout: Not authenticated")); emit finished(); }); connect(mOidc.networkAccessManager(), &QNetworkAccessManager::authenticationRequired, @@ -74,22 +108,22 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec switch (status) { case QAbstractOAuth::Status::Granted: { mTimeout.stop(); - emit statusChanged("Authentication granted"); + emit statusChanged(tr("Authentication granted")); emit authenticated(); break; } case QAbstractOAuth::Status::NotAuthenticated: { mTimeout.stop(); - emit statusChanged("Not authenticated"); + emit statusChanged(tr("Not authenticated")); emit finished(); break; } case QAbstractOAuth::Status::RefreshingToken: { - emit statusChanged("Refreshing token"); + emit statusChanged(tr("Refreshing token")); break; } case QAbstractOAuth::Status::TemporaryCredentialsReceived: { - emit statusChanged("Temporary credentials received"); + emit statusChanged(tr("Temporary credentials received")); break; } default: { @@ -99,22 +133,26 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec connect(&mOidc, &QOAuth2AuthorizationCodeFlow::requestFailed, [=](QAbstractOAuth::Error error) { mTimeout.stop(); - qWarning() << "RequestFailed:" << (int)error; + + const QMetaObject metaObject = QAbstractOAuth::staticMetaObject; + int index = metaObject.indexOfEnumerator("Error"); + QMetaEnum metaEnum = metaObject.enumerator(index); + qWarning() << "RequestFailed:" << metaEnum.valueToKey(static_cast<int>(error)); switch (error) { case QAbstractOAuth::Error::NetworkError: - emit requestFailed("Network error"); + emit requestFailed(tr("Network error")); break; case QAbstractOAuth::Error::ServerError: - emit requestFailed("Server error"); + emit requestFailed(tr("Server error")); break; case QAbstractOAuth::Error::OAuthTokenNotFoundError: - emit requestFailed("OAuth token not found"); + emit requestFailed(tr("OAuth token not found")); break; case QAbstractOAuth::Error::OAuthTokenSecretNotFoundError: - emit requestFailed("OAuth token secret not found"); + emit requestFailed(tr("OAuth token secret not found")); break; case QAbstractOAuth::Error::OAuthCallbackNotVerified: - emit requestFailed("OAuth callback not verified"); + emit requestFailed(tr("OAuth callback not verified")); break; default: { } @@ -124,7 +162,7 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec connect(&mOidc, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, [this](const QUrl &url) { qDebug() << "Browser authentication url : " << url; - emit statusChanged("Requesting authorization from browser"); + emit statusChanged(tr("Requesting authorization from browser")); mTimeout.start(); QDesktopServices::openUrl(url); }); @@ -141,21 +179,26 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant> *parameters) { parameters->insert("login_hint", username); parameters->replace("application_type", "native"); + if (stage == QAbstractOAuth::Stage::RequestingAuthorization) { + QUrl redirectUri = parameters->value("redirect_uri").toUrl(); + redirectUri.setHost("localhost"); + parameters->replace("redirect_uri", redirectUri); + } switch (stage) { case QAbstractOAuth::Stage::RequestingAccessToken: { - emit statusChanged("Requesting access token"); + emit statusChanged(tr("Requesting access token")); break; } case QAbstractOAuth::Stage::RefreshingAccessToken: { - emit statusChanged("Refreshing access token"); + emit statusChanged(tr("Refreshing access token")); break; } case QAbstractOAuth::Stage::RequestingAuthorization: { - emit statusChanged("Requesting authorization"); + emit statusChanged(tr("Requesting authorization")); break; } case QAbstractOAuth::Stage::RequestingTemporaryCredentials: { - emit statusChanged("Requesting temporary credentials"); + emit statusChanged(tr("Requesting temporary credentials")); break; } default: { @@ -165,7 +208,9 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec connect(this, &OIDCModel::finished, this, &OIDCModel::deleteLater); - QNetworkRequest request(QUrl(Utils::coreStringToAppString(authInfo->getAuthorizationServer()) + OIDCWellKnown)); + auto url = QUrl(Utils::coreStringToAppString(authInfo->getAuthorizationServer())); + url.setPath(url.path() + OIDCWellKnown); + QNetworkRequest request(url); auto reply = mOidc.networkAccessManager()->get(request); connect(reply, &QNetworkReply::finished, this, &OIDCModel::openIdConfigReceived); } @@ -177,11 +222,21 @@ void OIDCModel::openIdConfigReceived() { auto rootArray = document.toVariant().toMap(); if (rootArray.contains("authorization_endpoint")) { mOidc.setAuthorizationUrl(QUrl(rootArray["authorization_endpoint"].toString())); + } else { + qWarning() << "No authorization endpoint found in OpenID configuration"; + emit requestFailed(tr("No authorization endpoint found in OpenID configuration")); + emit finished(); + return; } if (rootArray.contains("token_endpoint")) { mOidc.setAccessTokenUrl(QUrl(rootArray["token_endpoint"].toString())); mAuthInfo->setTokenEndpointUri( Utils::appStringToCoreString(QUrl(rootArray["token_endpoint"].toString()).toString())); + } else { + qWarning() << "No token endpoint found in OpenID configuration"; + emit requestFailed(tr("No token endpoint found in OpenID configuration")); + emit finished(); + return; } mOidc.grant(); reply->deleteLater();