diff options
| author | Petr Mrázek <peterix@gmail.com> | 2016-04-10 15:53:05 +0200 | 
|---|---|---|
| committer | Petr Mrázek <peterix@gmail.com> | 2016-05-01 00:00:14 +0200 | 
| commit | b6d455a02bd338e9dc0faa09d4d8177ecd8d569a (patch) | |
| tree | 41982bca1ede50049f2f8c7109dd18edeefde6d0 /api/logic/minecraft/auth/flows | |
| parent | 47e37635f50c09b4f9a9ee7699e3120bab3e4088 (diff) | |
| download | PrismLauncher-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.tar.gz PrismLauncher-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.tar.bz2 PrismLauncher-b6d455a02bd338e9dc0faa09d4d8177ecd8d569a.zip | |
NOISSUE reorganize and document libraries
Diffstat (limited to 'api/logic/minecraft/auth/flows')
| -rw-r--r-- | api/logic/minecraft/auth/flows/AuthenticateTask.cpp | 202 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/AuthenticateTask.h | 46 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/RefreshTask.cpp | 144 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/RefreshTask.h | 44 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/ValidateTask.cpp | 61 | ||||
| -rw-r--r-- | api/logic/minecraft/auth/flows/ValidateTask.h | 47 | 
6 files changed, 544 insertions, 0 deletions
| diff --git a/api/logic/minecraft/auth/flows/AuthenticateTask.cpp b/api/logic/minecraft/auth/flows/AuthenticateTask.cpp new file mode 100644 index 00000000..8d136f0b --- /dev/null +++ b/api/logic/minecraft/auth/flows/AuthenticateTask.cpp @@ -0,0 +1,202 @@ + +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AuthenticateTask.h" +#include "../MojangAccount.h" + +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> +#include <QVariant> + +#include <QDebug> +#include <QUuid> + +AuthenticateTask::AuthenticateTask(MojangAccount * account, const QString &password, +								   QObject *parent) +	: YggdrasilTask(account, parent), m_password(password) +{ +} + +QJsonObject AuthenticateTask::getRequestContent() const +{ +	/* +	 * { +	 *   "agent": {								// optional +	 *   "name": "Minecraft",					// So far this is the only encountered value +	 *   "version": 1							// This number might be increased +	 * 											// by the vanilla client in the future +	 *   }, +	 *   "username": "mojang account name",		// Can be an email address or player name for +												// unmigrated accounts +	 *  "password": "mojang account password", +	 *  "clientToken": "client identifier"		// optional +	 *  "requestUser": true/false               // request the user structure +	 * } +	 */ +	QJsonObject req; + +	{ +		QJsonObject agent; +		// C++ makes string literals void* for some stupid reason, so we have to tell it +		// QString... Thanks Obama. +		agent.insert("name", QString("Minecraft")); +		agent.insert("version", 1); +		req.insert("agent", agent); +	} + +	req.insert("username", m_account->username()); +	req.insert("password", m_password); +	req.insert("requestUser", true); + +	// If we already have a client token, give it to the server. +	// Otherwise, let the server give us one. + +	if(m_account->m_clientToken.isEmpty()) +	{ +		auto uuid = QUuid::createUuid(); +		auto uuidString = uuid.toString().remove('{').remove('-').remove('}'); +		m_account->m_clientToken = uuidString; +	} +	req.insert("clientToken", m_account->m_clientToken); + +	return req; +} + +void AuthenticateTask::processResponse(QJsonObject responseData) +{ +	// Read the response data. We need to get the client token, access token, and the selected +	// profile. +	qDebug() << "Processing authentication response."; +	// qDebug() << responseData; +	// If we already have a client token, make sure the one the server gave us matches our +	// existing one. +	qDebug() << "Getting client token."; +	QString clientToken = responseData.value("clientToken").toString(""); +	if (clientToken.isEmpty()) +	{ +		// Fail if the server gave us an empty client token +		changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token.")); +		return; +	} +	if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken) +	{ +		changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported.")); +		return; +	} +	// Set the client token. +	m_account->m_clientToken = clientToken; + +	// Now, we set the access token. +	qDebug() << "Getting access token."; +	QString accessToken = responseData.value("accessToken").toString(""); +	if (accessToken.isEmpty()) +	{ +		// Fail if the server didn't give us an access token. +		changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token.")); +		return; +	} +	// Set the access token. +	m_account->m_accessToken = accessToken; + +	// Now we load the list of available profiles. +	// Mojang hasn't yet implemented the profile system, +	// but we might as well support what's there so we +	// don't have trouble implementing it later. +	qDebug() << "Loading profile list."; +	QJsonArray availableProfiles = responseData.value("availableProfiles").toArray(); +	QList<AccountProfile> loadedProfiles; +	for (auto iter : availableProfiles) +	{ +		QJsonObject profile = iter.toObject(); +		// Profiles are easy, we just need their ID and name. +		QString id = profile.value("id").toString(""); +		QString name = profile.value("name").toString(""); +		bool legacy = profile.value("legacy").toBool(false); + +		if (id.isEmpty() || name.isEmpty()) +		{ +			// This should never happen, but we might as well +			// warn about it if it does so we can debug it easily. +			// You never know when Mojang might do something truly derpy. +			qWarning() << "Found entry in available profiles list with missing ID or name " +						   "field. Ignoring it."; +		} + +		// Now, add a new AccountProfile entry to the list. +		loadedProfiles.append({id, name, legacy}); +	} +	// Put the list of profiles we loaded into the MojangAccount object. +	m_account->m_profiles = loadedProfiles; + +	// Finally, we set the current profile to the correct value. This is pretty simple. +	// We do need to make sure that the current profile that the server gave us +	// is actually in the available profiles list. +	// If it isn't, we'll just fail horribly (*shouldn't* ever happen, but you never know). +	qDebug() << "Setting current profile."; +	QJsonObject currentProfile = responseData.value("selectedProfile").toObject(); +	QString currentProfileId = currentProfile.value("id").toString(""); +	if (currentProfileId.isEmpty()) +	{ +		changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify a currently selected profile. The account exists, but likely isn't premium.")); +		return; +	} +	if (!m_account->setCurrentProfile(currentProfileId)) +	{ +		changeState(STATE_FAILED_HARD, tr("Authentication server specified a selected profile that wasn't in the available profiles list.")); +		return; +	} + +	// this is what the vanilla launcher passes to the userProperties launch param +	if (responseData.contains("user")) +	{ +		User u; +		auto obj = responseData.value("user").toObject(); +		u.id = obj.value("id").toString(); +		auto propArray = obj.value("properties").toArray(); +		for (auto prop : propArray) +		{ +			auto propTuple = prop.toObject(); +			auto name = propTuple.value("name").toString(); +			auto value = propTuple.value("value").toString(); +			u.properties.insert(name, value); +		} +		m_account->m_user = u; +	} + +	// We've made it through the minefield of possible errors. Return true to indicate that +	// we've succeeded. +	qDebug() << "Finished reading authentication response."; +	changeState(STATE_SUCCEEDED); +} + +QString AuthenticateTask::getEndpoint() const +{ +	return "authenticate"; +} + +QString AuthenticateTask::getStateMessage() const +{ +	switch (m_state) +	{ +	case STATE_SENDING_REQUEST: +		return tr("Authenticating: Sending request..."); +	case STATE_PROCESSING_RESPONSE: +		return tr("Authenticating: Processing response..."); +	default: +		return YggdrasilTask::getStateMessage(); +	} +} diff --git a/api/logic/minecraft/auth/flows/AuthenticateTask.h b/api/logic/minecraft/auth/flows/AuthenticateTask.h new file mode 100644 index 00000000..398fab98 --- /dev/null +++ b/api/logic/minecraft/auth/flows/AuthenticateTask.h @@ -0,0 +1,46 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "../YggdrasilTask.h" + +#include <QObject> +#include <QString> +#include <QJsonObject> + +/** + * The authenticate task takes a MojangAccount with no access token and password and attempts to + * authenticate with Mojang's servers. + * If successful, it will set the MojangAccount's access token. + */ +class AuthenticateTask : public YggdrasilTask +{ +	Q_OBJECT +public: +	AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0); + +protected: +	virtual QJsonObject getRequestContent() const override; + +	virtual QString getEndpoint() const override; + +	virtual void processResponse(QJsonObject responseData) override; + +	virtual QString getStateMessage() const override; + +private: +	QString m_password; +}; diff --git a/api/logic/minecraft/auth/flows/RefreshTask.cpp b/api/logic/minecraft/auth/flows/RefreshTask.cpp new file mode 100644 index 00000000..a0fb2e48 --- /dev/null +++ b/api/logic/minecraft/auth/flows/RefreshTask.cpp @@ -0,0 +1,144 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RefreshTask.h" +#include "../MojangAccount.h" + +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> +#include <QVariant> + +#include <QDebug> + +RefreshTask::RefreshTask(MojangAccount *account) : YggdrasilTask(account) +{ +} + +QJsonObject RefreshTask::getRequestContent() const +{ +	/* +	 * { +	 *  "clientToken": "client identifier" +	 *  "accessToken": "current access token to be refreshed" +	 *  "selectedProfile":                      // specifying this causes errors +	 *  { +	 *   "id": "profile ID" +	 *   "name": "profile name" +	 *  } +	 *  "requestUser": true/false               // request the user structure +	 * } +	 */ +	QJsonObject req; +	req.insert("clientToken", m_account->m_clientToken); +	req.insert("accessToken", m_account->m_accessToken); +	/* +	{ +		auto currentProfile = m_account->currentProfile(); +		QJsonObject profile; +		profile.insert("id", currentProfile->id()); +		profile.insert("name", currentProfile->name()); +		req.insert("selectedProfile", profile); +	} +	*/ +	req.insert("requestUser", true); + +	return req; +} + +void RefreshTask::processResponse(QJsonObject responseData) +{ +	// Read the response data. We need to get the client token, access token, and the selected +	// profile. +	qDebug() << "Processing authentication response."; + +	// qDebug() << responseData; +	// If we already have a client token, make sure the one the server gave us matches our +	// existing one. +	QString clientToken = responseData.value("clientToken").toString(""); +	if (clientToken.isEmpty()) +	{ +		// Fail if the server gave us an empty client token +		changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token.")); +		return; +	} +	if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken) +	{ +		changeState(STATE_FAILED_HARD, tr("Authentication server attempted to change the client token. This isn't supported.")); +		return; +	} + +	// Now, we set the access token. +	qDebug() << "Getting new access token."; +	QString accessToken = responseData.value("accessToken").toString(""); +	if (accessToken.isEmpty()) +	{ +		// Fail if the server didn't give us an access token. +		changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token.")); +		return; +	} + +	// we validate that the server responded right. (our current profile = returned current +	// profile) +	QJsonObject currentProfile = responseData.value("selectedProfile").toObject(); +	QString currentProfileId = currentProfile.value("id").toString(""); +	if (m_account->currentProfile()->id != currentProfileId) +	{ +		changeState(STATE_FAILED_HARD, tr("Authentication server didn't specify the same prefile as expected.")); +		return; +	} + +	// this is what the vanilla launcher passes to the userProperties launch param +	if (responseData.contains("user")) +	{ +		User u; +		auto obj = responseData.value("user").toObject(); +		u.id = obj.value("id").toString(); +		auto propArray = obj.value("properties").toArray(); +		for (auto prop : propArray) +		{ +			auto propTuple = prop.toObject(); +			auto name = propTuple.value("name").toString(); +			auto value = propTuple.value("value").toString(); +			u.properties.insert(name, value); +		} +		m_account->m_user = u; +	} + +	// We've made it through the minefield of possible errors. Return true to indicate that +	// we've succeeded. +	qDebug() << "Finished reading refresh response."; +	// Reset the access token. +	m_account->m_accessToken = accessToken; +	changeState(STATE_SUCCEEDED); +} + +QString RefreshTask::getEndpoint() const +{ +	return "refresh"; +} + +QString RefreshTask::getStateMessage() const +{ +	switch (m_state) +	{ +	case STATE_SENDING_REQUEST: +		return tr("Refreshing login token..."); +	case STATE_PROCESSING_RESPONSE: +		return tr("Refreshing login token: Processing response..."); +	default: +		return YggdrasilTask::getStateMessage(); +	} +} diff --git a/api/logic/minecraft/auth/flows/RefreshTask.h b/api/logic/minecraft/auth/flows/RefreshTask.h new file mode 100644 index 00000000..17714b4f --- /dev/null +++ b/api/logic/minecraft/auth/flows/RefreshTask.h @@ -0,0 +1,44 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "../YggdrasilTask.h" + +#include <QObject> +#include <QString> +#include <QJsonObject> + +/** + * The authenticate task takes a MojangAccount with a possibly timed-out access token + * and attempts to authenticate with Mojang's servers. + * If successful, it will set the new access token. The token is considered validated. + */ +class RefreshTask : public YggdrasilTask +{ +	Q_OBJECT +public: +	RefreshTask(MojangAccount * account); + +protected: +	virtual QJsonObject getRequestContent() const override; + +	virtual QString getEndpoint() const override; + +	virtual void processResponse(QJsonObject responseData) override; + +	virtual QString getStateMessage() const override; +}; + diff --git a/api/logic/minecraft/auth/flows/ValidateTask.cpp b/api/logic/minecraft/auth/flows/ValidateTask.cpp new file mode 100644 index 00000000..4deceb6a --- /dev/null +++ b/api/logic/minecraft/auth/flows/ValidateTask.cpp @@ -0,0 +1,61 @@ + +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ValidateTask.h" +#include "../MojangAccount.h" + +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> +#include <QVariant> + +#include <QDebug> + +ValidateTask::ValidateTask(MojangAccount * account, QObject *parent) +	: YggdrasilTask(account, parent) +{ +} + +QJsonObject ValidateTask::getRequestContent() const +{ +	QJsonObject req; +	req.insert("accessToken", m_account->m_accessToken); +	return req; +} + +void ValidateTask::processResponse(QJsonObject responseData) +{ +	// Assume that if processError wasn't called, then the request was successful. +	changeState(YggdrasilTask::STATE_SUCCEEDED); +} + +QString ValidateTask::getEndpoint() const +{ +	return "validate"; +} + +QString ValidateTask::getStateMessage() const +{ +	switch (m_state) +	{ +	case YggdrasilTask::STATE_SENDING_REQUEST: +		return tr("Validating access token: Sending request..."); +	case YggdrasilTask::STATE_PROCESSING_RESPONSE: +		return tr("Validating access token: Processing response..."); +	default: +		return YggdrasilTask::getStateMessage(); +	} +} diff --git a/api/logic/minecraft/auth/flows/ValidateTask.h b/api/logic/minecraft/auth/flows/ValidateTask.h new file mode 100644 index 00000000..77d628a0 --- /dev/null +++ b/api/logic/minecraft/auth/flows/ValidateTask.h @@ -0,0 +1,47 @@ +/* Copyright 2013-2015 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * :FIXME: DEAD CODE, DEAD CODE, DEAD CODE! :FIXME: + */ + +#pragma once + +#include "../YggdrasilTask.h" + +#include <QObject> +#include <QString> +#include <QJsonObject> + +/** + * The validate task takes a MojangAccount and checks to make sure its access token is valid. + */ +class ValidateTask : public YggdrasilTask +{ +	Q_OBJECT +public: +	ValidateTask(MojangAccount *account, QObject *parent = 0); + +protected: +	virtual QJsonObject getRequestContent() const override; + +	virtual QString getEndpoint() const override; + +	virtual void processResponse(QJsonObject responseData) override; + +	virtual QString getStateMessage() const override; + +private: +}; | 
