fix(setup): 绑定码可重试并说明连接方式

This commit is contained in:
Lemon-miaow committed 2026-10-02 17:07:53 +08:00
1 parent d2a6b6ec73
commit 47a2dec4bc
12 files changed
+563 -51

No files matched your search

+9 -9
View File
@@ -24,18 +24,18 @@ import (
// ownership operation (claim, §9.3), which otherwise dead-ends at a 412.
const (
// linkCodeTTL bounds how long a freshly minted code is accepted (spec §10:
// LinkCodeTTL bounds how long a freshly minted code is accepted (spec §10:
// 短 TTL). Long enough to alt-tab from the game to the panel, short enough that
// a leaked code is useless minutes later.
linkCodeTTL = 10 * time.Minute
// linkCodeAlphabet is a 32-symbol set with the visually ambiguous characters
LinkCodeTTL = 10 * time.Minute
// LinkCodeAlphabet is a 32-symbol set with the visually ambiguous characters
// I, O, 0 and 1 removed, so a player can read a code off chat and type it on the
// panel without confusion. 32 divides 256 evenly, so a uniform random byte
// reduced mod 32 is itself uniform — no modulo bias, no rejection sampling.
linkCodeAlphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
// linkCodeLen is the symbol count: a 32^8 ≈ 1.1e12 keyspace, far beyond brute
LinkCodeAlphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
// LinkCodeLen is the symbol count: a 32^8 ≈ 1.1e12 keyspace, far beyond brute
// force inside the TTL.
linkCodeLen = 8
LinkCodeLen = 8
// authSource records which Yggdrasil established the in-game UUID when a code
// was minted (spec §10, dual-Yggdrasil): the official Mojang service, or a
@@ -97,12 +97,12 @@ func deriveAuthSource(mcUUID string) string {
// newLinkCode returns a cryptographically random, unambiguous link code.
func newLinkCode() (string, error) {
buf := make([]byte, linkCodeLen)
buf := make([]byte, LinkCodeLen)
if _, err := rand.Read(buf); err != nil {
return "", err
}
for i, b := range buf {
buf[i] = linkCodeAlphabet[int(b)%len(linkCodeAlphabet)]
buf[i] = LinkCodeAlphabet[int(b)%len(LinkCodeAlphabet)]
}
return string(buf), nil
}
@@ -151,7 +151,7 @@ func (a *API) handleCreateLinkCode(w http.ResponseWriter, r *http.Request) {
writeError(w, r, err)
return
}
expiresAt := a.now().Add(linkCodeTTL)
expiresAt := a.now().Add(LinkCodeTTL)
if err := a.Repo.CreateLinkCode(r.Context(), code, mcUUID, authSource, expiresAt); err != nil {
writeError(w, r, err)
return
+4 -4
View File
@@ -50,8 +50,8 @@ func TestAccountLinkVertical(t *testing.T) {
t.Fatalf("mint code: code = %d, want 201 (%s)", w.Code, w.Body.String())
}
code, _ := acctBody(t, w)["code"].(string)
if len(code) != linkCodeLen {
t.Fatalf("minted code %q: len = %d, want %d", code, len(code), linkCodeLen)
if len(code) != LinkCodeLen {
t.Fatalf("minted code %q: len = %d, want %d", code, len(code), LinkCodeLen)
}
// 2) the player submits the code on the panel (external face).
@@ -113,11 +113,11 @@ func TestCreateLinkCode(t *testing.T) {
if rec.authSource != authSourceMojang {
t.Errorf("default authSource = %q, want %q", rec.authSource, authSourceMojang)
}
if want := api.now().Add(linkCodeTTL); !rec.expiresAt.Equal(want) {
if want := api.now().Add(LinkCodeTTL); !rec.expiresAt.Equal(want) {
t.Errorf("expiresAt = %v, want %v", rec.expiresAt, want)
}
for _, c := range code {
if !strings.ContainsRune(linkCodeAlphabet, c) {
if !strings.ContainsRune(LinkCodeAlphabet, c) {
t.Errorf("code %q contains out-of-alphabet rune %q", code, c)
}
}
+1 -1
View File
@@ -34,7 +34,7 @@ func seedBindAPI(t *testing.T) (*API, *fakeRepo) {
// mint (handleCreateLinkCode → CreateLinkCode).
func mintBindCode(t *testing.T, api *API, repo *fakeRepo, code, uuid, authSource string) {
t.Helper()
if err := repo.CreateLinkCode(t.Context(), code, uuid, authSource, api.now().Add(linkCodeTTL)); err != nil {
if err := repo.CreateLinkCode(t.Context(), code, uuid, authSource, api.now().Add(LinkCodeTTL)); err != nil {
t.Fatalf("mint bind code: %v", err)
}
}