From d08756c36faf36a3d5728a849b83879445f14c44 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sat, 29 Aug 2026 13:13:37 -0400 Subject: [PATCH] fix(mal): distinguish silent token refresh from a fresh re-login Two MAL login issues were caused by the refresh path being conflated with the full browser OAuth flow: 1. A spurious 'It is now safe to close your browser tab' dialog was shown after a *silent* background token refresh, even though the browser was never opened. The dialog now belongs only to the browser-callback handler (handleMyAnimeListCallback); the refresh raises no dialog. 2. When both the access token and the refresh token were invalid, the app reported MAL as logged in without a username. Now, if a refresh fails (HttpClient error, non-2xx, or an empty access token), the stale tokens are cleared and a fresh browser OAuth login is automatically initiated instead of silently returning an empty user. refreshMyAnimeListAuthorizationToken now returns a bool indicating whether a fresh access token was actually obtained, letting GetMyAnimeListLoggedInUser decide between retrying with the refreshed token or falling back to a full re-login. --- MALUserFunctions.go | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/MALUserFunctions.go b/MALUserFunctions.go index 0593dbb..36a5dff 100644 --- a/MALUserFunctions.go +++ b/MALUserFunctions.go @@ -235,7 +235,7 @@ func getMyAnimeListAuthorizationToken(content string, verifier *CodeVerifier) My return post } -func refreshMyAnimeListAuthorizationToken() { +func refreshMyAnimeListAuthorizationToken() bool { dataForURLs := struct { GrantType string `json:"grant_type"` RefreshToken string `json:"refresh_token"` @@ -260,13 +260,15 @@ func refreshMyAnimeListAuthorizationToken() { response, err := http.NewRequest("POST", "https://myanimelist.net/v1/oauth2/token", strings.NewReader(data.Encode())) if err != nil { log.Printf("Failed at response, %s\n", err) + return false } response.Header.Add("Content-Type", "application/x-www-form-urlencoded") client := &http.Client{} res, resErr := client.Do(response) if resErr != nil { - log.Printf("Failed at res, %s\n", err) + log.Printf("Failed at res, %s\n", resErr) + return false } defer res.Body.Close() @@ -274,13 +276,22 @@ func refreshMyAnimeListAuthorizationToken() { returnedBody, err := io.ReadAll(res.Body) if err != nil { log.Printf("Could not read returned body, %s\n", err) + return false } - err = json.Unmarshal(returnedBody, &myAnimeListJwt) + var refreshed MyAnimeListJWT + err = json.Unmarshal(returnedBody, &refreshed) if err != nil { log.Printf("Failed at unmarshal, %s\n", err) + return false } + if refreshed.AccessToken == "" { + return false + } + + myAnimeListJwt = refreshed + _ = myAnimeListRing.Set(keyring.Item{ Key: "MyAnimeListTokenType", Data: []byte(myAnimeListJwt.TokenType), @@ -297,26 +308,33 @@ func refreshMyAnimeListAuthorizationToken() { Key: "MyAnimeListRefreshToken", Data: []byte(myAnimeListJwt.RefreshToken), }) - _, err = runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{ - Title: "MyAnimeList Authorization", - Message: "It is now safe to close your browser tab", - }) - if err != nil { - fmt.Println(err) - } + return true } func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser { a.MyAnimeListLogin() user := createUser() - if user.Name == "" { - refreshMyAnimeListAuthorizationToken() + if user.Name == "" && !a.refreshMyAnimeListAndGetUser(&user) { + a.LogoutMyAnimeList() + a.MyAnimeListLogin() user = createUser() } return user } +func (a *App) refreshMyAnimeListAndGetUser(user *MyAnimeListUser) bool { + if !refreshMyAnimeListAuthorizationToken() { + return false + } + freshUser := createUser() + if freshUser.Name == "" { + return false + } + *user = freshUser + return true +} + func createUser() MyAnimeListUser { client := &http.Client{}