Verify access tokens before launching Minecraft
Kind of an important thing to do... Heh...
This commit is contained in:
		
							
								
								
									
										66
									
								
								logic/auth/ValidateTask.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								logic/auth/ValidateTask.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
|  | ||||
| /* Copyright 2013 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 <logic/auth/ValidateTask.h> | ||||
|  | ||||
| #include <logic/auth/MojangAccount.h> | ||||
|  | ||||
| #include <QJsonDocument> | ||||
| #include <QJsonObject> | ||||
| #include <QJsonArray> | ||||
| #include <QVariant> | ||||
| #include <QDebug> | ||||
|  | ||||
| #include "logger/QsLog.h" | ||||
|  | ||||
| ValidateTask::ValidateTask(MojangAccountPtr account, QObject* parent) : | ||||
| 	YggdrasilTask(account, parent) | ||||
| { | ||||
| } | ||||
|  | ||||
| QJsonObject ValidateTask::getRequestContent() const | ||||
| { | ||||
| 	QJsonObject req; | ||||
| 	req.insert("accessToken", getMojangAccount()->accessToken()); | ||||
| 	return req; | ||||
| } | ||||
|  | ||||
| bool ValidateTask::processResponse(QJsonObject responseData) | ||||
| { | ||||
| 	// Assume that if processError wasn't called, then the request was successful. | ||||
| 	emitSucceeded(); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| QString ValidateTask::getEndpoint() const | ||||
| { | ||||
| 	return "validate"; | ||||
| } | ||||
|  | ||||
| QString ValidateTask::getStateMessage(const YggdrasilTask::State state) const | ||||
| { | ||||
| 	switch (state) | ||||
| 	{ | ||||
| 	case STATE_SENDING_REQUEST: | ||||
| 		return tr("Validating Access Token: Sending request."); | ||||
| 	case STATE_PROCESSING_RESPONSE: | ||||
| 		return tr("Validating Access Token: Processing response."); | ||||
| 	default: | ||||
| 		return YggdrasilTask::getStateMessage(state); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										44
									
								
								logic/auth/ValidateTask.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								logic/auth/ValidateTask.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| /* Copyright 2013 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 <logic/auth/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(MojangAccountPtr account, QObject* parent=0); | ||||
|  | ||||
| protected: | ||||
| 	virtual QJsonObject getRequestContent() const; | ||||
| 	 | ||||
| 	virtual QString getEndpoint() const; | ||||
|  | ||||
| 	virtual bool processResponse(QJsonObject responseData); | ||||
| 	 | ||||
| 	QString getStateMessage(const YggdrasilTask::State state) const; | ||||
|  | ||||
| private: | ||||
| }; | ||||
|  | ||||
| @@ -64,78 +64,65 @@ void YggdrasilTask::processReply(QNetworkReply* reply) | ||||
| 		// Wrong reply for some reason... | ||||
| 		return; | ||||
|  | ||||
| 	// Check for errors. | ||||
| 	switch (reply->error()) | ||||
| 	if (reply->error() == QNetworkReply::OperationCanceledError) | ||||
| 	{ | ||||
| 		case QNetworkReply::NoError: | ||||
| 		emitFailed("Yggdrasil task cancelled."); | ||||
| 		return; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		// Try to parse the response regardless of the response code. | ||||
| 		// Sometimes the auth server will give more information and an error code. | ||||
| 		QJsonParseError jsonError; | ||||
| 		QByteArray replyData = reply->readAll(); | ||||
| 		QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); | ||||
|  | ||||
| 		// Check the response code. | ||||
| 		int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); | ||||
|  | ||||
| 		if (responseCode == 200) | ||||
| 		{ | ||||
| 			// Try to parse the response regardless of the response code. | ||||
| 			// Sometimes the auth server will give more information and an error code. | ||||
| 			QJsonParseError jsonError; | ||||
| 			QByteArray replyData = reply->readAll(); | ||||
| 			QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); | ||||
|  | ||||
| 			// Check the response code. | ||||
| 			int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); | ||||
|  | ||||
| 			switch (responseCode) | ||||
| 			// If the response code was 200, then there shouldn't be an error. Make sure anyways. | ||||
| 			// Also, sometimes an empty reply indicates success. If there was no data received,  | ||||
| 			// pass an empty json object to the processResponse function. | ||||
| 			if (jsonError.error  == QJsonParseError::NoError || replyData.size() == 0) | ||||
| 			{ | ||||
| 				case 200: | ||||
| 				if (!processResponse(replyData.size() > 0 ? doc.object() : QJsonObject())) | ||||
| 				{ | ||||
| 					// If the response code was 200, then there shouldn't be an error. Make sure anyways. | ||||
| 					switch (jsonError.error) | ||||
| 					{ | ||||
| 						case QJsonParseError::NoError: | ||||
| 							if (!processResponse(doc.object())) | ||||
| 							{ | ||||
| 								YggdrasilTask::Error* err = getError(); | ||||
| 								if (err) | ||||
| 									emitFailed(err->getErrorMessage()); | ||||
| 								else | ||||
| 									emitFailed(tr("An unknown error occurred when processing the response from the authentication server.")); | ||||
| 							} | ||||
| 							else | ||||
| 							{ | ||||
| 								emitSucceeded(); | ||||
| 							} | ||||
| 						break; | ||||
|  | ||||
| 						default: | ||||
| 						emitFailed(tr("Failed to parse Yggdrasil JSON response: \"%1\".").arg(jsonError.errorString())); | ||||
| 						break; | ||||
| 					} | ||||
| 					break; | ||||
| 					YggdrasilTask::Error* err = getError(); | ||||
| 					if (err) | ||||
| 						emitFailed(err->getErrorMessage()); | ||||
| 					else | ||||
| 						emitFailed(tr("An unknown error occurred when processing the response from the authentication server.")); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					emitSucceeded(); | ||||
| 				} | ||||
|  | ||||
| 				default: | ||||
| 					// If the response code was something else, then Yggdrasil may have given us information about the error. | ||||
| 					// If we can parse the response, then get information from it. Otherwise just say there was an unknown error. | ||||
| 					switch (jsonError.error) | ||||
| 					{ | ||||
| 						case QJsonParseError::NoError: | ||||
| 							// We were able to parse the server's response. Woo! | ||||
| 							// Call processError. If a subclass has overridden it then they'll handle their stuff there. | ||||
| 							processError(doc.object()); | ||||
| 							break; | ||||
|  | ||||
| 						default: | ||||
| 							// The server didn't say anything regarding the error. Give the user an unknown error. | ||||
| 							emitFailed(tr("Login failed: Unknown HTTP code %1 encountered.").arg(responseCode)); | ||||
| 							break; | ||||
| 					} | ||||
| 					break; | ||||
| 			} | ||||
|  | ||||
| 			break; | ||||
| 			else | ||||
| 			{ | ||||
| 				emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.").arg(jsonError.errorString()).arg(jsonError.offset)); | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			// If the response code was not 200, then Yggdrasil may have given us information about the error. | ||||
| 			// If we can parse the response, then get information from it. Otherwise just say there was an unknown error. | ||||
| 			if (jsonError.error == QJsonParseError::NoError) | ||||
| 			{ | ||||
| 				// We were able to parse the server's response. Woo! | ||||
| 				// Call processError. If a subclass has overridden it then they'll handle their stuff there. | ||||
| 				QLOG_DEBUG() << "The request failed, but the server gave us an error message. Processing error."; | ||||
| 				emitFailed(processError(doc.object())); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				// The server didn't say anything regarding the error. Give the user an unknown error. | ||||
| 				QLOG_DEBUG() << "The request failed and the server gave no error message. Unknown error."; | ||||
| 				emitFailed(tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(reply->errorString())); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		case QNetworkReply::OperationCanceledError: | ||||
| 			emitFailed(tr("Login canceled.")); | ||||
| 			break; | ||||
|  | ||||
| 		default: | ||||
| 			emitFailed(tr("An unknown error occurred when trying to communicate with the authentication server.")); | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -145,7 +132,7 @@ QString YggdrasilTask::processError(QJsonObject responseData) | ||||
| 	QJsonValue msgVal = responseData.value("errorMessage"); | ||||
| 	QJsonValue causeVal = responseData.value("cause"); | ||||
|  | ||||
| 	if (errorVal.isString() && msgVal.isString() && causeVal.isString()) | ||||
| 	if (errorVal.isString() && msgVal.isString()) | ||||
| 	{ | ||||
| 		m_error = new Error(errorVal.toString(""), msgVal.toString(""), causeVal.toString("")); | ||||
| 		return m_error->getDisplayMessage(); | ||||
|   | ||||
| @@ -99,6 +99,8 @@ protected: | ||||
| 	 * If an error occurred, this should emit a failed signal and return false. | ||||
| 	 * If Yggdrasil gave an error response, it should call setError() first, and then return false. | ||||
| 	 * Otherwise, it should return true. | ||||
| 	 * Note: If the response from the server was blank, and the HTTP code was 200, this function is called with | ||||
| 	 * an empty QJsonObject. | ||||
| 	 */ | ||||
| 	virtual bool processResponse(QJsonObject responseData) = 0; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user