Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/game/OAuthServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,13 @@ public Response serve(IHTTPSession session) {
return newFixedLengthResponse(Response.Status.OK, "text/html; charset=UTF-8", html);
}

@Override
public void close() {
if (!future.isDone())
future.completeExceptionally(new AuthenticationException("OAuth server is closing"));
stop();
}
Comment on lines +166 to +171
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

建议在 close() 方法中调用 future.completeExceptionally。这样可以确保如果会话被外部关闭(例如在 waitFor() 阻塞时),等待的线程能够立即收到异常并退出,而不是无限期阻塞。由于 future 只能被完成一次,这不会影响正常的登录流程。

Suggested change
@Override
public void close() {
stop();
}
@Override
public void close() {
future.completeExceptionally(new AuthenticationException("Session closed"));
stop();
}


public static class Factory implements OAuth.Callback {
public final EventManager<GrantDeviceCodeEvent> onGrantDeviceCode = new EventManager<>();
public final EventManager<OpenBrowserEvent> onOpenBrowserAuthorizationCode = new EventManager<>();
Expand Down
67 changes: 35 additions & 32 deletions HMCLCore/src/main/java/org/jackhuang/hmcl/auth/OAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,37 +80,37 @@ public Result authenticate(GrantFlow grantFlow, Options options) throws Authenti
}

private Result authenticateAuthorizationCode(Options options) throws IOException, InterruptedException, JsonParseException, ExecutionException, AuthenticationException {
Session session = options.callback.startServer();

String codeVerifier = session.getCodeVerifier();
String state = session.getState();
String codeChallenge = generateCodeChallenge(codeVerifier);

options.callback.openBrowser(GrantFlow.AUTHORIZATION_CODE, NetworkUtils.withQuery(authorizationURL,
mapOf(pair("client_id", options.callback.getClientId()),
pair("response_type", "code"),
pair("redirect_uri", session.getRedirectURI()),
pair("scope", options.scope),
pair("prompt", "select_account"),
pair("code_challenge", codeChallenge),
pair("state", state),
pair("code_challenge_method", "S256")
)));
String code = session.waitFor();

// Authorization Code -> Token
AuthorizationResponse response = HttpRequest.POST(accessTokenURL)
.form(pair("client_id", options.callback.getClientId()),
pair("code", code),
pair("grant_type", "authorization_code"),
pair("code_verifier", codeVerifier),
pair("redirect_uri", session.getRedirectURI()),
pair("scope", options.scope))
.ignoreHttpCode()
.retry(5)
.getJson(AuthorizationResponse.class);
handleErrorResponse(response);
return new Result(response.accessToken, response.refreshToken);
try (Session session = options.callback.startServer()) {
String codeVerifier = session.getCodeVerifier();
String state = session.getState();
String codeChallenge = generateCodeChallenge(codeVerifier);

options.callback.openBrowser(GrantFlow.AUTHORIZATION_CODE, NetworkUtils.withQuery(authorizationURL,
mapOf(pair("client_id", options.callback.getClientId()),
pair("response_type", "code"),
pair("redirect_uri", session.getRedirectURI()),
pair("scope", options.scope),
pair("prompt", "select_account"),
pair("code_challenge", codeChallenge),
pair("state", state),
pair("code_challenge_method", "S256")
)));
String code = session.waitFor();

// Authorization Code -> Token
AuthorizationResponse response = HttpRequest.POST(accessTokenURL)
.form(pair("client_id", options.callback.getClientId()),
pair("code", code),
pair("grant_type", "authorization_code"),
pair("code_verifier", codeVerifier),
pair("redirect_uri", session.getRedirectURI()),
pair("scope", options.scope))
.ignoreHttpCode()
.retry(5)
.getJson(AuthorizationResponse.class);
handleErrorResponse(response);
return new Result(response.accessToken, response.refreshToken);
}
}

private Result authenticateDevice(Options options) throws IOException, InterruptedException, JsonParseException, AuthenticationException {
Expand Down Expand Up @@ -234,7 +234,7 @@ public Options setUserAgent(String userAgent) {
}
}

public interface Session {
public interface Session extends AutoCloseable {
String getState();

String getCodeVerifier();
Expand All @@ -253,6 +253,9 @@ public interface Session {
default String getIdToken() {
return null;
}

@Override
void close();
}

public interface Callback {
Expand Down