#!/usr/bin/env python3 """ verify-cloudron-2fa.py -- Verify 2FA round-trip with correct TOTP selector. The Cloudron OIDC login uses #inputTotpToken (not #inputTotp). Usage: docker compose run --rm --entrypoint python3 provision verify-cloudron-2fa.py """ import os, sys, time from pathlib import Path from playwright.sync_api import sync_playwright sys.path.insert(0, os.path.dirname(__file__)) from bw_helper import BitwardenHelper CLOUDRON_BASE = os.environ.get("CLOUDRON_BASE", "https://my.knownelement.com") STATE_DIR = Path("/app/state") BW_ITEM = "vp-techops Cloudron" EMAIL = "tsgstaff-coo-vptechops@turnsys.com" def main(): bw = BitwardenHelper( client_id=os.environ["BW_CLIENTID"], client_secret=os.environ["BW_CLIENTSECRET"], password=os.environ["BW_PASSWORD"], server_url=os.environ.get("BW_SERVER", ""), ) bw.login() password = bw.get_item_password(BW_ITEM) with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_context(viewport={"width": 1280, "height": 1024}).new_page() print("=== Fresh login (should prompt for TOTP) ===") page.goto(f"{CLOUDRON_BASE}/login.html", wait_until="networkidle", timeout=15000) page.wait_for_timeout(2000) page.wait_for_selector("#inputPassword", timeout=15000) page.click("#inputUsername") page.keyboard.type(EMAIL) page.click("#inputPassword") page.keyboard.type(password) page.locator('[role="button"]:has-text("Log in")').first.click() page.wait_for_timeout(3000) # Check for TOTP token field (correct selector: #inputTotpToken) totp_input = page.query_selector("#inputTotpToken") if totp_input and totp_input.is_visible(): print(" TOTP prompt appeared! 2FA is confirmed working.") totp_code = bw.get_totp(BW_ITEM) print(f" Entering TOTP code: {totp_code}") totp_input.click() page.keyboard.type(totp_code) page.locator("#totpTokenSubmitButton").click() page.wait_for_timeout(5000) print(f" Post-TOTP URL: {page.url}") if "login" not in page.url.lower() and "openid" not in page.url.lower(): print(" FULL 2FA ROUND-TRIP VERIFIED!") else: body = page.evaluate("() => document.body.innerText.substring(0, 200)") print(f" Still on login/OIDC page. Body: {body}") else: print(" WARNING: No TOTP prompt appeared") print(f" URL: {page.url}") page.screenshot(path=str(STATE_DIR / "verify-2fa-final.png")) browser.close() if __name__ == "__main__": main()