From 979c2b8b5b9d3e3b1c79606b63264dbc93566c36 Mon Sep 17 00:00:00 2001 From: f0x Date: Thu, 4 Apr 2019 18:33:25 +0200 Subject: [PATCH] .well-known flow implementation --- app.js | 57 +++------------ components/Login.js | 158 +++++++++++++++++++++++++++++++++++++++++ public/scss/style.scss | 12 +++- 3 files changed, 176 insertions(+), 51 deletions(-) create mode 100644 components/Login.js diff --git a/app.js b/app.js index 0a548e2..5d515c0 100644 --- a/app.js +++ b/app.js @@ -9,6 +9,7 @@ const Matrix = require('./backends/matrix.js') const Sidebar = require('./components/sidebar.js') +const Login = require('./components/Login.js') const Info = require('./components/info.js') const Chat = require('./components/chat.js') const Input = require('./components/input.js') @@ -40,60 +41,18 @@ let App = create({ }, 100) }, - login: function() { - let user = document.getElementById("user").value - let pass = document.getElementById("pass").value - let hs = document.getElementById("hs").value - this.setState({apiUrl: hs}) - hs = urllib.parse(hs) - - let data = { - user: user, - password: pass, - type: "m.login.password", - initial_device_display_name: "Neo v4", - }; - - let url = urllib.format(Object.assign(hs, { - pathname: "/_matrix/client/r0/login" - })); - - fetch(url, { - body: JSON.stringify(data), - headers: { - 'content-type': 'application/json' - }, - method: 'POST', - }).then((response) => response.json()) - .then((responseJson) => { - this.setState({json: responseJson}); - if(responseJson.access_token != undefined) { - let backend = new Matrix(responseJson.user_id, responseJson.access_token, this.state.apiUrl) - this.setState({ - backend: backend - }) - this.checkBackend() - } - }) - .catch((error) => { - console.error(error); - }); + loginCallback: function(userId, accessToken, apiUrl) { + let backend = new Matrix(userId, accessToken, apiUrl) + this.setState({ + backend: backend + }) + this.checkBackend() }, render: function() { if (this.state.backend == undefined) { //Login screen - return ( -
- -
- - - - -
-
- ) + return } return ( <> diff --git a/components/Login.js b/components/Login.js new file mode 100644 index 0000000..81194e6 --- /dev/null +++ b/components/Login.js @@ -0,0 +1,158 @@ +'use strict' +const React = require('react') +const ReactDOM = require('react-dom') +const create = require('create-react-class') +const Promise = require('bluebird') +const urllib = require('url') +const debounce = require('debounce') +const defaultValue = require('default-value') + +let login = create({ + displayName: "Login", + + getInitialState: function() { + return {error: null} + }, + + login: function() { + let user = document.getElementById("user").value + let pass = document.getElementById("pass").value + + let parts = user.split(':') + if (parts.length != 2) { + return this.setState({error: "Please enter a full mxid, like @username:homeserver.tld"}) + } + + let hostname = urllib.parse("https://" + parts[1]) + getApiServer(hostname).then((hostname) => { + hostname.pathname = "" + let hs = urllib.format(hostname) + console.log("Using API server", hs) + this.setState({apiUrl: hs}) + //this.login() + }) + }, + + login: function() { + let data = { + user: this.state.user, + password: this.state.password, + type: "m.login.password", + initial_device_display_name: "Neo v4", + }; + + let url = urllib.format(Object.assign(this.state.apiUrl, { + pathname: "/_matrix/client/r0/login" + })); + + fetch(url, { + body: JSON.stringify(data), + headers: { + 'content-type': 'application/json' + }, + method: 'POST', + }).then((response) => response.json()) + .then((responseJson) => { + this.setState({json: responseJson}); + if(responseJson.access_token != undefined) { + this.props.loginCallback() + } + }) + .catch((error) => { + console.error(error); + }); + }, + + render: function() { + return ( +
+ +
{this.state.error != null && this.state.error}
+
+ + + + + + + {this.state.promptHS && + <> + + + + } + + +
+
+ ) + } +}) + +function getApiServer(hostname) { + return new Promise((resolve, reject) => { + console.log("Checking for api server from mxid", urllib.format(hostname)) + checkApi(hostname).then(() => { + // Hostname is a valid api server + resolve(hostname) + }).catch(() => { + console.log("trying .well-known") + tryWellKnown(hostname).then((hostname) => { + console.log("got .well-known host", hostname) + }).catch((err) => { + console.log("Fatal error trying to get API host", err) + }) + }) + }) +} + +function checkApi(host) { + let versionUrl = buildUrl(host, "/_matrix/client/versions") + return new Promise((resolve, reject) => { + fetch(versionUrl).then((response) => { + if (response.status != 200) { + console.log("Invalid homeserver url", versionUrl) + return reject() + } + resolve() + }).catch((err) => { + reject(err) + }) + }) +} + +function tryWellKnown(host) { + let wellKnownUrl = urllib.format(Object.assign(host, { + pathname: "/.well-known/matrix/client" + })) + console.log("Trying", wellKnownUrl, "for .well-known") + return new Promise((resolve, reject) => { + return fetch(wellKnownUrl) + .then((response) => { + if (response.status != 200) { + console.log("no well-known in use") + reject() + } + return response + }) + .then((response) => response.json()) + .then((json) => { + console.log("Parsed json", json) + if (json['m.homeserver'] != undefined && json['m.homeserver'].base_url != undefined) { + resolve(json['m.homeserver'].base_url) + } + }) + .catch((err) => { + console.log("Error in json", err) + reject() + }) + }) +} + +function buildUrl(host, path) { + return urllib.format(Object.assign(host, { + pathname: path + })) +} + +module.exports = login diff --git a/public/scss/style.scss b/public/scss/style.scss index a5a5304..dcaad54 100644 --- a/public/scss/style.scss +++ b/public/scss/style.scss @@ -57,6 +57,10 @@ body { justify-content: center; flex-direction: column; + input, label, button { + font-size: 140%; + } + img { height: 15rem; width: 15rem; @@ -65,6 +69,10 @@ body { } } +.error { + color: $red; +} + .login { align-self: center; display: inline-grid; @@ -73,8 +81,8 @@ body { $blue: #5294e2; label, input, button { - margin: 0.3rem; - padding: 0.3rem; + margin: 0.3em; + padding: 0.3em; } label {