Skip to content

Commit

Permalink
Improved API error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
RaphiMC committed Dec 25, 2023
1 parent c4bccc0 commit 78e4b82
Show file tree
Hide file tree
Showing 14 changed files with 217 additions and 19 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ if (!isAvailable) {
} catch (CompletionException e) {
if (e.getCause() instanceof RealmsResponseException) {
RealmsResponseException exception = (RealmsResponseException) e.getCause();
if (exception.getRealmsErrorCode() == RealmsResponseException.TOS_NOT_ACCEPTED) {
if (exception.getErrorCode() == RealmsResponseException.TOS_NOT_ACCEPTED) {
// The Java Edition Realms API requires users to accept the Minecraft Realms Terms of Service (https://aka.ms/MinecraftRealmsTerms)
// You should display the terms to the user and ask them to accept them:
javaRealmsService.acceptTos().join();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* This file is part of MinecraftAuth - https://github.com/RaphiMC/MinecraftAuth
* Copyright (C) 2023 RK_01/RaphiMC and contributors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.raphimc.minecraftauth.responsehandler;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

public class DebugResponseHandler implements ResponseHandler<String> {

@Override
public String handleResponse(HttpResponse response) throws IOException {
final StatusLine statusLine = response.getStatusLine();
final HttpEntity entity = response.getEntity();
final String body = entity == null ? null : EntityUtils.toString(entity);
if (statusLine.getStatusCode() >= 300) {
throw new HttpResponseException(statusLine.getStatusCode(), body);
}
return body;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* This file is part of MinecraftAuth - https://github.com/RaphiMC/MinecraftAuth
* Copyright (C) 2023 RK_01/RaphiMC and contributors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.raphimc.minecraftauth.responsehandler;

import com.google.gson.JsonObject;
import net.raphimc.minecraftauth.responsehandler.exception.MinecraftResponseException;
import net.raphimc.minecraftauth.util.JsonUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.entity.ContentType;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

public class MinecraftResponseHandler implements ResponseHandler<String> {

@Override
public String handleResponse(HttpResponse response) throws IOException {
final StatusLine statusLine = response.getStatusLine();
final HttpEntity entity = response.getEntity();
final String body = entity == null ? null : EntityUtils.toString(entity);
if (statusLine.getStatusCode() >= 300) {
if (body != null && ContentType.getOrDefault(entity).getMimeType().equals(ContentType.APPLICATION_JSON.getMimeType())) {
final JsonObject obj = (JsonObject) JsonUtil.parseString(body);
if (obj.has("error") && obj.has("errorMessage")) {
throw new MinecraftResponseException(statusLine.getStatusCode(), obj.get("error").getAsString(), obj.get("errorMessage").getAsString());
}
}
throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
}
return body;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package net.raphimc.minecraftauth.responsehandler;

import com.google.gson.JsonObject;
import net.raphimc.minecraftauth.responsehandler.exception.MsaResponseException;
import net.raphimc.minecraftauth.util.JsonUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
Expand All @@ -40,7 +41,7 @@ public String handleResponse(HttpResponse response) throws IOException {
if (body != null && ContentType.getOrDefault(entity).getMimeType().equals(ContentType.APPLICATION_JSON.getMimeType())) {
final JsonObject obj = (JsonObject) JsonUtil.parseString(body);
if (obj.has("error") && obj.has("error_description")) {
throw new HttpResponseException(statusLine.getStatusCode(), obj.get("error").getAsString() + ": " + obj.get("error_description").getAsString());
throw new MsaResponseException(statusLine.getStatusCode(), obj.get("error").getAsString(), obj.get("error_description").getAsString());
}
}
throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package net.raphimc.minecraftauth.responsehandler;

import com.google.gson.JsonObject;
import net.raphimc.minecraftauth.responsehandler.exception.PlayFabResponseException;
import net.raphimc.minecraftauth.util.JsonUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
Expand All @@ -40,7 +41,7 @@ public String handleResponse(HttpResponse response) throws IOException {
if (body != null && ContentType.getOrDefault(entity).getMimeType().equals(ContentType.APPLICATION_JSON.getMimeType())) {
final JsonObject obj = (JsonObject) JsonUtil.parseString(body);
if (obj.has("error") && obj.has("errorMessage")) {
throw new HttpResponseException(statusLine.getStatusCode(), obj.get("error").getAsString() + ": " + obj.get("errorMessage").getAsString());
throw new PlayFabResponseException(statusLine.getStatusCode(), obj.get("error").getAsString(), obj.get("errorMessage").getAsString());
}
}
throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* This file is part of MinecraftAuth - https://github.com/RaphiMC/MinecraftAuth
* Copyright (C) 2023 RK_01/RaphiMC and contributors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.raphimc.minecraftauth.responsehandler.exception;

import lombok.Getter;
import org.apache.http.client.HttpResponseException;

@Getter
public class MinecraftResponseException extends HttpResponseException {

private final String error;

public MinecraftResponseException(final int statusCode, final String error, final String reasonPhrase) {
super(statusCode, reasonPhrase + ", minecraft error: " + error);

this.error = error;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* This file is part of MinecraftAuth - https://github.com/RaphiMC/MinecraftAuth
* Copyright (C) 2023 RK_01/RaphiMC and contributors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.raphimc.minecraftauth.responsehandler.exception;

import lombok.Getter;
import org.apache.http.client.HttpResponseException;

@Getter
public class MsaResponseException extends HttpResponseException {

private final String error;

public MsaResponseException(final int statusCode, final String error, final String reasonPhrase) {
super(statusCode, reasonPhrase + ", msa error: " + error);

this.error = error;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* This file is part of MinecraftAuth - https://github.com/RaphiMC/MinecraftAuth
* Copyright (C) 2023 RK_01/RaphiMC and contributors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.raphimc.minecraftauth.responsehandler.exception;

import lombok.Getter;
import org.apache.http.client.HttpResponseException;

@Getter
public class PlayFabResponseException extends HttpResponseException {

private final String error;

public PlayFabResponseException(final int statusCode, final String error, final String reasonPhrase) {
super(statusCode, reasonPhrase + ", playfab error: " + error);

this.error = error;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ public class RealmsResponseException extends HttpResponseException {

public static final int TOS_NOT_ACCEPTED = 6002;

private final int realmsErrorCode;
private final int errorCode;

public RealmsResponseException(final int statusCode, final int realmsErrorCode, final String reasonPhrase) {
super(statusCode, reasonPhrase + ", realms error code: " + realmsErrorCode);
public RealmsResponseException(final int statusCode, final int errorCode, final String reasonPhrase) {
super(statusCode, reasonPhrase + ", realms error code: " + errorCode);

this.realmsErrorCode = realmsErrorCode;
this.errorCode = errorCode;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ public class XblResponseException extends HttpResponseException {
ERROR_CODES.put(2148916238L, "The account date of birth is under 18 years and cannot proceed unless the account is added to a family by an adult.");
}

private final long xblErrorCode;
private final long errorCode;

public XblResponseException(final int statusCode, final long xblErrorCode, final String reasonPhrase) {
super(statusCode, reasonPhrase + ", xbox live error code: " + xblErrorCode);
public XblResponseException(final int statusCode, final long errorCode, final String reasonPhrase) {
super(statusCode, reasonPhrase + ", xbox live error code: " + errorCode);

this.xblErrorCode = xblErrorCode;
this.errorCode = errorCode;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import lombok.EqualsAndHashCode;
import lombok.Value;
import net.raphimc.minecraftauth.MinecraftAuth;
import net.raphimc.minecraftauth.responsehandler.MinecraftResponseHandler;
import net.raphimc.minecraftauth.step.AbstractStep;
import net.raphimc.minecraftauth.step.xbl.StepXblXstsToken;
import net.raphimc.minecraftauth.util.CryptUtil;
Expand All @@ -34,7 +35,6 @@
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
Expand Down Expand Up @@ -73,7 +73,7 @@ public MCChain applyStep(final HttpClient httpClient, final StepXblXstsToken.Xbl
final HttpPost httpPost = new HttpPost(MINECRAFT_LOGIN_URL);
httpPost.setEntity(new StringEntity(postData.toString(), ContentType.APPLICATION_JSON));
httpPost.addHeader(HttpHeaders.AUTHORIZATION, "XBL3.0 x=" + xblXsts.getServiceToken());
final String response = httpClient.execute(httpPost, new BasicResponseHandler());
final String response = httpClient.execute(httpPost, new MinecraftResponseHandler());
final JsonObject obj = JsonUtil.parseString(response).getAsJsonObject();
final JsonArray chain = obj.get("chain").getAsJsonArray();
if (chain.size() != 2) throw new IllegalStateException("Invalid chain size");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@
import lombok.EqualsAndHashCode;
import lombok.Value;
import net.raphimc.minecraftauth.MinecraftAuth;
import net.raphimc.minecraftauth.responsehandler.MinecraftResponseHandler;
import net.raphimc.minecraftauth.step.AbstractStep;
import net.raphimc.minecraftauth.util.JsonUtil;
import net.raphimc.minecraftauth.util.UuidUtil;
import org.apache.http.HttpHeaders;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;

import java.io.IOException;
import java.util.UUID;
Expand All @@ -46,7 +46,7 @@ public MCProfile applyStep(final HttpClient httpClient, final StepMCToken.MCToke

final HttpGet httpGet = new HttpGet(MINECRAFT_PROFILE_URL);
httpGet.addHeader(HttpHeaders.AUTHORIZATION, mcToken.getTokenType() + " " + mcToken.getAccessToken());
final String response = httpClient.execute(httpGet, new BasicResponseHandler());
final String response = httpClient.execute(httpGet, new MinecraftResponseHandler());
final JsonObject obj = JsonUtil.parseString(response).getAsJsonObject();

if (obj.has("error")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
import lombok.EqualsAndHashCode;
import lombok.Value;
import net.raphimc.minecraftauth.MinecraftAuth;
import net.raphimc.minecraftauth.responsehandler.MinecraftResponseHandler;
import net.raphimc.minecraftauth.step.AbstractStep;
import net.raphimc.minecraftauth.step.xbl.StepXblXstsToken;
import net.raphimc.minecraftauth.util.JsonUtil;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;

import java.time.Instant;
import java.time.ZoneId;
Expand All @@ -50,7 +50,7 @@ public MCToken applyStep(final HttpClient httpClient, final StepXblXstsToken.Xbl

final HttpPost httpPost = new HttpPost(MINECRAFT_LOGIN_URL);
httpPost.setEntity(new StringEntity(postData.toString(), ContentType.APPLICATION_JSON));
final String response = httpClient.execute(httpPost, new BasicResponseHandler());
final String response = httpClient.execute(httpPost, new MinecraftResponseHandler());
final JsonObject obj = JsonUtil.parseString(response).getAsJsonObject();

final MCToken mcToken = new MCToken(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import lombok.EqualsAndHashCode;
import lombok.Value;
import net.raphimc.minecraftauth.MinecraftAuth;
import net.raphimc.minecraftauth.responsehandler.MinecraftResponseHandler;
import net.raphimc.minecraftauth.step.AbstractStep;
import net.raphimc.minecraftauth.util.CryptUtil;
import net.raphimc.minecraftauth.util.JsonUtil;
Expand All @@ -29,7 +30,6 @@
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicResponseHandler;

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
Expand All @@ -54,7 +54,7 @@ public PlayerCertificates applyStep(final HttpClient httpClient, final StepMCTok
final HttpPost httpPost = new HttpPost(PLAYER_CERTIFICATES_URL);
httpPost.setEntity(new StringEntity("", ContentType.APPLICATION_JSON));
httpPost.addHeader(HttpHeaders.AUTHORIZATION, mcToken.getTokenType() + " " + mcToken.getAccessToken());
final String response = httpClient.execute(httpPost, new BasicResponseHandler());
final String response = httpClient.execute(httpPost, new MinecraftResponseHandler());
final JsonObject obj = JsonUtil.parseString(response).getAsJsonObject();
final JsonObject keyPair = obj.getAsJsonObject("keyPair");

Expand Down

0 comments on commit 78e4b82

Please sign in to comment.