From 0eef318e78f4ab3bc8f5266991c18c7c0c8ec708 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 15 Jun 2026 23:55:53 +0000 Subject: [PATCH 1/4] docs: update Authelia setup instructions to use MERGE workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Step 3 now says MERGE (not replace/append) with clear warning to keep existing config - access_control: kiosk one_factor rule must go ABOVE any existing two_factor rule, with explanation that Authelia applies rules top-down (first match wins) - session block: keep existing values; only add the block if none exists yet - Kiosk can only do one_factor — TOTP/WebAuthn via API is not possible - Updated in both configure_authelia() printed output and README Authentication section https://claude.ai/code/session_01EyjEQLWbTXcZgbMDarf7NU --- Readme.md | 30 ++++++++++++++++++++---------- ubuntu-based-kiosk-v1.0.2.sh | 31 +++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Readme.md b/Readme.md index 76b7e0b..e0f7f11 100644 --- a/Readme.md +++ b/Readme.md @@ -156,25 +156,35 @@ kiosk: - kiosk ``` -**Step 3 — Configure session duration and access control** in `~/docker/authelia/config/configuration.yml`: +**Step 3 — MERGE into `~/docker/authelia/config/configuration.yml`** (do not replace your existing config): + +**access_control** — Add the kiosk rule **ABOVE** any existing `two_factor` rule. Authelia applies rules top-down — first match wins: ```yaml -session: - expiration: 1y # absolute session lifetime - inactivity: 90d # idle timeout before logout - remember_me: 1y # duration granted by keepMeLoggedIn - access_control: default_policy: deny rules: - # Allow kiosk group to reach any subdomain with one-factor auth + # ADD THIS — kiosk can only do one_factor (no TOTP/WebAuthn via API) - domain: '*.yourdomain.com' subject: 'group:kiosk' policy: one_factor - # Optional: bypass Authelia entirely for the kiosk's static IP + # Keep your existing rules below — e.g.: # - domain: '*.yourdomain.com' - # networks: ['192.168.1.50/32'] - # policy: bypass + # policy: two_factor +``` + +**session** — Keep your existing session block as-is; no changes needed. The kiosk re-authenticates via API on every startup so session expiry barely matters for it. + +If you do **not** yet have a session block, add: + +```yaml +session: + expiration: 8h + inactivity: 1h + remember_me: 7d + cookies: + - domain: yourdomain.com + authelia_url: https://auth.yourdomain.com ``` **Step 4 — Restart Authelia:** diff --git a/ubuntu-based-kiosk-v1.0.2.sh b/ubuntu-based-kiosk-v1.0.2.sh index 6fcd505..32ad4af 100644 --- a/ubuntu-based-kiosk-v1.0.2.sh +++ b/ubuntu-based-kiosk-v1.0.2.sh @@ -10039,7 +10039,8 @@ process.stdout.write(Buffer.concat([iv,enc]).toString('base64')); echo echo " Copy the \$argon2id\$... output — that is your hash." echo - echo "2. Add a kiosk user to ~/docker/authelia/config/users.yml:" + echo "2. ADD a kiosk user to ~/docker/authelia/config/users.yml" + echo " (append — do not replace existing users):" echo echo " kiosk:" echo " displayname: \"Kiosk Display\"" @@ -10048,19 +10049,37 @@ process.stdout.write(Buffer.concat([iv,enc]).toString('base64')); echo " groups:" echo " - kiosk" echo - echo "3. Add to ~/docker/authelia/config/configuration.yml:" + echo "3. MERGE into ~/docker/authelia/config/configuration.yml:" echo - echo " session:" - echo " expiration: 1y" - echo " inactivity: 90d" - echo " remember_me: 1y" + echo " ── access_control ─────────────────────────────────────" + echo " Add the kiosk rule ABOVE any existing two_factor rule." + echo " Authelia applies rules top-down — first match wins." echo echo " access_control:" echo " default_policy: deny" echo " rules:" + echo " # ADD THIS — kiosk can only do one_factor (no TOTP/WebAuthn via API)" echo " - domain: '*.yourdomain.com'" echo " subject: 'group:kiosk'" echo " policy: one_factor" + echo " # Keep your existing rules below — e.g.:" + echo " # - domain: '*.yourdomain.com'" + echo " # policy: two_factor" + echo + echo " ── session ─────────────────────────────────────────────" + echo " Keep your existing session block — no changes needed." + echo " The kiosk re-authenticates via API on every startup so" + echo " session expiry barely matters for it." + echo + echo " If you do NOT yet have a session block, add:" + echo + echo " session:" + echo " expiration: 8h" + echo " inactivity: 1h" + echo " remember_me: 7d" + echo " cookies:" + echo " - domain: yourdomain.com" + echo " authelia_url: https://auth.yourdomain.com" echo echo "4. Restart Authelia on your Docker host:" echo " docker compose restart authelia" From effb7269795087384376ebb399e06b5f9a018025 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 16 Jun 2026 00:15:18 +0000 Subject: [PATCH 2/4] fix: add 10-second timeout to Authelia fetch to prevent white screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without a timeout, session.defaultSession.fetch() hangs for 1-2 minutes on TCP timeout when Authelia is unreachable (wrong URL, server down, firewall). Since createWindow() awaits autheliaAuthenticate(), the main window is visible but no BrowserView is attached during that wait — causing a persistent white screen with ibeam cursor. https://claude.ai/code/session_01EyjEQLWbTXcZgbMDarf7NU --- ubuntu-based-kiosk-v1.0.2.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ubuntu-based-kiosk-v1.0.2.sh b/ubuntu-based-kiosk-v1.0.2.sh index 32ad4af..a251d8d 100644 --- a/ubuntu-based-kiosk-v1.0.2.sh +++ b/ubuntu-based-kiosk-v1.0.2.sh @@ -5309,11 +5309,14 @@ async function autheliaAuthenticate(){ const decipher=crypto.createDecipheriv('aes-256-cbc',key,iv); const password=Buffer.concat([decipher.update(enc),decipher.final()]).toString('utf8'); + const ctrl=new AbortController(); + const timer=setTimeout(()=>ctrl.abort(),10000); const res=await session.defaultSession.fetch(`${autheliaURL}/api/firstfactor`,{ method:'POST', headers:{'Content-Type':'application/json','User-Agent':'kiosk/1.0'}, - body:JSON.stringify({username:autheliaUsername,password,keepMeLoggedIn:true,requestMethod:'GET',targetURL:''}) - }); + body:JSON.stringify({username:autheliaUsername,password,keepMeLoggedIn:true,requestMethod:'GET',targetURL:''}), + signal:ctrl.signal + }).finally(()=>clearTimeout(timer)); const body=await res.json().catch(()=>({})); if(res.ok&&body.status==='OK'){ console.log('[AUTHELIA] Authenticated as',autheliaUsername); From 31eb750514bed95f27d65a43d6d6e2034f9f0aa2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 16 Jun 2026 00:25:11 +0000 Subject: [PATCH 3/4] docs: clarify Authelia access_control merge with before/after example The duplicate-block pitfall (YAML silently ignores duplicate keys, causing a white screen) is now called out explicitly in both the script's printed output and the README. Added a before/after example showing the correct merged result with the kiosk one_factor rule above the two_factor wildcard. Also explains why one_factor is required (TOTP/WebAuthn need interactive second step, impossible via API). https://claude.ai/code/session_01EyjEQLWbTXcZgbMDarf7NU --- Readme.md | 24 +++++++++++++++++++----- ubuntu-based-kiosk-v1.0.2.sh | 32 ++++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/Readme.md b/Readme.md index e0f7f11..45a1d1c 100644 --- a/Readme.md +++ b/Readme.md @@ -158,19 +158,33 @@ kiosk: **Step 3 — MERGE into `~/docker/authelia/config/configuration.yml`** (do not replace your existing config): -**access_control** — Add the kiosk rule **ABOVE** any existing `two_factor` rule. Authelia applies rules top-down — first match wins: +**access_control** — Find your **existing** `access_control:` block and add the kiosk rule as the **first** rule inside it. +> **Do NOT create a second `access_control:` block.** YAML silently ignores duplicate keys — Authelia will never see the kiosk rule and the kiosk will get a white screen. + +Authelia reads rules top-down — first match wins. The kiosk rule **must** sit above any `two_factor` wildcard rule, otherwise the wildcard matches first. + +**Why `one_factor`?** The kiosk authenticates via the API (`/api/firstfactor` — password only). TOTP and WebAuthn require an interactive second step that is impossible from a script, so the kiosk group must use `one_factor`. + +*Before (your existing config):* ```yaml access_control: default_policy: deny rules: - # ADD THIS — kiosk can only do one_factor (no TOTP/WebAuthn via API) - domain: '*.yourdomain.com' + policy: two_factor +``` + +*After (add kiosk rule above the two_factor rule — same block, not a new one):* +```yaml +access_control: + default_policy: deny + rules: + - domain: '*.yourdomain.com' # kiosk first — one_factor only subject: 'group:kiosk' policy: one_factor - # Keep your existing rules below — e.g.: - # - domain: '*.yourdomain.com' - # policy: two_factor + - domain: '*.yourdomain.com' # existing rule stays below + policy: two_factor ``` **session** — Keep your existing session block as-is; no changes needed. The kiosk re-authenticates via API on every startup so session expiry barely matters for it. diff --git a/ubuntu-based-kiosk-v1.0.2.sh b/ubuntu-based-kiosk-v1.0.2.sh index a251d8d..eeee051 100644 --- a/ubuntu-based-kiosk-v1.0.2.sh +++ b/ubuntu-based-kiosk-v1.0.2.sh @@ -10055,19 +10055,39 @@ process.stdout.write(Buffer.concat([iv,enc]).toString('base64')); echo "3. MERGE into ~/docker/authelia/config/configuration.yml:" echo echo " ── access_control ─────────────────────────────────────" - echo " Add the kiosk rule ABOVE any existing two_factor rule." - echo " Authelia applies rules top-down — first match wins." + echo " Find your EXISTING access_control block and add the" + echo " kiosk rule as the FIRST rule inside it." echo + echo " !! DO NOT create a second access_control: block !!" + echo " YAML silently ignores duplicate keys — the kiosk rule" + echo " will be invisible to Authelia and you will get a white" + echo " screen on the kiosk." + echo + echo " Authelia reads rules top-down, first match wins." + echo " The kiosk rule MUST be above any two_factor rule or" + echo " the two_factor wildcard will match first." + echo + echo " ── EXAMPLE — before (your existing config): ──────────" echo " access_control:" echo " default_policy: deny" echo " rules:" - echo " # ADD THIS — kiosk can only do one_factor (no TOTP/WebAuthn via API)" echo " - domain: '*.yourdomain.com'" + echo " policy: two_factor" + echo + echo " ── EXAMPLE — after (add kiosk rule above two_factor): ─" + echo " access_control:" + echo " default_policy: deny" + echo " rules:" + echo " - domain: '*.yourdomain.com' # <-- kiosk first" echo " subject: 'group:kiosk'" echo " policy: one_factor" - echo " # Keep your existing rules below — e.g.:" - echo " # - domain: '*.yourdomain.com'" - echo " # policy: two_factor" + echo " - domain: '*.yourdomain.com' # <-- existing" + echo " policy: two_factor" + echo + echo " Why one_factor? The kiosk authenticates via the API" + echo " (/api/firstfactor — password only). TOTP and WebAuthn" + echo " require a second interactive step that is impossible" + echo " from a script, so the kiosk group must use one_factor." echo echo " ── session ─────────────────────────────────────────────" echo " Keep your existing session block — no changes needed." From 3d5a5839b3f2456b88a344945d4f8c3331c93aa4 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 16 Jun 2026 00:31:14 +0000 Subject: [PATCH 4/4] fix: use capture:true on touch event listeners to prevent page JS interference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Touch handlers used bubble phase (no capture:true), so any page script that called stopPropagation() on touchstart/touchend — e.g. Authelia's login form or scroll containers — silently blocked the preload's swipe detection. Using capture:true fires the preload's listeners in the capture phase (before any element-level handlers), so swipe works even on pages with their own touch handling. Applied to both preloads (standard and auto-show keyboard). Also adds missing [TOUCH] 2-finger HORIZONTAL console.log to the standard preload so swipe events are visible in electron.log for debugging. https://claude.ai/code/session_01EyjEQLWbTXcZgbMDarf7NU --- ubuntu-based-kiosk-v1.0.2.sh | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ubuntu-based-kiosk-v1.0.2.sh b/ubuntu-based-kiosk-v1.0.2.sh index eeee051..f46815f 100644 --- a/ubuntu-based-kiosk-v1.0.2.sh +++ b/ubuntu-based-kiosk-v1.0.2.sh @@ -7172,8 +7172,8 @@ window.addEventListener('DOMContentLoaded',()=>{ touchStartTime=Date.now(); fingerCount=e.touches.length; } - },{passive:true}); - + },{passive:true,capture:true}); + document.addEventListener('touchend',e=>{ if(e.changedTouches.length>=1){ const touchEndX=e.changedTouches[0].clientX; @@ -7181,9 +7181,9 @@ window.addEventListener('DOMContentLoaded',()=>{ const deltaX=touchEndX-touchStartX; const deltaY=touchEndY-touchStartY; const deltaTime=Date.now()-touchStartTime; - + if(deltaTime>SWIPE_MAX_TIME)return; - + const absX=Math.abs(deltaX); const absY=Math.abs(deltaY); @@ -7194,6 +7194,7 @@ window.addEventListener('DOMContentLoaded',()=>{ } // 2-finger HORIZONTAL = change tabs else if(fingerCount===2&&absX>SWIPE_THRESHOLD&&absY0?'swipe-right':'swipe-left'); } // 1-finger HORIZONTAL = arrow keys @@ -7207,7 +7208,7 @@ window.addEventListener('DOMContentLoaded',()=>{ }); } } - },{passive:true}); + },{passive:true,capture:true}); // Show pause button on user interaction (for rotation sites only) let lastUserInteraction=0; @@ -9188,8 +9189,8 @@ window.addEventListener('DOMContentLoaded',()=>{ touchStartTime=Date.now(); fingerCount=e.touches.length; } - },{passive:true}); - + },{passive:true,capture:true}); + document.addEventListener('touchend',e=>{ if(e.changedTouches.length>=1){ const touchEndX=e.changedTouches[0].clientX; @@ -9197,9 +9198,9 @@ window.addEventListener('DOMContentLoaded',()=>{ const deltaX=touchEndX-touchStartX; const deltaY=touchEndY-touchStartY; const deltaTime=Date.now()-touchStartTime; - + if(deltaTime>SWIPE_MAX_TIME)return; - + const absX=Math.abs(deltaX); const absY=Math.abs(deltaY); @@ -9231,7 +9232,7 @@ window.addEventListener('DOMContentLoaded',()=>{ }); } } - },{passive:true}); + },{passive:true,capture:true}); // Optional: Auto-show on text field focus (can be disabled) let autoShowEnabled = true; // Set to false to disable auto-show