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
}
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{}