Summary
BoostcampAPI keeps a session alive by replaying the stored email + plaintext password to re-login when the Firebase ID token expires. The cleaner, Firebase-native approach is to use the refresh token returned at login (via the secure-token exchange endpoint), which avoids storing the password at all.
Current behavior
login() calls the Firebase Identity Toolkit sign-in endpoint with returnSecureToken: True, but only keeps data["idToken"] — the refreshToken in the response is discarded (boostcampapi/boostcampapi.py:154).
save_session() pickles {token, email, password} to .boostcamp/session.pickle, persisting the plaintext password (boostcampapi/boostcampapi.py:182-189).
- On a
403, _post() → _re_login() re-submits the stored email/password to mint a fresh idToken (boostcampapi/boostcampapi.py:206-244).
Problems
- Plaintext password at rest. The pickle stores the raw password. Even though
.boostcamp/ is gitignored, it's an unnecessary credential exposure on disk.
- Not how Firebase is meant to refresh. Firebase issues short-lived ID tokens (~1h) specifically so clients exchange a long-lived
refreshToken at the secure-token endpoint — no need to retain the password.
Proposed change
- Capture
refreshToken (and ideally expiresIn) from the login response.
- Add a refresh path that POSTs to the Firebase secure-token endpoint:
https://securetoken.googleapis.com/v1/token?key=<API_KEY> with
grant_type=refresh_token&refresh_token=<token>, returning a new id_token + refresh_token.
- In
_post()'s 403 handler (and/or proactively before expiry using expiresIn), refresh via the token grant instead of _re_login().
- Stop persisting the password in
save_session(); store {token, refresh_token} instead. Keep _re_login() (password replay) only as an optional fallback, or drop it.
Notes / open questions
- The Firebase Web API key is needed for the secure-token endpoint — confirm where it currently comes from for the sign-in URL and reuse it.
- This is a
boostcamp-api library change; the boostcamp-mcp server would benefit automatically since it already constructs BoostcampAPI and relies on the library for auth. (Today the MCP server only passes a token and never loads the session, so it gets no auto-renewal at all — a refresh-token flow plus loading a session would fix that cleanly.)
Filed as a future enhancement; not blocking current work.
Summary
BoostcampAPIkeeps a session alive by replaying the stored email + plaintext password to re-login when the Firebase ID token expires. The cleaner, Firebase-native approach is to use the refresh token returned at login (via the secure-token exchange endpoint), which avoids storing the password at all.Current behavior
login()calls the Firebase Identity Toolkit sign-in endpoint withreturnSecureToken: True, but only keepsdata["idToken"]— therefreshTokenin the response is discarded (boostcampapi/boostcampapi.py:154).save_session()pickles{token, email, password}to.boostcamp/session.pickle, persisting the plaintext password (boostcampapi/boostcampapi.py:182-189).403,_post()→_re_login()re-submits the stored email/password to mint a freshidToken(boostcampapi/boostcampapi.py:206-244).Problems
.boostcamp/is gitignored, it's an unnecessary credential exposure on disk.refreshTokenat the secure-token endpoint — no need to retain the password.Proposed change
refreshToken(and ideallyexpiresIn) from the login response.https://securetoken.googleapis.com/v1/token?key=<API_KEY>withgrant_type=refresh_token&refresh_token=<token>, returning a newid_token+refresh_token._post()'s 403 handler (and/or proactively before expiry usingexpiresIn), refresh via the token grant instead of_re_login().save_session(); store{token, refresh_token}instead. Keep_re_login()(password replay) only as an optional fallback, or drop it.Notes / open questions
boostcamp-apilibrary change; theboostcamp-mcpserver would benefit automatically since it already constructsBoostcampAPIand relies on the library for auth. (Today the MCP server only passes a token and never loads the session, so it gets no auto-renewal at all — a refresh-token flow plus loading a session would fix that cleanly.)Filed as a future enhancement; not blocking current work.