diff --git a/bincio/serve/server.py b/bincio/serve/server.py index 5c680c1..77612ea 100644 --- a/bincio/serve/server.py +++ b/bincio/serve/server.py @@ -1760,6 +1760,18 @@ async def me_set_strava_credentials( pass if not csec: raise HTTPException(400, "client_secret is required (no existing secret to preserve)") + + # If the client_id changed, the existing token belongs to a different OAuth + # app and will fail on refresh — delete it so the user must re-authenticate. + token_path = _get_data_dir() / user.handle / "strava_token.json" + if creds_path.exists() and token_path.exists(): + try: + old_cid = str(json.loads(creds_path.read_text(encoding="utf-8")).get("client_id", "")).strip() + if old_cid and old_cid != cid: + token_path.unlink(missing_ok=True) + except Exception: + pass + creds_path.write_text( json.dumps({"client_id": cid, "client_secret": csec}, indent=2), encoding="utf-8", @@ -2470,6 +2482,15 @@ async def strava_status(bincio_session: Optional[str] = Cookie(default=None)) -> }) +@app.post("/api/strava/disconnect") +async def strava_disconnect(bincio_session: Optional[str] = Cookie(default=None)) -> JSONResponse: + """Remove the stored Strava token, forcing a fresh OAuth on next connect.""" + user = _require_user(bincio_session) + token_path = _get_data_dir() / user.handle / "strava_token.json" + token_path.unlink(missing_ok=True) + return JSONResponse({"ok": True}) + + @app.post("/api/strava/reset") async def strava_reset(request: Request, bincio_session: Optional[str] = Cookie(default=None)) -> JSONResponse: """Reset last_sync_at so the next sync re-fetches from a chosen point. diff --git a/site/src/pages/settings/index.astro b/site/src/pages/settings/index.astro index 7f039b9..264be5b 100644 --- a/site/src/pages/settings/index.astro +++ b/site/src/pages/settings/index.astro @@ -124,6 +124,14 @@ import Base from '../../layouts/Base.astro'; +
Connected to Strava. Disconnect to re-authenticate with different credentials.
+ + +