177 lines
5.3 KiB
Go
177 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"time"
|
|
|
|
"crypto/ed25519"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"syscall/js"
|
|
|
|
"git.ailur.dev/ailur/jsFetch"
|
|
"golang.org/x/crypto/argon2"
|
|
)
|
|
|
|
func main() {
|
|
localStorage := js.Global().Get("localStorage")
|
|
status := js.Global().Get("document").Call("getElementById", "status")
|
|
loginButton := js.Global().Get("document").Call("getElementById", "login")
|
|
username := js.Global().Get("document").Call("getElementById", "username")
|
|
password := js.Global().Get("document").Call("getElementById", "password")
|
|
|
|
if localStorage.Call("getItem", "server").IsNull() {
|
|
localStorage.Call("setItem", "server", "https://chat.ailur.dev:1974")
|
|
}
|
|
|
|
if !localStorage.Call("getItem", "token").IsNull() {
|
|
js.Global().Get("location").Set("href", "../")
|
|
return
|
|
}
|
|
|
|
loginButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
|
go func() {
|
|
username.Set("disabled", true)
|
|
password.Set("disabled", true)
|
|
loginButton.Set("disabled", true)
|
|
status.Set("innerText", "Generating encryption keys...")
|
|
hash := argon2.IDKey(
|
|
[]byte(password.Get("value").String()),
|
|
[]byte(username.Get("value").String()),
|
|
32,
|
|
19264,
|
|
1,
|
|
32,
|
|
)
|
|
|
|
status.Set("innerText", "Retrieving challenge from server...")
|
|
|
|
jsonBody, err := json.Marshal(map[string]interface{}{
|
|
"username": username.Get("value").String(),
|
|
})
|
|
if err != nil {
|
|
status.Set("innerText", "Failed to encode request: "+err.Error())
|
|
username.Set("disabled", false)
|
|
password.Set("disabled", false)
|
|
loginButton.Set("disabled", false)
|
|
return
|
|
}
|
|
|
|
response, err := jsFetch.Post(localStorage.Call("getItem", "server").String()+"/api/loginChallenge", "application/json", bytes.NewReader(jsonBody))
|
|
if err != nil {
|
|
status.Set("innerText", "Failed to contact server: "+err.Error())
|
|
username.Set("disabled", false)
|
|
password.Set("disabled", false)
|
|
loginButton.Set("disabled", false)
|
|
return
|
|
}
|
|
|
|
if response.StatusCode != 200 {
|
|
if response.StatusCode == 500 {
|
|
status.Set("innerText", "Something went wrong on our end. Please try again later.")
|
|
} else {
|
|
var body map[string]interface{}
|
|
err := json.NewDecoder(response.Body).Decode(&body)
|
|
if err != nil {
|
|
status.Set("innerText", "Failed to decode response: "+err.Error())
|
|
} else {
|
|
status.Set("innerText", body["error"].(string))
|
|
}
|
|
}
|
|
username.Set("disabled", false)
|
|
password.Set("disabled", false)
|
|
loginButton.Set("disabled", false)
|
|
return
|
|
}
|
|
|
|
status.Set("innerText", "Signing challenge...")
|
|
var body map[string]interface{}
|
|
err = json.NewDecoder(response.Body).Decode(&body)
|
|
if err != nil {
|
|
status.Set("innerText", "Failed to decode response: "+err.Error())
|
|
username.Set("disabled", false)
|
|
password.Set("disabled", false)
|
|
loginButton.Set("disabled", false)
|
|
return
|
|
}
|
|
|
|
challenge, err := base64.StdEncoding.DecodeString(body["nonce"].(string))
|
|
if err != nil {
|
|
status.Set("innerText", "Failed to decode challenge: "+err.Error())
|
|
username.Set("disabled", false)
|
|
password.Set("disabled", false)
|
|
loginButton.Set("disabled", false)
|
|
return
|
|
}
|
|
|
|
signature := ed25519.Sign(ed25519.NewKeyFromSeed(hash), challenge)
|
|
|
|
status.Set("innerText", "Contacting server...")
|
|
|
|
jsonBody, err = json.Marshal(map[string]interface{}{
|
|
"username": username.Get("value").String(),
|
|
"signature": base64.StdEncoding.EncodeToString(signature),
|
|
"challenge": body["token"].(string),
|
|
})
|
|
if err != nil {
|
|
status.Set("innerText", "Failed to encode request: "+err.Error())
|
|
username.Set("disabled", false)
|
|
password.Set("disabled", false)
|
|
loginButton.Set("disabled", false)
|
|
return
|
|
}
|
|
|
|
response, err = jsFetch.Post(localStorage.Call("getItem", "server").String()+"/api/login", "application/json", bytes.NewReader(jsonBody))
|
|
if err != nil {
|
|
status.Set("innerText", "Failed to contact server: "+err.Error())
|
|
username.Set("disabled", false)
|
|
password.Set("disabled", false)
|
|
loginButton.Set("disabled", false)
|
|
return
|
|
}
|
|
|
|
if response.StatusCode != 200 {
|
|
if response.StatusCode == 500 {
|
|
status.Set("innerText", "Something went wrong on our end. Please try again later.")
|
|
} else {
|
|
var body map[string]interface{}
|
|
err := json.NewDecoder(response.Body).Decode(&body)
|
|
if err != nil {
|
|
status.Set("innerText", "Failed to decode response: "+err.Error())
|
|
} else {
|
|
status.Set("innerText", body["error"].(string))
|
|
}
|
|
}
|
|
username.Set("disabled", false)
|
|
password.Set("disabled", false)
|
|
loginButton.Set("disabled", false)
|
|
return
|
|
}
|
|
|
|
err = json.NewDecoder(response.Body).Decode(&body)
|
|
if err != nil {
|
|
status.Set("innerText", "Failed to decode response: "+err.Error())
|
|
username.Set("disabled", false)
|
|
password.Set("disabled", false)
|
|
loginButton.Set("disabled", false)
|
|
return
|
|
}
|
|
|
|
localStorage.Call("setItem", "token", body["token"].(string))
|
|
localStorage.Call("setItem", "userId", body["userId"].(string))
|
|
_, exists := body["admin"]
|
|
if exists {
|
|
localStorage.Call("setItem", "admin", "true")
|
|
} else {
|
|
localStorage.Call("setItem", "admin", "false")
|
|
}
|
|
|
|
status.Set("innerText", "Welcome, "+username.Get("value").String()+", to LChat!")
|
|
time.Sleep(300 * time.Millisecond)
|
|
js.Global().Get("location").Set("href", "../")
|
|
}()
|
|
return nil
|
|
}))
|
|
|
|
select {}
|
|
}
|