RandomCatLover Claude Sonnet 5 commited on
Commit
fe83ebb
·
1 Parent(s): 5be8e7e

Surface Drive-connection failures instead of swallowing them

Browse files

Every failure in the Connect Drive / silent-refresh path was being
caught and turned into a generic re-show-the-button state, with no
way to tell what actually went wrong (missing refresh_token from
Google, expired/revoked token, cookie decrypt failure from a changed
encryption key). Now each failure mode shows a specific message, and
a 'Drive connection diagnostics' expander reports whether a token
cookie is present. Also switched the cookie's SameSite from this
library's default 'strict' to 'lax', matching Streamlit's own auth
cookie policy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Files changed (2) hide show
  1. app.py +20 -4
  2. src/cookie_token_store.py +7 -1
app.py CHANGED
@@ -58,13 +58,20 @@ def _handle_drive_oauth_callback(cookie_manager) -> None:
58
  _drive_redirect_uri(),
59
  code,
60
  )
61
- except Exception:
62
  st.query_params.clear()
63
- st.error("Failed to connect Google Drive. Please try again.")
64
  return
65
 
66
  refresh_token = tokens.get("refresh_token")
67
- if refresh_token and cookie_manager is not None:
 
 
 
 
 
 
 
68
  cookie_token_store.save_refresh_token(cookie_manager, st.user.email, refresh_token)
69
  _store_access_token(tokens)
70
  st.query_params.clear()
@@ -90,7 +97,8 @@ def _ensure_drive_access(cookie_manager) -> None:
90
  )
91
  _store_access_token(tokens)
92
  return
93
- except Exception:
 
94
  cookie_token_store.delete_refresh_token(cookie_manager, st.user.email)
95
 
96
  state = uuid.uuid4().hex
@@ -100,6 +108,14 @@ def _ensure_drive_access(cookie_manager) -> None:
100
  )
101
  st.link_button("Connect Google Drive", authorize_url)
102
 
 
 
 
 
 
 
 
 
103
 
104
  # Constructed at most once per script run -- see get_cookie_manager's docstring.
105
  _cookie_manager = cookie_token_store.get_cookie_manager() if "cookies" in st.secrets else None
 
58
  _drive_redirect_uri(),
59
  code,
60
  )
61
+ except Exception as e:
62
  st.query_params.clear()
63
+ st.error(f"Failed to connect Google Drive: {e}")
64
  return
65
 
66
  refresh_token = tokens.get("refresh_token")
67
+ if not refresh_token:
68
+ st.warning(
69
+ "Google didn't return a refresh_token (this happens if the app was "
70
+ "already authorized without prompt=consent taking effect). "
71
+ "Drive access will work for this session only -- try disconnecting "
72
+ "the app at myaccount.google.com/permissions and reconnecting."
73
+ )
74
+ elif cookie_manager is not None:
75
  cookie_token_store.save_refresh_token(cookie_manager, st.user.email, refresh_token)
76
  _store_access_token(tokens)
77
  st.query_params.clear()
 
97
  )
98
  _store_access_token(tokens)
99
  return
100
+ except Exception as e:
101
+ st.warning(f"Stored Drive access expired or was revoked ({e}); reconnecting.")
102
  cookie_token_store.delete_refresh_token(cookie_manager, st.user.email)
103
 
104
  state = uuid.uuid4().hex
 
108
  )
109
  st.link_button("Connect Google Drive", authorize_url)
110
 
111
+ with st.expander("Drive connection diagnostics"):
112
+ st.write("Logged in as:", st.user.email)
113
+ st.write("Cookie storage configured:", cookie_manager is not None)
114
+ if cookie_manager is not None:
115
+ found = cookie_token_store.get_refresh_token(cookie_manager, st.user.email) is not None
116
+ st.write("Refresh token cookie present:", found)
117
+ st.write("Raw cookies seen by the app:", list(cookie_manager.cookies.keys()))
118
+
119
 
120
  # Constructed at most once per script run -- see get_cookie_manager's docstring.
121
  _cookie_manager = cookie_token_store.get_cookie_manager() if "cookies" in st.secrets else None
src/cookie_token_store.py CHANGED
@@ -34,7 +34,9 @@ def save_refresh_token(cookie_manager: stx.CookieManager, email: str, refresh_to
34
  encrypted = _fernet().encrypt(refresh_token.encode()).decode()
35
  expires_at = datetime.now(timezone.utc) + timedelta(days=_COOKIE_MAX_AGE_DAYS)
36
  name = _cookie_name(email)
37
- cookie_manager.set(name, encrypted, expires_at=expires_at, key=f"set_{name}")
 
 
38
 
39
 
40
  def get_refresh_token(cookie_manager: stx.CookieManager, email: str) -> str | None:
@@ -44,6 +46,10 @@ def get_refresh_token(cookie_manager: stx.CookieManager, email: str) -> str | No
44
  try:
45
  return _fernet().decrypt(encrypted.encode()).decode()
46
  except Exception:
 
 
 
 
47
  return None
48
 
49
 
 
34
  encrypted = _fernet().encrypt(refresh_token.encode()).decode()
35
  expires_at = datetime.now(timezone.utc) + timedelta(days=_COOKIE_MAX_AGE_DAYS)
36
  name = _cookie_name(email)
37
+ # same_site="lax" (this library defaults to "strict") to match the SameSite
38
+ # policy Streamlit's own auth cookie uses.
39
+ cookie_manager.set(name, encrypted, expires_at=expires_at, key=f"set_{name}", same_site="lax")
40
 
41
 
42
  def get_refresh_token(cookie_manager: stx.CookieManager, email: str) -> str | None:
 
46
  try:
47
  return _fernet().decrypt(encrypted.encode()).decode()
48
  except Exception:
49
+ st.warning(
50
+ "Found a stored Drive cookie but couldn't decrypt it (likely the "
51
+ "encryption key changed since it was saved). Treating it as absent."
52
+ )
53
  return None
54
 
55