Skip to content

Commit

Permalink
Refactored MSA oAuth flow
Browse files Browse the repository at this point in the history
  • Loading branch information
RaphiMC committed Dec 26, 2023
1 parent 9e4c6a4 commit f566300
Show file tree
Hide file tree
Showing 14 changed files with 160 additions and 131 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ org.gradle.configureondemand=true

maven_group=net.raphimc
maven_name=MinecraftAuth
maven_version=3.0.3-SNAPSHOT
maven_version=3.1.0-SNAPSHOT
41 changes: 19 additions & 22 deletions src/main/java/net/raphimc/minecraftauth/MinecraftAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,8 @@ public static MsaTokenBuilder builder() {

public static class MsaTokenBuilder {

private String clientId = MicrosoftConstants.JAVA_TITLE_ID;
private String scope = MicrosoftConstants.SCOPE1;
private String clientSecret = null;
private MsaCodeStep.ApplicationDetails applicationDetails = new MsaCodeStep.ApplicationDetails(MicrosoftConstants.JAVA_TITLE_ID, MicrosoftConstants.SCOPE1, null, null);
private int timeout = 120;
private String redirectUri = null;

private AbstractStep<?, MsaCodeStep.MsaCode> msaCodeStep;

Expand All @@ -92,7 +89,7 @@ public static class MsaTokenBuilder {
* @return The builder
*/
public MsaTokenBuilder withClientId(final String clientId) {
this.clientId = clientId;
this.applicationDetails = this.applicationDetails.withClientId(clientId);

return this;
}
Expand All @@ -104,7 +101,7 @@ public MsaTokenBuilder withClientId(final String clientId) {
* @return The builder
*/
public MsaTokenBuilder withScope(final String scope) {
this.scope = scope;
this.applicationDetails = this.applicationDetails.withScope(scope);

return this;
}
Expand All @@ -116,31 +113,31 @@ public MsaTokenBuilder withScope(final String scope) {
* @return The builder
*/
public MsaTokenBuilder withClientSecret(final String clientSecret) {
this.clientSecret = clientSecret;
this.applicationDetails = this.applicationDetails.withClientSecret(clientSecret);

return this;
}

/**
* Sets the timeout of the device code or local webserver auth flow
* Sets the redirect uri to use for the local webserver or credentials auth flow
*
* @param timeout The timeout in seconds
* @param redirectUri The redirect uri
* @return The builder
*/
public MsaTokenBuilder withTimeout(final int timeout) {
this.timeout = timeout;
public MsaTokenBuilder withRedirectUri(final String redirectUri) {
this.applicationDetails = this.applicationDetails.withRedirectUri(redirectUri);

return this;
}

/**
* Sets the redirect uri to use for the local webserver or credentials auth flow
* Sets the timeout of the device code or local webserver auth flow
*
* @param redirectUri The redirect uri
* @param timeout The timeout in seconds
* @return The builder
*/
public MsaTokenBuilder withRedirectUri(final String redirectUri) {
this.redirectUri = redirectUri;
public MsaTokenBuilder withTimeout(final int timeout) {
this.timeout = timeout;

return this;
}
Expand All @@ -152,7 +149,7 @@ public MsaTokenBuilder withRedirectUri(final String redirectUri) {
* @return The builder
*/
public InitialXblSessionBuilder deviceCode() {
this.msaCodeStep = new StepMsaDeviceCodeMsaCode(new StepMsaDeviceCode(this.clientId, this.scope), this.clientId, this.scope, this.clientSecret, this.timeout * 1000);
this.msaCodeStep = new StepMsaDeviceCodeMsaCode(new StepMsaDeviceCode(this.applicationDetails), this.timeout * 1000);

return new InitialXblSessionBuilder(this);
}
Expand All @@ -164,11 +161,11 @@ public InitialXblSessionBuilder deviceCode() {
* @return The builder
*/
public InitialXblSessionBuilder localWebServer() {
if (this.redirectUri == null) {
this.redirectUri = "http://localhost";
if (this.applicationDetails.getRedirectUri() == null) {
this.applicationDetails = this.applicationDetails.withRedirectUri("http://localhost");
}

this.msaCodeStep = new StepLocalWebServerMsaCode(new StepLocalWebServer(this.clientId, this.scope, this.redirectUri), this.clientId, this.scope, this.clientSecret, this.timeout * 1000);
this.msaCodeStep = new StepLocalWebServerMsaCode(new StepLocalWebServer(this.applicationDetails), this.timeout * 1000);

return new InitialXblSessionBuilder(this);
}
Expand All @@ -180,11 +177,11 @@ public InitialXblSessionBuilder localWebServer() {
* @return The builder
*/
public InitialXblSessionBuilder credentials() {
if (this.redirectUri == null) {
this.redirectUri = MicrosoftConstants.LIVE_OAUTH_DESKTOP_URL;
if (this.applicationDetails.getRedirectUri() == null) {
this.applicationDetails = this.applicationDetails.withRedirectUri(MicrosoftConstants.LIVE_OAUTH_DESKTOP_URL);
}

this.msaCodeStep = new StepCredentialsMsaCode(this.clientId, this.scope, this.clientSecret, this.redirectUri);
this.msaCodeStep = new StepCredentialsMsaCode(this.applicationDetails);

return new InitialXblSessionBuilder(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ public MCChain applyStep(final HttpClient httpClient, final StepXblXstsToken.Xbl
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");
if (chain.size() != 2) {
throw new IllegalStateException("Invalid chain size");
}

final Jws<Claims> mojangJwt = Jwts.parser().clockSkewSeconds(CLOCK_SKEW).verifyWith(MOJANG_PUBLIC_KEY).build().parseSignedClaims(chain.get(0).getAsString());
final ECPublicKey mojangJwtPublicKey = CryptUtil.publicKeyEcFromBase64(mojangJwt.getPayload().get("identityPublicKey", String.class));
Expand Down
59 changes: 35 additions & 24 deletions src/main/java/net/raphimc/minecraftauth/step/msa/MsaCodeStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,58 +20,69 @@
import com.google.gson.JsonObject;
import lombok.EqualsAndHashCode;
import lombok.Value;
import lombok.With;
import net.raphimc.minecraftauth.step.AbstractStep;
import org.apache.http.client.HttpClient;
import net.raphimc.minecraftauth.util.JsonUtil;
import net.raphimc.minecraftauth.util.UuidUtil;

public abstract class MsaCodeStep<I extends AbstractStep.StepResult<?>> extends AbstractStep<I, MsaCodeStep.MsaCode> {

protected final String clientId;
protected final String scope;
protected final String clientSecret;

public MsaCodeStep(final AbstractStep<?, I> prevStep, final String clientId, final String scope, final String clientSecret) {
public MsaCodeStep(final AbstractStep<?, I> prevStep) {
super("msaCode", prevStep);

this.clientId = clientId;
this.scope = scope;
this.clientSecret = clientSecret;
}

@Override
public MsaCode applyStep(final HttpClient httpClient, final I prevResult) throws Exception {
throw new UnsupportedOperationException();
}

@Override
public MsaCode fromJson(final JsonObject json) {
return new MsaCode(
json.get("code").getAsString(),
json.get("clientId").getAsString(),
json.get("scope").getAsString(),
json.get("clientSecret") != null && !json.get("clientSecret").isJsonNull() ? json.get("clientSecret").getAsString() : null,
null);
new ApplicationDetails(
json.get("clientId").getAsString(),
json.get("scope").getAsString(),
JsonUtil.getStringOr(json, "clientSecret", null),
JsonUtil.getStringOr(json, "redirectUri", null)
)
);
}

@Override
public JsonObject toJson(final MsaCode msaCode) {
final JsonObject json = new JsonObject();
json.addProperty("code", msaCode.code);
json.addProperty("clientId", msaCode.clientId);
json.addProperty("scope", msaCode.scope);
json.addProperty("clientSecret", msaCode.clientSecret);
json.addProperty("clientId", msaCode.applicationDetails.clientId);
json.addProperty("scope", msaCode.applicationDetails.scope);
json.addProperty("clientSecret", msaCode.applicationDetails.clientSecret);
json.addProperty("redirectUri", msaCode.applicationDetails.redirectUri);
return json;
}

@Value
@With
@EqualsAndHashCode(callSuper = false)
public static class MsaCode extends AbstractStep.FirstStepResult {
public static class ApplicationDetails extends AbstractStep.FirstStepResult {

String code;
String clientId;
String scope;
String clientSecret;
String redirectUri;

public boolean isTitleClientId() {
return !UuidUtil.isDashedUuid(this.clientId);
}

}

@Value
@EqualsAndHashCode(callSuper = false)
public static class MsaCode extends AbstractStep.StepResult<ApplicationDetails> {

String code;
ApplicationDetails applicationDetails;

@Override
protected ApplicationDetails prevResult() {
return this.applicationDetails;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,21 @@ public class StepCredentialsMsaCode extends MsaCodeStep<StepCredentialsMsaCode.M
public static final String AUTHORIZE_URL = "https://login.live.com/oauth20_authorize.srf";
// public static final String AUTHORIZE_URL = "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize";

private final String redirectUri;
private final ApplicationDetails applicationDetails;

public StepCredentialsMsaCode(final String clientId, final String scope, final String redirectUri) {
this(clientId, scope, null, redirectUri);
}

public StepCredentialsMsaCode(final String clientId, final String scope, final String clientSecret, final String redirectUri) {
super(null, clientId, scope, clientSecret);
public StepCredentialsMsaCode(final ApplicationDetails applicationDetails) {
super(null);

this.redirectUri = redirectUri;
this.applicationDetails = applicationDetails;
}

@Override
public MsaCode applyStep(final HttpClient httpClient, final StepCredentialsMsaCode.MsaCredentials msaCredentials) throws Exception {
MinecraftAuth.LOGGER.info("Trying to get MSA Code using email and password...");

if (msaCredentials == null) throw new IllegalStateException("Missing StepCredentialsMsaCode.MsaCredentials input");
if (msaCredentials == null) {
throw new IllegalStateException("Missing StepCredentialsMsaCode.MsaCredentials input");
}

final BasicCookieStore cookieStore = new BasicCookieStore();
final HttpClientContext context = HttpClientContext.create();
Expand Down Expand Up @@ -92,17 +90,17 @@ public MsaCode applyStep(final HttpClient httpClient, final StepCredentialsMsaCo
httpPost.setEntity(new UrlEncodedFormEntity(postData, StandardCharsets.UTF_8));
final String code = httpClient.execute(httpPost, new MsaCredentialsResponseHandler(), context);

final MsaCode msaCode = new MsaCode(code, this.clientId, this.scope, this.clientSecret, this.redirectUri);
final MsaCode msaCode = new MsaCode(code, this.applicationDetails);
MinecraftAuth.LOGGER.info("Got MSA Code");
return msaCode;
}

private URI getAuthenticationUrl() throws URISyntaxException {
return new URIBuilder(AUTHORIZE_URL)
.setParameter("client_id", this.clientId)
.setParameter("redirect_uri", this.redirectUri)
.setParameter("client_id", this.applicationDetails.getClientId())
.setParameter("redirect_uri", this.applicationDetails.getRedirectUri())
.setParameter("response_type", "code")
.setParameter("scope", this.scope)
.setParameter("scope", this.applicationDetails.getScope())
.build();
}

Expand All @@ -114,7 +112,10 @@ public static class MsaCredentials extends AbstractStep.InitialInput {
String password;

public static MsaCredentials fromJson(final JsonObject json) {
return new MsaCredentials(json.get("email").getAsString(), json.get("password").getAsString());
return new MsaCredentials(
json.get("email").getAsString(),
json.get("password").getAsString()
);
}

public static JsonObject toJson(final MsaCredentials msaCredentials) {
Expand Down
Loading

0 comments on commit f566300

Please sign in to comment.