Allow historical usernames to sign in
authorPhil Denhoff <phil@denhoff.ca>
Thu, 29 Jul 2021 05:07:58 +0000 (22:07 -0700)
committerPhil Denhoff <phil@denhoff.ca>
Thu, 29 Jul 2021 05:20:59 +0000 (22:20 -0700)
src/app/templates/auth/Auth.jsx

index 2be50fc9adb8a74c81afc0aa1ad6cb15b5c3df80..a16206d6c3e43bbb80de1536bef28b6e19a39df8 100644 (file)
@@ -13,8 +13,12 @@ import Spinner from '../../atoms/spinner/Spinner';
 
 import CinnySvg from '../../../../public/res/svg/cinny.svg';
 
-const USERNAME_REGEX = /^[a-z0-9_-]+$/;
-const BAD_USERNAME_ERROR = 'Username must contain only lowercase letters, numbers, dashes and underscores.';
+// This regex validates historical usernames, which don't satisy today's username requirements.
+// See https://matrix.org/docs/spec/appendices#id13 for more info.
+const LOCALPART_LOGIN_REGEX = /^[!-9|;-~]+$/;
+const LOCALPART_SIGNUP_REGEX = /^[a-z0-9_\-+./]+$/;
+const BAD_LOCALPART_ERROR = 'Username must contain only lowercase letters, numbers, dashes and underscores.';
+const USER_ID_TOO_LONG_ERROR = 'Your user ID, including the hostname, can\'t be more than 255 characters long.';
 
 const PASSWORD_REGEX = /.+/;
 const PASSWORD_STRENGHT_REGEX = /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\w\d\s:])([^\s]){8,16}$/;
@@ -52,6 +56,18 @@ function validateOnChange(e, regex, error) {
   document.getElementById('auth_submit-btn').disabled = false;
 }
 
+/**
+ * Normalizes a username into a standard format.
+ *
+ * Removes leading and trailing whitespaces and leading "@" symbols.
+ * @param {string} rawUsername A raw-input username, which may include invalid characters.
+ * @returns {string}
+ */
+function normalizeUsername(rawUsername) {
+  const noLeadingAt = rawUsername.indexOf('@') === 0 ? rawUsername.substr(1) : rawUsername;
+  return noLeadingAt.trim();
+}
+
 function Auth({ type }) {
   const [process, changeProcess] = useState(null);
   const usernameRef = useRef(null);
@@ -99,12 +115,17 @@ function Auth({ type }) {
     document.getElementById('auth_submit-btn').disabled = true;
     document.getElementById('auth_error').style.display = 'none';
 
-    if (!isValidInput(usernameRef.current.value, USERNAME_REGEX)) {
-      showBadInputError(usernameRef.current, BAD_USERNAME_ERROR);
+    /** @type {string} */
+    const rawUsername = usernameRef.current.value;
+    /** @type {string} */
+    const normalizedUsername = normalizeUsername(rawUsername);
+
+    if (!isValidInput(normalizedUsername, LOCALPART_LOGIN_REGEX)) {
+      showBadInputError(usernameRef.current, BAD_LOCALPART_ERROR);
       return;
     }
 
-    auth.login(usernameRef.current.value, homeserverRef.current.value, passwordRef.current.value)
+    auth.login(normalizedUsername, homeserverRef.current.value, passwordRef.current.value)
       .then(() => {
         document.getElementById('auth_submit-btn').disabled = false;
         window.location.replace('/');
@@ -122,8 +143,8 @@ function Auth({ type }) {
     document.getElementById('auth_submit-btn').disabled = true;
     document.getElementById('auth_error').style.display = 'none';
 
-    if (!isValidInput(usernameRef.current.value, USERNAME_REGEX)) {
-      showBadInputError(usernameRef.current, BAD_USERNAME_ERROR);
+    if (!isValidInput(usernameRef.current.value, LOCALPART_SIGNUP_REGEX)) {
+      showBadInputError(usernameRef.current, BAD_LOCALPART_ERROR);
       return;
     }
     if (!isValidInput(passwordRef.current.value, PASSWORD_STRENGHT_REGEX)) {
@@ -138,6 +159,10 @@ function Auth({ type }) {
       showBadInputError(emailRef.current, BAD_EMAIL_ERROR);
       return;
     }
+    if (`@${usernameRef.current.value}:${homeserverRef.current.value}`.length > 255) {
+      showBadInputError(usernameRef.current, USER_ID_TOO_LONG_ERROR);
+      return;
+    }
     register();
   }
 
@@ -171,7 +196,9 @@ function Auth({ type }) {
             <div className="username__wrapper">
               <Input
                 forwardRef={usernameRef}
-                onChange={(e) => validateOnChange(e, USERNAME_REGEX, BAD_USERNAME_ERROR)}
+                onChange={(e) => (type === 'login'
+                  ? validateOnChange(e, LOCALPART_LOGIN_REGEX, BAD_LOCALPART_ERROR)
+                  : validateOnChange(e, LOCALPART_SIGNUP_REGEX, BAD_LOCALPART_ERROR))}
                 id="auth_username"
                 label="Username"
                 required