1
Fork 0

Force a user interaction before token login.

This avoids issues with autoplay being disabled.
This commit is contained in:
Juliusz Chroboczek 2024-06-11 11:57:15 +02:00
parent 14aeb125be
commit 8fe2e9ca5f
4 changed files with 55 additions and 34 deletions

View File

@ -2,7 +2,9 @@ Galene 0.9.1 (unreleased)
* Added an example client with minimal functionality under static/example. * Added an example client with minimal functionality under static/example.
* Fixed a race condition that would cause the client to run with * Fixed a race condition that would cause the client to run with
undefined settings if the initial connection was extremely quick. undefined settings if the initial connection was extremely fast.
* Changed the token login to force a user interaction in order to avoid
issues with autoplay.
* Implemented client-side timeouts (we already had one on the server side). * Implemented client-side timeouts (we already had one on the server side).
* Reduced the server-side timeout. * Reduced the server-side timeout.
* Don't attempt to set the file descriptor limit, since recent versions * Don't attempt to set the file descriptor limit, since recent versions

View File

@ -715,17 +715,17 @@ h1 {
font-weight: bold; font-weight: bold;
} }
.userform { .login {
display: inline display: inline
} }
.userform label { .loginform label {
min-width: 3em; min-width: 3em;
display: inline-block; display: inline-block;
padding-top: 10px; padding-top: 10px;
} }
.userform input[type="text"], .userform input[type="password"] { .loginform input[type="text"], .loginform input[type="password"] {
width: 100%; width: 100%;
} }

View File

@ -101,10 +101,12 @@
</div> </div>
<div class="login-container invisible" id="login-container"> <div class="login-container invisible" id="login-container">
<div class="login-box"> <div class="login-box">
<form id="userform" class="userform"> <form id="loginform" class="loginform">
<div id="userform">
<label for="username">Username</label> <label for="username">Username</label>
<input id="username" type="text" name="username" <input id="username" type="text" name="username"
autocomplete="username" class="form-control"/> autocomplete="username" class="form-control"/>
</div>
<div id="passwordform"> <div id="passwordform">
<label for="password">Password</label> <label for="password">Password</label>
<input id="password" type="password" name="password" <input id="password" type="password" name="password"

View File

@ -35,8 +35,8 @@ let pwAuth = false;
/** @type {string} */ /** @type {string} */
let token = null; let token = null;
/** @type {boolean} */ /** @type {string} */
let connectingAgain = false; let probingState = null;
/** /**
* @typedef {Object} settings * @typedef {Object} settings
@ -317,8 +317,6 @@ function setConnected(connected) {
} else { } else {
userbox.classList.add('invisible'); userbox.classList.add('invisible');
connectionbox.classList.remove('invisible'); connectionbox.classList.remove('invisible');
if(!connectingAgain)
displayError('Disconnected', 'error');
hideVideo(); hideVideo();
window.onresize = null; window.onresize = null;
} }
@ -329,9 +327,7 @@ function setConnected(connected) {
*/ */
async function gotConnected() { async function gotConnected() {
setConnected(true); setConnected(true);
let again = connectingAgain; await join();
connectingAgain = false;
await join(again);
} }
/** /**
@ -352,10 +348,7 @@ function setChangePassword(username) {
} }
} }
/** async function join() {
* @param {boolean} again
*/
async function join(again) {
let username = getInputElement('username').value.trim(); let username = getInputElement('username').value.trim();
let credentials; let credentials;
if(token) { if(token) {
@ -364,11 +357,30 @@ async function join(again) {
type: 'token', type: 'token',
token: token, token: token,
}; };
if(!again) switch(probingState) {
// the first time around, we need to join with no username in case null:
// order to give the server a chance to reply with 'need-username'. // when logging in with a token, we need to give the user
// a chance to interact with the page in order to enable
// autoplay. Probe the group first in order to determine if
// we need a username. We should really extend the protocol
// to have a simpler protocol for probing.
probingState = 'probing';
username = null; username = null;
break;
case 'need-username':
case 'success':
probingState = null;
break
default:
console.warn(`Unexpected probing state ${probingState}`);
probingState = null;
break;
}
} else { } else {
if(probingState !== null) {
console.warn(`Unexpected probing state ${probingState}`);
probingState = null;
}
let pw = getInputElement('password').value; let pw = getInputElement('password').value;
getInputElement('password').value = ''; getInputElement('password').value = '';
if(!groupStatus.authServer) { if(!groupStatus.authServer) {
@ -420,9 +432,9 @@ function gotClose(code, reason) {
if(code != 1000) { if(code != 1000) {
console.warn('Socket close', code, reason); console.warn('Socket close', code, reason);
} }
let form = document.getElementById('userform'); let form = document.getElementById('loginform');
if(!(form instanceof HTMLFormElement)) if(!(form instanceof HTMLFormElement))
throw new Error('Bad type for userform'); throw new Error('Bad type for loginform');
form.active = true; form.active = true;
} }
@ -2471,14 +2483,13 @@ async function gotJoined(kind, group, perms, status, data, error, message) {
switch(kind) { switch(kind) {
case 'fail': case 'fail':
if(error === 'need-username' || error === 'duplicate-username') { if(probingState === 'probing' && error === 'need-username') {
probingState = 'need-username';
setVisibility('passwordform', false); setVisibility('passwordform', false);
connectingAgain = true;
} else { } else {
token = null; token = null;
}
if(error !== 'need-username')
displayError('The server said: ' + message); displayError('The server said: ' + message);
}
this.close(); this.close();
setButtonsVisibility(); setButtonsVisibility();
return; return;
@ -2489,13 +2500,21 @@ async function gotJoined(kind, group, perms, status, data, error, message) {
return; return;
case 'leave': case 'leave':
this.close(); this.close();
token = null;
setButtonsVisibility(); setButtonsVisibility();
setChangePassword(null); setChangePassword(null);
return; return;
case 'join': case 'join':
case 'change': case 'change':
if(probingState === 'probing') {
probingState = 'success';
setVisibility('userform', false);
setVisibility('passwordform', false);
this.close();
setButtonsVisibility();
return;
} else {
token = null; token = null;
}
// don't discard endPoint and friends // don't discard endPoint and friends
for(let key in status) for(let key in status)
groupStatus[key] = status[key]; groupStatus[key] = status[key];
@ -2515,8 +2534,6 @@ async function gotJoined(kind, group, perms, status, data, error, message) {
return; return;
} }
token = null;
let input = /** @type{HTMLTextAreaElement} */ let input = /** @type{HTMLTextAreaElement} */
(document.getElementById('input')); (document.getElementById('input'));
input.placeholder = 'Type /help for help'; input.placeholder = 'Type /help for help';
@ -3907,12 +3924,12 @@ function displayMessage(message) {
return displayError(message, "info"); return displayError(message, "info");
} }
document.getElementById('userform').onsubmit = async function(e) { document.getElementById('loginform').onsubmit = async function(e) {
e.preventDefault(); e.preventDefault();
let form = this; let form = this;
if(!(form instanceof HTMLFormElement)) if(!(form instanceof HTMLFormElement))
throw new Error('Bad type for userform'); throw new Error('Bad type for loginform');
setVisibility('passwordform', true); setVisibility('passwordform', true);