Skip to content

Conversation

@CiiLu
Copy link
Contributor

@CiiLu CiiLu commented Jan 25, 2026

Resolves #5135
界面参考自 PrismLauncher
image
image

@CiiLu CiiLu marked this pull request as ready for review January 25, 2026 11:56
@3gf8jv4dv
Copy link
Contributor

界面撑的太大了,标题栏不好点、拖动。

Image

@3gf8jv4dv
Copy link
Contributor

「复制链接并打开浏览器」我只看到了浏览器出现,但剪贴板没有新的东西。

CiiLu and others added 2 commits January 25, 2026 20:56
Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>
@WhatDamon
Copy link
Contributor

此 PR 若未添加 OAuth 信息,可能会导致进入此界面后无法退出当前对话框
截屏2026-01-25 21 59 52

请考虑像 #5297 添加一个警告信息,并提供按钮能够让用户返回

@WhatDamon
Copy link
Contributor

注意到 Prism Launcher 的逻辑是二维码包含登录所需的代码,内容为 https://www.microsoft.com/link?otc={登录代码},而本 PR 的二维码则用于快速打开 https://www.microsoft.com/link

如果要比较,Prism Launcher 的体验相对会更好,但如果 HMCL 也这么干,则需要引入一个二维码生成库例如 ZXing (Apache 2.0) 或者 nayuki/QR-Code-generator (MIT) ,是否有必要在这一方面进行取舍 🤔

@Glavo
Copy link
Member

Glavo commented Jan 26, 2026

注意到 Prism Launcher 的逻辑是二维码包含登录所需的代码,内容为 https://www.microsoft.com/link?otc={登录代码},而本 PR 的二维码则用于快速打开 https://www.microsoft.com/link

如果要比较,Prism Launcher 的体验相对会更好,但如果 HMCL 也这么干,则需要引入一个二维码生成库例如 ZXing (Apache 2.0) 或者 nayuki/QR-Code-generator (MIT) ,是否有必要在这一方面进行取舍 🤔

可以分成两步实现,这个 PR 先专心更新到授权代码流,其他部分我之后研究一下。二维码库我之前就在考虑引入了。

@3gf8jv4dv
Copy link
Contributor

3gf8jv4dv commented Jan 26, 2026

microsoft_login.png 可以用 optipng 进一步压缩。
我试了下,参数 optipng -o7 -zm1-9 -strip all <filepath> 可以从 2,303 字节压到 701 字节。

Comment on lines +128 to 130
account.methods.microsoft.manual=If the website fails to load, please visit %s manually in your browser.\n\
\n\
<b>If your internet connection is bad, it may cause web pages to load slowly or fail to load altogether. You may try again later or switch to a different internet connection.</b>
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
account.methods.microsoft.manual=If the website fails to load, please visit %s manually in your browser.\n\
\n\
<b>If your internet connection is bad, it may cause web pages to load slowly or fail to load altogether. You may try again later or switch to a different internet connection.</b>
account.methods.microsoft.manual=<b>If your internet connection is bad, it may cause web pages to load slowly or fail to load altogether.\nYou may try again later or switch to a different internet connection.</b>

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for the AUTHORIZATION_CODE authentication flow for Microsoft accounts alongside the existing DEVICE flow, improving the user experience by offering a more streamlined browser-based login option while maintaining the device code flow for cross-device scenarios. This implementation is inspired by PrismLauncher's approach.

Changes:

  • Added dual authentication flow support (AUTHORIZATION_CODE and DEVICE) with concurrent execution
  • Replaced OAuthAccountLoginDialog with a new MicrosoftAccountLoginPane that displays both login options
  • Updated HTML success page with modern styling using the application's theme system
  • Enhanced localization strings to support the new two-flow UI

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/OAuth.java Added flow type parameter to openBrowser callback to differentiate between flows
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftService.java Modified authenticate method to accept OAuth.GrantFlow parameter
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccount.java Updated constructor to use GrantFlow instead of CharacterSelector
HMCLCore/src/main/java/org/jackhuang/hmcl/auth/microsoft/MicrosoftAccountFactory.java Changed create method to pass GrantFlow from additionalData parameter
HMCL/src/main/java/org/jackhuang/hmcl/ui/account/MicrosoftAccountLoginPane.java New dialog that runs both auth flows concurrently and displays both login options
HMCL/src/main/java/org/jackhuang/hmcl/ui/account/OAuthAccountLoginDialog.java Removed old single-flow login dialog
HMCL/src/main/java/org/jackhuang/hmcl/ui/account/CreateAccountPane.java Updated to use new MicrosoftAccountLoginPane
HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListPage.java Updated to use new MicrosoftAccountLoginPane
HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogController.java Updated to use new MicrosoftAccountLoginPane for credential refresh
HMCL/src/main/java/org/jackhuang/hmcl/game/OAuthServer.java Split openBrowser events into separate handlers for each flow type
HMCL/src/main/resources/assets/microsoft_auth.html Enhanced success page with modern Material Design styling
HMCL/src/main/resources/assets/lang/*.properties Added localization for new two-flow login interface
HMCL/src/main/resources/assets/img/microsoft_login.png Added QR code placeholder image for device flow
HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java Minor whitespace formatting
Comments suppressed due to low confidence (1)

HMCL/src/main/java/org/jackhuang/hmcl/ui/account/MicrosoftAccountLoginPane.java:329

  • Extra blank line at the end of the file. The file should end with a single newline character.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
import org.jackhuang.hmcl.util.javafx.BindingMapping;

import java.awt.*;
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

Unused import 'java.awt.*' should be removed. This import was added but is not used anywhere in the file.

Suggested change
import java.awt.*;

Copilot uses AI. Check for mistakes.
lblErrorMessage.setText("");
lblErrorMessage.setVisible(true);
actions.setVisible(true);
actions.setVisible(true);
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

Duplicate line: 'actions.setVisible(false);' is called twice consecutively. One of these lines should be removed.

Suggested change
actions.setVisible(true);

Copilot uses AI. Check for mistakes.

import org.jackhuang.hmcl.auth.AccountFactory;
import org.jackhuang.hmcl.auth.AuthenticationException;
import org.jackhuang.hmcl.auth.CharacterSelector;
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The import 'org.jackhuang.hmcl.auth.CharacterSelector' is no longer used in this file and should be removed. The CharacterSelector parameter was replaced with OAuth.GrantFlow in the create method.

Copilot uses AI. Check for mistakes.
Comment on lines +273 to +280
ExceptionalConsumer<Exception, Exception> onFail = (e) -> runInFX(() -> {
if (!(e instanceof CancellationException)) {
errHintPane.setText(Accounts.localizeErrorMessage(e));
errHintPane.setVisible(true);
authMethodsContentBox.setVisible(false);
loginButtonSpinner.hideSpinner();
}
});
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The error handler doesn't hide the spinner when a CancellationException occurs. While this might be intentional (since the tasks are being cancelled), it could leave the UI in an inconsistent state if cancelAllTasks is called while an error is being displayed. Consider explicitly hiding the spinner in the cancelAllTasks method to ensure consistent UI state.

Copilot uses AI. Check for mistakes.
} catch (AuthenticationException e) {
errHintPane.setText(Accounts.localizeErrorMessage(e));
errHintPane.setVisible(true);
loginButtonSpinner.showSpinner();
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

Error handling issue: When loginCallback.accept(account.logIn()) throws an AuthenticationException on line 306, the code sets errHintPane visibility to true and shows the spinner on line 310, but it doesn't call cancelAllTasks(). This means the other background task (browser or device) continues running unnecessarily. Consider calling cancelAllTasks() before returning to clean up properly.

Suggested change
loginButtonSpinner.showSpinner();
loginButtonSpinner.showSpinner();
cancelAllTasks();

Copilot uses AI. Check for mistakes.
Comment on lines +240 to +243
* @param authorizationCode
* @param url OAuth url.
*/
void openBrowser(String url) throws IOException;
void openBrowser(GrantFlow authorizationCode, String url) throws IOException;
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The parameter name 'authorizationCode' is misleading. It should be named 'grantFlow' or 'flow' since it represents the type of grant flow being used (AUTHORIZATION_CODE or DEVICE), not an actual authorization code value. This makes the API confusing for developers who might expect this to be a code string.

Copilot uses AI. Check for mistakes.
Comment on lines +282 to +289
browserTaskExecuter = Task.supplyAsync(() -> Accounts.FACTORY_MICROSOFT.create(null, null, null, null, OAuth.GrantFlow.AUTHORIZATION_CODE))
.whenComplete(Schedulers.javafx(), onSuccess, onFail)
.executor(true);

deviceTaskExecuter = Task.supplyAsync(() -> Accounts.FACTORY_MICROSOFT.create(null, null, null, null, OAuth.GrantFlow.DEVICE))
.whenComplete(Schedulers.javafx(), onSuccess, onFail)
.executor(true);
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

Potential race condition: When either browser or device flow succeeds, it cancels all tasks and calls onLoginCompleted. However, if both flows complete nearly simultaneously, onLoginCompleted could be called twice. The second call might interfere with account management operations. Consider adding a flag to ensure onLoginCompleted is only executed once, or synchronize access to prevent concurrent execution.

Copilot uses AI. Check for mistakes.
Comment on lines +286 to 290
vbox.getChildren().addAll(new MicrosoftAccountLoginPane(true));
btnAccept.setOnAction(e -> {
fireEvent(new DialogCloseEvent());
Controllers.dialog(new MicrosoftAccountLoginPane());
});
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

The Microsoft account creation flow creates a MicrosoftAccountLoginPane with bodyonly=true and embeds it in the CreateAccountPane, but then the btnAccept action closes the CreateAccountPane and opens a new full MicrosoftAccountLoginPane dialog. This creates a confusing UX where users see a preview/hint of the login interface, then clicking "Log in" closes that dialog and opens a new one with the actual login functionality. Consider either showing the full functional login pane directly in CreateAccountPane, or removing the embedded preview pane to avoid confusion.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 优化微软登录流程

4 participants