Skip to content

fix: warn instead of block on unverified Minecraft ownership, and explain the login 400 - #3

Merged
ChimeraAnt-DEV merged 1 commit into
mainfrom
fix/ownership-warning-and-login-400
Sep 17, 2026
Merged

ChimeraAnt-DEV merged 1 commit into
mainfrom
fix/ownership-warning-and-login-400

Conversation

@ChimeraAnt-DEV

Copy link
Copy Markdown
Owner

Follow-up to #2 (already merged). Two account-flow bugs, both reported by users.

1. Unverified Minecraft ownership blocked the login

An account that signs in successfully but has never bought Minecraft hit
NotPurchasedMinecraftException and the login aborted with a message the user could not act on.

The ownership check is not removed and still runs exactly as upstream intends. Only its failure
handling changed:

  • verifyGameOwnership() returns whether an entitlement was found, instead of throwing.
  • microsoftAuthAsync() uses that to fall back to an offline identity, built from the Xbox gamertag
    carried on the XSTS response. The selected instance still launches.
  • Login now reports the situation as a notice ("you can still play single-player and use mods"), not an error.
  • Account.ownsMinecraft is persisted via a new Room migration, so the state survives restarts. The account
    list shows an "ownership unverified" hint, and skin/cape fetches are skipped because there is no official
    profile to read from.

Offline play works. Online play is not unlocked and cannot be — official servers run their own ownership
check on every connection. No client-side change can alter that, and this PR does not try to bypass it.

2. Microsoft login failed with "(400) the server could not understand the request"

Root cause confirmed empirically: a build with no oauth_client_id sends an empty client_id to the
device-code endpoint, and Azure answers

400 AADSTS900144: The request body must contain the following parameter: 'client_id'.

which ResponseException.toLocal() rendered as the unhelpful (400) The server could not understand the request.

fetchDeviceCodeResponse() now detects the missing ID up front and states the real cause, pointing at
gradle.properties, the git-ignored .oauth_client_id.txt, and the OAUTH_CLIENT_ID CI secret. An OAuth
client ID belongs to whoever ships the build — upstream's is not reusable by a fork — so this cannot be
"fixed" in code; it has to be configured.

README

Documented the non-blocking ownership behaviour, the three account types, and step-by-step Azure app
registration to enable Microsoft sign-in.

Verification

No JDK/Android SDK in this environment, so CI is the check. build.yml already builds APKs on pull_request;
this PR should produce an artifact the way #2 did. Changes are compile-checked by the build (all) job.

New strings added to values, values-zh-rCN and values-zh-rTW (all three XML files validate, no duplicate
keys).


This pull request was created by an AI agent (OpenHands) on behalf of the user.

@ChimeraAnt-DEV can click here to continue refining the PR

Signing in with a Microsoft account that has never bought Minecraft
previously threw NotPurchasedMinecraftException and aborted the login with
an error the user could not act on. The ownership check itself is unchanged
and still runs; only its failure handling changes.

- verifyGameOwnership() now returns whether an entitlement was found instead
  of throwing. microsoftAuthAsync() uses that to fall back to an offline
  identity, built from the Xbox gamertag carried on the XSTS response, when
  no entitlement exists. The selected instance still launches.
- Account gains ownsMinecraft, persisted via a new Room migration, so the
  state survives restarts. The account list shows an "ownership unverified"
  hint, and skin/cape fetches are skipped because no official profile exists
  to read from.
- Login reports the situation as a notice rather than an error.

Offline play is unaffected. Online play is not unlocked by this change and
cannot be, because official servers perform their own ownership check on
every connection.

Also surface the real cause of the Microsoft login 400. A build with no
oauth_client_id sends an empty client_id and Azure answers 400
AADSTS900144, which the launcher presented as "(400) The server could not
understand the request". fetchDeviceCodeResponse() now detects the missing
ID up front and says so, pointing at gradle.properties, the git-ignored
.oauth_client_id.txt, and the OAUTH_CLIENT_ID CI secret.

README: document the non-blocking ownership behaviour, the three account
types, and how to register an Azure app to enable Microsoft sign-in.

Co-authored-by: openhands <openhands@all-hands.dev>
@ChimeraAnt-DEV ChimeraAnt-DEV added bug Something isn't working enhancement New feature or request labels Sep 17, 2026 — with OpenHands AI

Copy link
Copy Markdown
Owner Author

CI build result

build (all) passed in 4m28s (run 35185848069).

Artifact: debug apk (all) — 375.9 MB universal debug APK (all ABIs), built from 8e26f83.

All Kotlin changes in this PR (account model, Room migration, authenticator, viewmodel, Compose UI, and the three strings.xml files) compiled cleanly.

Note: this build has no OAUTH_CLIENT_ID set, so it exercises the new missing client ID path rather than a real Microsoft sign-in. Add the secret on the repo to test the full flow.

This comment was posted by an AI agent (OpenHands) on behalf of the user.

@ChimeraAnt-DEV
ChimeraAnt-DEV marked this pull request as ready for review September 17, 2026 14:19
@ChimeraAnt-DEV
ChimeraAnt-DEV merged commit 7ce4035 into main Sep 17, 2026
1 check passed
@ChimeraAnt-DEV
ChimeraAnt-DEV deleted the fix/ownership-warning-and-login-400 branch September 17, 2026 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants