From 6577a733c6afa90dd93249a690dff6b32f4d91cc Mon Sep 17 00:00:00 2001 From: Steve Dogiakos Date: Sun, 29 Mar 2026 20:22:07 -0600 Subject: [PATCH] feat: add /api/csrf endpoint for offline queue token refresh Returns the current session CSRF token as JSON so the offline queue replay can obtain a fresh token without parsing HTML. Rate-limited to 30 requests/minute. Passes offline flag through to the thank-you template. --- app.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 0347380..8f21ecd 100644 --- a/app.py +++ b/app.py @@ -13,7 +13,7 @@ from flask_limiter.util import get_remote_address from flask_login import ( LoginManager, UserMixin, login_user, logout_user, login_required, current_user ) -from flask_wtf.csrf import CSRFProtect +from flask_wtf.csrf import CSRFProtect, generate_csrf from werkzeug.security import generate_password_hash, check_password_hash # Set up logging @@ -329,6 +329,7 @@ def thank_you(): name = request.args.get('name', '').strip() if not name: return redirect(url_for('index')) + offline = request.args.get('offline') == '1' site_title = os.environ.get('SITE_TITLE', 'Guestbook') logo_url = os.environ.get('LOGO_URL', '/static/images/logo.png') try: @@ -341,7 +342,8 @@ def thank_you(): logger.error("Database error loading guests for thank-you: %s", e) guests = [] return render_template('thank_you.html', name=name, guests=guests, - site_title=site_title, logo_url=logo_url) + site_title=site_title, logo_url=logo_url, + offline=offline) # --------------------------------------------------------------------------- # Admin auth routes @@ -611,6 +613,12 @@ def api_guests(): ] return jsonify(guests) +@app.route('/api/csrf', methods=['GET']) +@csrf.exempt +@limiter.limit("30 per minute") +def api_csrf(): + return jsonify({"csrf_token": generate_csrf()}) + # --------------------------------------------------------------------------- # PWA # ---------------------------------------------------------------------------