summary refs log tree commit diff
path: root/synapse/res/username_picker/script.js
diff options
context:
space:
mode:
authorRichard van der Hoff <1389908+richvdh@users.noreply.github.com>2020-12-18 14:19:46 +0000
committerGitHub <noreply@github.com>2020-12-18 14:19:46 +0000
commit28877fade90a5cfb3457c9e6c70924dbbe8af715 (patch)
tree77e5c2e21144fb58026b98a6ad340b95a7419bb4 /synapse/res/username_picker/script.js
parentAllow re-using a UI auth validation for a period of time (#8970) (diff)
downloadsynapse-28877fade90a5cfb3457c9e6c70924dbbe8af715.tar.xz
Implement a username picker for synapse (#8942)
The final part (for now) of my work to implement a username picker in synapse itself. The idea is that we allow
`UsernameMappingProvider`s to return `localpart=None`, in which case, rather than redirecting the browser
back to the client, we redirect to a username-picker resource, which allows the user to enter a username.
We *then* complete the SSO flow (including doing the client permission checks).

The static resources for the username picker itself (in 
https://github.com/matrix-org/synapse/tree/rav/username_picker/synapse/res/username_picker)
are essentially lifted wholesale from
https://github.com/matrix-org/matrix-synapse-saml-mozilla/tree/master/matrix_synapse_saml_mozilla/res. 
As the comment says, we might want to think about making them customisable, but that can be a follow-up. 

Fixes #8876.
Diffstat (limited to 'synapse/res/username_picker/script.js')
-rw-r--r--synapse/res/username_picker/script.js95
1 files changed, 95 insertions, 0 deletions
diff --git a/synapse/res/username_picker/script.js b/synapse/res/username_picker/script.js
new file mode 100644
index 0000000000..416a7c6f41
--- /dev/null
+++ b/synapse/res/username_picker/script.js
@@ -0,0 +1,95 @@
+let inputField = document.getElementById("field-username");
+let inputForm = document.getElementById("form");
+let submitButton = document.getElementById("button-submit");
+let message = document.getElementById("message");
+
+// Submit username and receive response
+function showMessage(messageText) {
+    // Unhide the message text
+    message.classList.remove("hidden");
+
+    message.textContent = messageText;
+};
+
+function doSubmit() {
+    showMessage("Success. Please wait a moment for your browser to redirect.");
+
+    // remove the event handler before re-submitting the form.
+    delete inputForm.onsubmit;
+    inputForm.submit();
+}
+
+function onResponse(response) {
+    // Display message
+    showMessage(response);
+
+    // Enable submit button and input field
+    submitButton.classList.remove('button--disabled');
+    submitButton.value = "Submit";
+};
+
+let allowedUsernameCharacters = RegExp("[^a-z0-9\\.\\_\\=\\-\\/]");
+function usernameIsValid(username) {
+    return !allowedUsernameCharacters.test(username);
+}
+let allowedCharactersString = "lowercase letters, digits, ., _, -, /, =";
+
+function buildQueryString(params) {
+    return Object.keys(params)
+        .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
+        .join('&');
+}
+
+function submitUsername(username) {
+    if(username.length == 0) {
+        onResponse("Please enter a username.");
+        return;
+    }
+    if(!usernameIsValid(username)) {
+        onResponse("Invalid username. Only the following characters are allowed: " + allowedCharactersString);
+        return;
+    }
+
+    // if this browser doesn't support fetch, skip the availability check.
+    if(!window.fetch) {
+        doSubmit();
+        return;
+    }
+
+    let check_uri = 'check?' + buildQueryString({"username": username});
+    fetch(check_uri, {
+        // include the cookie
+        "credentials": "same-origin",
+    }).then((response) => {
+        if(!response.ok) {
+            // for non-200 responses, raise the body of the response as an exception
+            return response.text().then((text) => { throw text; });
+        } else {
+            return response.json();
+        }
+    }).then((json) => {
+        if(json.error) {
+            throw json.error;
+        } else if(json.available) {
+            doSubmit();
+        } else {
+            onResponse("This username is not available, please choose another.");
+        }
+    }).catch((err) => {
+        onResponse("Error checking username availability: " + err);
+    });
+}
+
+function clickSubmit() {
+    event.preventDefault();
+    if(submitButton.classList.contains('button--disabled')) { return; }
+
+    // Disable submit button and input field
+    submitButton.classList.add('button--disabled');
+
+    // Submit username
+    submitButton.value = "Checking...";
+    submitUsername(inputField.value);
+};
+
+inputForm.onsubmit = clickSubmit;