Fix regressions with the remote-auth preventing it from working. Minor usability and style improvements. Changed /auth/api/ws/pair name to permit, to go with other parts of the software.

This commit is contained in:
2025-12-09 21:57:33 +00:00
parent 9b491164fd
commit 087b24388c
4 changed files with 28 additions and 13 deletions
+16 -9
View File
@@ -75,8 +75,6 @@
</button>
</div>
</div>
<p v-if="error && !deviceInfo" class="error-message">{{ error }}</p>
</form>
</div>
</template>
@@ -346,7 +344,7 @@ async function ensureConnection() {
wsConnecting = true
try {
const authHost = settings.value?.auth_host
const wsPath = '/auth/ws/remote-auth/pair'
const wsPath = '/auth/ws/remote-auth/permit'
const wsUrl = authHost && location.host !== authHost ? `//${authHost}${wsPath}` : wsPath
ws = await aWebSocket(wsUrl)
const msg = await ws.receive_json()
@@ -365,7 +363,7 @@ async function ensureConnection() {
// Defer cursor position update to after browser processes the key
function deferUpdateCursor(event) {
// Handle Tab/Space for autocomplete immediately
if (event.key === 'Tab' || event.key === ' ') {
if (event.key === 'Tab' || event.key === ' ' || event.key === 'Escape') {
handleKeydown(event)
return
}
@@ -527,7 +525,7 @@ async function lookupDeviceInfo() {
const res = await ws.receive_json()
updateChallenge(res.pow)
if (typeof res.status === 'number' && res.status >= 400) {
error.value = res.detail || 'Request failed'
showMessage(res.detail || 'Request failed', 'error')
serverError.value = true
deviceInfo.value = null
lastLookedUpCode = null
@@ -544,14 +542,14 @@ async function lookupDeviceInfo() {
lastLookedUpCode = currentCode
nextTick(() => { submitBtnRef.value?.focus() })
} else {
error.value = 'Unexpected response from server'
showMessage('Unexpected response from server', 'error')
serverError.value = true
deviceInfo.value = null
lastLookedUpCode = null
}
} catch (err) {
console.error('Lookup error:', err)
error.value = err.message || 'Lookup failed'
showMessage(err.message || 'Lookup failed', 'error')
serverError.value = true
deviceInfo.value = null
lastLookedUpCode = null
@@ -563,6 +561,12 @@ async function lookupDeviceInfo() {
}
function handleKeydown(event) {
if (event.key === 'Escape') {
code.value = ''
handleInput()
event.preventDefault()
return
}
if (event.key === 'Tab') {
if (autocompleteHint.value) {
const applied = applyAutocomplete()
@@ -708,12 +712,15 @@ defineExpose({ reset, deny, code, handleInput, loading, error })
}
.slot-machine.has-error {
border-color: var(--color-error, #ef4444);
/* Error background only shown when focused */
}
.input-wrapper.focused.has-error .slot-machine {
background: var(--color-error-bg, rgba(239, 68, 68, 0.05));
}
.slot-machine.is-complete {
border-color: var(--color-success, #10b981);
/* Success state - no special styling */
}
.slot-reel {
+3 -3
View File
@@ -38,7 +38,7 @@ async def websocket_remote_auth_request(ws: WebSocket):
1. Client connects
2. Server sends HARD PoW challenge, client solves and responds
3. Server creates a 3-word pairing code and sends it with expiry
4. Server waits for another device to authenticate via /remote-auth/pair
4. Server waits for another device to authenticate via /remote-auth/permit
5. When auth completes, server sends session_token to this client
6. Client can then use the session token to set a cookie
7. Connection times out after 5 minutes with explicit timeout message
@@ -250,9 +250,9 @@ async def websocket_remote_auth_request(ws: WebSocket):
remoteauth.instance.decrement_connections()
@app.websocket("/pair")
@app.websocket("/permit")
@websocket_error_handler
async def websocket_remote_auth_pair(ws: WebSocket):
async def websocket_remote_auth_permit(ws: WebSocket):
"""Complete a remote authentication request using a 3-word pairing code.
This endpoint is called from the user's profile on the authenticating device.
+4 -1
View File
@@ -3,7 +3,7 @@ from uuid import UUID
from fastapi import FastAPI, WebSocket
from paskia.authsession import create_session, get_reset, get_session
from paskia.fastapi import authz
from paskia.fastapi import authz, remote
from paskia.fastapi.session import AUTH_COOKIE, infodict
from paskia.fastapi.wsutil import validate_origin, websocket_error_handler
from paskia.globals import db, passkey
@@ -13,6 +13,9 @@ from paskia.util.tokens import create_token, session_key
# Create a FastAPI subapp for WebSocket endpoints
app = FastAPI()
# Mount the remote auth WebSocket endpoints
app.mount("/remote-auth", remote.app)
async def register_chat(
ws: WebSocket,
+5
View File
@@ -39,6 +39,8 @@ async def init(
In FastAPI lifespan we call with bootstrap=False to avoid duplicate bootstrapping
since the CLI performs it once before servers start.
"""
from . import remoteauth
# Initialize passkey instance with provided parameters
passkey.instance = Passkey(
rp_id=rp_id,
@@ -54,6 +56,9 @@ async def init(
await sql.init()
# Initialize remote auth manager
await remoteauth.init()
if bootstrap:
# Bootstrap system if needed
from .bootstrap import bootstrap_if_needed