fix(api): stop promoting the ephemeral Garmin client after setup completes
handleSetupComplete reused the onboarding client object as-is in the
permanent per-user cache, but its garmin.Config.TokenStorePath was fixed
at construction to the ephemeral setup/{hash} directory and never
corrected after that directory was renamed to the permanent {userID}
path. The next respawn of that same client (e.g. any Profile-page save,
which unconditionally calls UpdateCredentials) wrote a fresh token file
back under setup/{hash}, forcing a real re-login/MFA on the next Garmin
connect even though a valid session already existed under {userID}.
Close the ephemeral client instead and let the next garminFor call build
a fresh one against the correct, already-renamed directory.
This commit is contained in:
@@ -88,10 +88,13 @@ func (s *Server) handleSetupGarminMFA(w http.ResponseWriter, r *http.Request) {
|
||||
// handleSetupComplete is the single atomic commit point: only reachable
|
||||
// once the ephemeral session for this subject last reported
|
||||
// garmin.AuthSuccess. Provisions the account, persists the Garmin
|
||||
// credentials, marks it connected, and promotes the already-authenticated
|
||||
// ephemeral client into the permanent per-user cache instead of discarding
|
||||
// it (no redundant re-authentication, no repeat MFA prompt, right after
|
||||
// signup).
|
||||
// credentials, marks it connected, closes the ephemeral client, and
|
||||
// renames its token-store directory into the permanent per-user path --
|
||||
// the next real garminFor(ctx, userID) builds a fresh client from scratch
|
||||
// against that now-permanent directory, whose subprocess's lazy
|
||||
// startup-login resumes the just-renamed, still-valid session without
|
||||
// needing to re-authenticate (a cheap local token-store resume, not a
|
||||
// fresh Garmin login).
|
||||
func (s *Server) handleSetupComplete(w http.ResponseWriter, r *http.Request) {
|
||||
claims, ok := auth.ClaimsFromContext(r.Context())
|
||||
if !ok {
|
||||
@@ -142,9 +145,22 @@ func (s *Server) handleSetupComplete(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// The ephemeral client's own garmin.Config.TokenStorePath was set once,
|
||||
// at construction time in replaceSetupSession, to the ephemeral
|
||||
// setup/{hash} directory being renamed below -- there's no setter to
|
||||
// correct it in place, so promoting this object into s.userGarmin would
|
||||
// leave a client whose subprocess respawns (e.g. on the very next
|
||||
// UpdateCredentials call from a Profile save) using that now-stale
|
||||
// path, recreating a setup/{hash} directory next to the real one.
|
||||
// Closing it here and leaving s.userGarmin empty for this user makes
|
||||
// the next garminFor(ctx, userID) call build a fresh client against the
|
||||
// correct, just-renamed {userID} directory instead -- its subprocess's
|
||||
// lazy startup-login resumes that session without a real Garmin
|
||||
// re-authentication.
|
||||
sess.Client.Close()
|
||||
|
||||
s.mu.Lock()
|
||||
delete(s.setupGarmin, claims.Sub)
|
||||
s.userGarmin[userID] = sess.Client
|
||||
s.userAuthStatus[userID] = sess.Status
|
||||
s.userAuthMessage[userID] = sess.Message
|
||||
s.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user