lchat/web/wasm/login.go
2024-11-12 19:34:06 +00:00

172 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", "username", username.Get("value").String())
localStorage.Call("setItem", "userId", body["userId"].(string))
status.Set("innerText", "Welcome, "+username.Get("value").String()+", to LChat!")
time.Sleep(300 * time.Millisecond)
js.Global().Get("location").Set("href", "../")
}()
return nil
}))
select {}
}