Merge pull request #573 from ctrlaltdavid/feature/oauth2-login-display

Display domain login dialog
This commit is contained in:
kasenvr 2020-07-25 13:44:24 -04:00 committed by GitHub
commit ff648bf95b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 224 additions and 21 deletions

View file

@ -4,6 +4,7 @@
//
// Created by Stephen Birarda on 2015-08-24.
// Copyright 2015 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -514,7 +515,7 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
nodeConnection.machineFingerprint);
if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) {
if (domainHasLogin() && !domainUsername.isEmpty()) {
if (domainHasLogin()) {
sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.",
nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedDomain);
} else {

View file

@ -4,6 +4,7 @@
//
// Created by Stephen Birarda on 2015-08-24.
// Copyright 2015 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html

View file

@ -45,6 +45,8 @@ Item {
property bool lostFocus: false
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
readonly property bool isLoggingInToDomain: loginDialog.getDomainLoginRequested()
readonly property string domainAuthProvider: loginDialog.getDomainLoginAuthProvider()
QtObject {
id: d
@ -71,7 +73,12 @@ Item {
}
function login() {
loginDialog.login(emailField.text, passwordField.text);
if (!isLoggingInToDomain) {
loginDialog.login(emailField.text, passwordField.text);
} else {
loginDialog.loginDomain(emailField.text, passwordField.text, domainAuthProvider);
}
if (linkAccountBody.loginDialogPoppedUp) {
var data;
if (linkAccountBody.linkSteam) {
@ -87,7 +94,7 @@ Item {
}
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam,
"withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus,
"displayName":displayNameField.text });
"displayName":displayNameField.text, "isLoggingInToDomain": linkAccountBody.isLoggingInToDomain });
}
function init() {
@ -99,13 +106,19 @@ Item {
errorContainer.height = (loginErrorMessageTextMetrics.width / displayNameField.width) * loginErrorMessageTextMetrics.height;
}
loginButton.text = (!linkAccountBody.linkSteam && !linkAccountBody.linkOculus) ? "Log In" : "Link Account";
loginButton.text = (!isLoggingInToDomain) ? "Log In" : "Log In to Domain";
loginButton.color = hifi.buttons.blue;
displayNameField.placeholderText = "Display Name (optional)";
var savedDisplayName = Settings.getValue("Avatar/displayName", "");
displayNameField.text = savedDisplayName;
emailField.placeholderText = "Username or Email";
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : "";
if (!isLoggingInToDomain) {
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : "";
} else {
// ####### TODO
}
if (linkAccountBody.linkSteam || linkAccountBody.linkOculus) {
loginButton.width = (passwordField.width - hifi.dimensions.contentSpacing.x) / 2;
loginButton.anchors.right = displayNameField.right;
@ -193,7 +206,11 @@ Item {
case Qt.Key_Return:
event.accepted = true;
if (keepMeLoggedInCheckbox.checked) {
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
if (!isLoggingInToDomain) {
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
} else {
// ####### TODO
}
}
linkAccountBody.login();
break;
@ -232,7 +249,11 @@ Item {
case Qt.Key_Return:
event.accepted = true;
if (keepMeLoggedInCheckbox.checked) {
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
if (!isLoggingInToDomain) {
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
} else {
// ####### TODO
}
}
linkAccountBody.login();
break;
@ -312,7 +333,11 @@ Item {
case Qt.Key_Return:
event.accepted = true;
if (keepMeLoggedInCheckbox.checked) {
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
if (!isLoggingInToDomain) {
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
} else {
// ####### TODO
}
}
linkAccountBody.login();
break;
@ -321,7 +346,7 @@ Item {
}
HifiControlsUit.CheckBox {
id: keepMeLoggedInCheckbox
checked: Settings.getValue("keepMeLoggedIn", false);
checked: !isLoggingInToDomain ? Settings.getValue("keepMeLoggedIn", false) : false; // ####### TODO
text: qsTr("Keep Me Logged In");
boxSize: 18;
labelFontFamily: linkAccountBody.fontFamily
@ -334,14 +359,22 @@ Item {
}
onCheckedChanged: {
Settings.setValue("keepMeLoggedIn", checked);
if (keepMeLoggedInCheckbox.checked) {
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
if (!isLoggingInToDomain) {
if (keepMeLoggedInCheckbox.checked) {
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
} else {
Settings.setValue("keepMeLoggedIn/savedUsername", "");
}
} else {
Settings.setValue("keepMeLoggedIn/savedUsername", "");
// ####### TODO
}
}
Component.onCompleted: {
keepMeLoggedInCheckbox.checked = !Account.loggedIn;
if (!isLoggingInToDomain) {
keepMeLoggedInCheckbox.checked = !Account.loggedIn;
} else {
// ####### TODO
}
}
}
HifiControlsUit.Button {
@ -393,7 +426,7 @@ Item {
HifiStylesUit.ShortcutText {
id: cantAccessText
z: 10
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus && !linkAccountBody.isLoggingInToDomain
anchors {
top: loginButton.bottom
topMargin: hifi.dimensions.contentSpacing.y
@ -492,7 +525,7 @@ Item {
id: signUpContainer
width: loginContainer.width
height: signUpTextMetrics.height
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus && !linkAccountBody.isLoggingInToDomain
anchors {
left: loginContainer.left
top: loginContainer.bottom

View file

@ -3,6 +3,7 @@
//
// Created by Wayne Chen on 10/18/18
// Copyright 2018 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -31,6 +32,7 @@ Item {
property bool linkSteam: linkSteam
property bool linkOculus: linkOculus
property bool createOculus: createOculus
property bool isLoggingInToDomain: isLoggingInToDomain
property string displayName: ""
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
@ -106,6 +108,9 @@ Item {
loggingInGlyph.visible = true;
loggingInText.text = "Logging in to Oculus";
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
} else if (loggingInBody.isLoggingInToDomain) {
loggingInText.text = "Logging in to Domain";
loggingInText.anchors.centerIn = loggingInHeader;
} else {
loggingInText.text = "Logging in";
loggingInText.anchors.centerIn = loggingInHeader;

View file

@ -4,6 +4,7 @@
//
// Created by Andrzej Kapolka on 5/10/13.
// Copyright 2013 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -65,6 +66,7 @@
#include <Trace.h>
#include <ResourceScriptingInterface.h>
#include <AccountManager.h>
#include <DomainAccountManager.h>
#include <AddressManager.h>
#include <AnimDebugDraw.h>
#include <BuildInfo.h>
@ -852,6 +854,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
#else
DependencyManager::set<AccountManager>(true, std::bind(&Application::getUserAgent, qApp));
#endif
DependencyManager::set<DomainAccountManager>();
DependencyManager::set<StatTracker>();
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT, defaultScriptsOverrideOption);
DependencyManager::set<Preferences>();
@ -1348,6 +1351,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
#endif
connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
connect(domainAccountManager.data(), &DomainAccountManager::authRequired, dialogsManager.data(),
&DialogsManager::showDomainLoginDialog);
// ####### TODO: Connect any other signals from domainAccountManager.
// use our MyAvatar position and quat for address manager path
addressManager->setPositionGetter([] {
auto avatarManager = DependencyManager::get<AvatarManager>();
@ -2801,6 +2812,7 @@ void Application::cleanupBeforeQuit() {
if (!keepMeLoggedIn) {
DependencyManager::get<AccountManager>()->removeAccountFromFile();
}
// ####### TODO
_displayPlugin.reset();
PluginManager::getInstance()->shutdown();
@ -3150,6 +3162,7 @@ extern void setupPreferences();
static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active = false);
#endif
// ####### TODO
void Application::showLoginScreen() {
#if !defined(DISABLE_QML)
auto accountManager = DependencyManager::get<AccountManager>();
@ -7072,6 +7085,7 @@ void Application::updateWindowTitle() const {
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
+ " " + applicationVersion();
// ####### TODO
QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)";
QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" :
@ -9430,6 +9444,7 @@ void Application::forceDisplayName(const QString& displayName) {
getMyAvatar()->setDisplayName(displayName);
}
void Application::forceLoginWithTokens(const QString& tokens) {
// ####### TODO
DependencyManager::get<AccountManager>()->setAccessTokens(tokens);
Setting::Handle<bool>(KEEP_ME_LOGGED_IN_SETTING_NAME, true).set(true);
}

View file

@ -4,6 +4,7 @@
//
// Created by Clement on 1/18/15.
// Copyright 2015 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -110,10 +111,16 @@ void DialogsManager::setDomainConnectionFailureVisibility(bool visible) {
}
void DialogsManager::toggleLoginDialog() {
_isDomainLogin = false;
LoginDialog::toggleAction();
}
void DialogsManager::showLoginDialog() {
// ####### TODO: May be called from script via DialogsManagerScriptingInterface. Need to handle the case that it's already
// displayed and may be the domain login version.
_isDomainLogin = false;
LoginDialog::showWithSelection();
}
@ -121,10 +128,22 @@ void DialogsManager::hideLoginDialog() {
LoginDialog::hide();
}
void DialogsManager::showDomainLoginDialog() {
_isDomainLogin = true;
LoginDialog::showWithSelection();
}
// #######: TODO: Domain version of toggleLoginDialog()?
// #######: TODO: Domain version of hiadLoginDialog()?
void DialogsManager::showUpdateDialog() {
UpdateDialog::show();
}
void DialogsManager::octreeStatsDetails() {
if (!_octreeStatsDialog) {
_octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats());

View file

@ -4,6 +4,7 @@
//
// Created by Clement on 1/18/15.
// Copyright 2015 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -40,6 +41,7 @@ public:
QPointer<TestingDialog> getTestingDialog() const { return _testingDialog; }
void emitAddressBarShown(bool visible) { emit addressBarShown(visible); }
void setAddressBarVisible(bool addressBarVisible);
bool getIsDomainLogin() { return _isDomainLogin; }
public slots:
void showAddressBar();
@ -49,6 +51,7 @@ public slots:
void toggleLoginDialog();
void showLoginDialog();
void hideLoginDialog();
void showDomainLoginDialog();
void octreeStatsDetails();
void lodTools();
void hmdTools(bool showTools);
@ -82,6 +85,8 @@ private:
QPointer<DomainConnectionDialog> _domainConnectionDialog;
bool _dialogCreatedWhileShown { false };
bool _addressBarVisible { false };
bool _isDomainLogin { false };
};
#endif // hifi_DialogsManager_h

View file

@ -4,6 +4,7 @@
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -25,6 +26,7 @@
#include "AccountManager.h"
#include "DependencyManager.h"
#include "DialogsManager.h"
#include "Menu.h"
#include "Application.h"
@ -131,10 +133,19 @@ void LoginDialog::dismissLoginDialog() {
}
void LoginDialog::login(const QString& username, const QString& password) const {
qDebug() << "Attempting to login " << username;
qDebug() << "Attempting to login" << username;
DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
}
void LoginDialog::loginDomain(const QString& username, const QString& password, const QString& domainAuthProvider) const {
qDebug() << "Attempting to login" << username << "into a domain through" << domainAuthProvider;
// ####### TODO
// DependencyManager::get<DomainAccountManager>()->requestAccessToken(username, password, domainAuthProvider);
// ####### TODO: It may not be necessary to pass domainAuthProvider to the login dialog and through to here because it was
// originally provided to the QML from C++.
}
void LoginDialog::loginThroughOculus() {
qDebug() << "Attempting to login through Oculus";
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
@ -410,3 +421,13 @@ void LoginDialog::signupFailed(QNetworkReply* reply) {
emit handleSignupFailed(DEFAULT_SIGN_UP_FAILURE_MESSAGE);
}
}
bool LoginDialog::getDomainLoginRequested() const {
return DependencyManager::get<DialogsManager>()->getIsDomainLogin();
}
// ####### TODO: This method may not be necessary.
QString LoginDialog::getDomainLoginAuthProvider() const {
// ####### TODO
return QString("https://example.com/oauth2");
}

View file

@ -4,6 +4,7 @@
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -71,6 +72,7 @@ protected slots:
Q_INVOKABLE QString oculusUserID() const;
Q_INVOKABLE void login(const QString& username, const QString& password) const;
Q_INVOKABLE void loginDomain(const QString& username, const QString& password, const QString& domainAuthProvider) const;
Q_INVOKABLE void loginThroughSteam();
Q_INVOKABLE void linkSteam();
Q_INVOKABLE void createAccountFromSteam(QString username = QString());
@ -81,6 +83,10 @@ protected slots:
Q_INVOKABLE void signup(const QString& email, const QString& username, const QString& password);
Q_INVOKABLE bool getLoginDialogPoppedUp() const;
Q_INVOKABLE bool getDomainLoginRequested() const;
Q_INVOKABLE QString getDomainLoginAuthProvider() const;
};
#endif // hifi_LoginDialog_h

View file

@ -0,0 +1,39 @@
//
// DomainAccountManager.cpp
// libraries/networking/src
//
// Created by David Rowe on 23 Jul 2020.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DomainAccountManager.h"
#include <QTimer>
DomainAccountManager::DomainAccountManager() {
}
bool DomainAccountManager::hasValidAccessToken() {
// #######: TODO
return false;
}
bool DomainAccountManager::checkAndSignalForAccessToken() {
bool hasToken = hasValidAccessToken();
if (!hasToken) {
// Emit a signal so somebody can call back to us and request an access token given a user name and password.
// Dialog can be hidden immediately after showing if we've just teleported to the domain, unless the signal is delayed.
QTimer::singleShot(500, this, [this] { emit this->authRequired(); });
}
return hasToken;
}

View file

@ -0,0 +1,39 @@
//
// DomainAccountManager.h
// libraries/networking/src
//
// Created by David Rowe on 23 Jul 2020.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_DomainAccountManager_h
#define hifi_DomainAccountManager_h
#include <QtCore/QObject>
#include <DependencyManager.h>
class DomainAccountManager : public QObject, public Dependency {
Q_OBJECT
public:
DomainAccountManager();
Q_INVOKABLE bool checkAndSignalForAccessToken();
public slots:
signals:
void authRequired();
private slots:
private:
bool hasValidAccessToken();
};
#endif // hifi_DomainAccountManager_h

View file

@ -4,6 +4,7 @@
//
// Created by Stephen Birarda on 2/18/2014.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -24,6 +25,7 @@
#include "AddressManager.h"
#include "Assignment.h"
#include "DomainAccountManager.h"
#include "HifiSockAddr.h"
#include "NodeList.h"
#include "udt/Packet.h"
@ -220,6 +222,8 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) {
QString previousHost = _domainURL.host();
_domainURL = domainURL;
_hasCheckedForDomainAccessToken = false;
if (previousHost != domainURL.host()) {
qCDebug(networking) << "Updated domain hostname to" << domainURL.host();
@ -500,6 +504,7 @@ bool DomainHandler::reasonSuggestsMetaverseLogin(ConnectionRefusedReason reasonC
case ConnectionRefusedReason::Unknown:
case ConnectionRefusedReason::ProtocolMismatch:
case ConnectionRefusedReason::TooManyUsers:
case ConnectionRefusedReason::NotAuthorizedDomain:
return false;
}
return false;
@ -515,6 +520,7 @@ bool DomainHandler::reasonSuggestsDomainLogin(ConnectionRefusedReason reasonCode
case ConnectionRefusedReason::Unknown:
case ConnectionRefusedReason::ProtocolMismatch:
case ConnectionRefusedReason::TooManyUsers:
case ConnectionRefusedReason::NotAuthorizedMetaverse:
return false;
}
return false;
@ -557,12 +563,13 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
#endif
}
auto accountManager = DependencyManager::get<AccountManager>();
// Some connection refusal reasons imply that a login is required. If so, suggest a new login.
if (reasonSuggestsMetaverseLogin(reasonCode)) {
qCWarning(networking) << "Make sure you are logged in to the metaverse.";
auto accountManager = DependencyManager::get<AccountManager>();
if (!_hasCheckedForAccessToken) {
accountManager->checkAndSignalForAccessToken();
_hasCheckedForAccessToken = true;
@ -578,7 +585,15 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
} else if (reasonSuggestsDomainLogin(reasonCode)) {
qCWarning(networking) << "Make sure you are logged in to the domain.";
auto accountManager = DependencyManager::get<DomainAccountManager>();
if (!_hasCheckedForDomainAccessToken) {
accountManager->checkAndSignalForAccessToken();
_hasCheckedForDomainAccessToken = true;
}
// ####### TODO: regenerate key-pair after several failed connection attempts, similar to metaverse login code?
}
}

View file

@ -4,6 +4,7 @@
//
// Created by Stephen Birarda on 2/18/2014.
// Copyright 2014 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -291,6 +292,7 @@ private:
QSet<QString> _domainConnectionRefusals;
bool _hasCheckedForAccessToken { false };
bool _hasCheckedForDomainAccessToken { false };
int _connectionDenialsSinceKeypairRegen { 0 };
int _checkInPacketsSinceLastReply { 0 };

View file

@ -4,6 +4,7 @@
//
// Created by Stephen Birarda on 2/15/13.
// Copyright 2013 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -486,10 +487,10 @@ void NodeList::sendDomainServerCheckIn() {
// ####### If get into difficulties, could perhaps send domain's username and signature instead of metaverse.
bool domainLoginIsConnected = false;
if (!domainLoginIsConnected) {
if (true) {
if (false) { // ####### For testing, false causes user to be considered "not logged in".
packetStream << QString("a@b.c");
if (true) {
packetStream << QString("signature");
if (true) { // ####### For testing, false is unhandled at this stage.
packetStream << QString("signature"); // #######: Consider "logged in" if this is sent during testing.
}
}
}

View file

@ -4,6 +4,7 @@
//
// Created by Seth Alves on 2016-6-1.
// Copyright 2016 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html