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.
This commit is contained in:
2026-08-29 13:13:37 -04:00
parent b42456f255
commit d08756c36f
+30 -12
View File
@@ -235,7 +235,7 @@ func getMyAnimeListAuthorizationToken(content string, verifier *CodeVerifier) My
return post return post
} }
func refreshMyAnimeListAuthorizationToken() { func refreshMyAnimeListAuthorizationToken() bool {
dataForURLs := struct { dataForURLs := struct {
GrantType string `json:"grant_type"` GrantType string `json:"grant_type"`
RefreshToken string `json:"refresh_token"` 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())) response, err := http.NewRequest("POST", "https://myanimelist.net/v1/oauth2/token", strings.NewReader(data.Encode()))
if err != nil { if err != nil {
log.Printf("Failed at response, %s\n", err) log.Printf("Failed at response, %s\n", err)
return false
} }
response.Header.Add("Content-Type", "application/x-www-form-urlencoded") response.Header.Add("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{} client := &http.Client{}
res, resErr := client.Do(response) res, resErr := client.Do(response)
if resErr != nil { 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() defer res.Body.Close()
@@ -274,13 +276,22 @@ func refreshMyAnimeListAuthorizationToken() {
returnedBody, err := io.ReadAll(res.Body) returnedBody, err := io.ReadAll(res.Body)
if err != nil { if err != nil {
log.Printf("Could not read returned body, %s\n", err) 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 { if err != nil {
log.Printf("Failed at unmarshal, %s\n", err) log.Printf("Failed at unmarshal, %s\n", err)
return false
} }
if refreshed.AccessToken == "" {
return false
}
myAnimeListJwt = refreshed
_ = myAnimeListRing.Set(keyring.Item{ _ = myAnimeListRing.Set(keyring.Item{
Key: "MyAnimeListTokenType", Key: "MyAnimeListTokenType",
Data: []byte(myAnimeListJwt.TokenType), Data: []byte(myAnimeListJwt.TokenType),
@@ -297,26 +308,33 @@ func refreshMyAnimeListAuthorizationToken() {
Key: "MyAnimeListRefreshToken", Key: "MyAnimeListRefreshToken",
Data: []byte(myAnimeListJwt.RefreshToken), Data: []byte(myAnimeListJwt.RefreshToken),
}) })
_, err = runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{ return true
Title: "MyAnimeList Authorization",
Message: "It is now safe to close your browser tab",
})
if err != nil {
fmt.Println(err)
}
} }
func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser { func (a *App) GetMyAnimeListLoggedInUser() MyAnimeListUser {
a.MyAnimeListLogin() a.MyAnimeListLogin()
user := createUser() user := createUser()
if user.Name == "" { if user.Name == "" && !a.refreshMyAnimeListAndGetUser(&user) {
refreshMyAnimeListAuthorizationToken() a.LogoutMyAnimeList()
a.MyAnimeListLogin()
user = createUser() user = createUser()
} }
return user 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 { func createUser() MyAnimeListUser {
client := &http.Client{} client := &http.Client{}