CAPTCHA support
This commit is contained in:
parent
4191af1cbd
commit
b43d48d997
3
go.mod
3
go.mod
|
@ -3,6 +3,7 @@ module hectabit.org/burgerauth
|
||||||
go 1.21.5
|
go 1.21.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
centrifuge.hectabit.org/HectaBit/captcha v1.4.4
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/gin-contrib/sessions v1.0.1
|
github.com/gin-contrib/sessions v1.0.1
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
|
@ -22,6 +23,7 @@ require (
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.19.0 // indirect
|
github.com/go-playground/validator/v10 v10.19.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/gorilla/context v1.1.2 // indirect
|
github.com/gorilla/context v1.1.2 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/gorilla/sessions v1.2.2 // indirect
|
github.com/gorilla/sessions v1.2.2 // indirect
|
||||||
|
@ -48,6 +50,7 @@ require (
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
golang.org/x/arch v0.7.0 // indirect
|
golang.org/x/arch v0.7.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||||
|
golang.org/x/image v0.15.0 // indirect
|
||||||
golang.org/x/net v0.24.0 // indirect
|
golang.org/x/net v0.24.0 // indirect
|
||||||
golang.org/x/sys v0.19.0 // indirect
|
golang.org/x/sys v0.19.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -1,3 +1,5 @@
|
||||||
|
centrifuge.hectabit.org/HectaBit/captcha v1.4.4 h1:WLGIy4hkwwps8V1j+LFCfdMOrSEmhlEzFqvt9xdW/VY=
|
||||||
|
centrifuge.hectabit.org/HectaBit/captcha v1.4.4/go.mod h1:QptuIyO9HKPi/rXn6g/c7BOWIlq0hjbVGm6V732EVyo=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||||
github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA=
|
github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA=
|
||||||
|
@ -37,6 +39,8 @@ github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn
|
||||||
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
@ -125,6 +129,8 @@ golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
|
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
||||||
|
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|
128
main.go
128
main.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
@ -20,6 +21,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"centrifuge.hectabit.org/HectaBit/captcha"
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-contrib/sessions/cookie"
|
"github.com/gin-contrib/sessions/cookie"
|
||||||
|
@ -351,7 +353,36 @@ func main() {
|
||||||
})
|
})
|
||||||
|
|
||||||
router.GET("/signup", func(c *gin.Context) {
|
router.GET("/signup", func(c *gin.Context) {
|
||||||
c.HTML(200, "signup.html", gin.H{})
|
session := sessions.Default(c)
|
||||||
|
sessionid := genSalt(512)
|
||||||
|
session.Options(sessions.Options{
|
||||||
|
SameSite: 3,
|
||||||
|
})
|
||||||
|
data, err := captcha.New(500, 100)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR] Failed to generate captcha at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
||||||
|
c.String(500, "Failed to generate captcha")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
session.Set("captcha", data.Text)
|
||||||
|
session.Set("id", sessionid)
|
||||||
|
err = session.Save()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR] Failed to save session in /login at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
||||||
|
c.String(500, "Failed to save session")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var b64bytes bytes.Buffer
|
||||||
|
err = data.WriteImage(&b64bytes)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR] Failed to encode captcha at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
||||||
|
c.String(500, "Failed to encode captcha")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.HTML(200, "signup.html", gin.H{
|
||||||
|
"captcha_image": base64.StdEncoding.EncodeToString(b64bytes.Bytes()),
|
||||||
|
"unique_token": sessionid,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
router.GET("/logout", func(c *gin.Context) {
|
router.GET("/logout", func(c *gin.Context) {
|
||||||
|
@ -404,6 +435,7 @@ func main() {
|
||||||
router.POST("/api/signup", func(c *gin.Context) {
|
router.POST("/api/signup", func(c *gin.Context) {
|
||||||
var data map[string]interface{}
|
var data map[string]interface{}
|
||||||
err := c.ShouldBindJSON(&data)
|
err := c.ShouldBindJSON(&data)
|
||||||
|
session := sessions.Default(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
||||||
return
|
return
|
||||||
|
@ -412,6 +444,17 @@ func main() {
|
||||||
username := data["username"].(string)
|
username := data["username"].(string)
|
||||||
password := data["password"].(string)
|
password := data["password"].(string)
|
||||||
|
|
||||||
|
if data["unique_token"].(string) != session.Get("unique_token") {
|
||||||
|
c.JSON(403, gin.H{"error": "Invalid token"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if data["captcha"].(string) != session.Get("captcha") {
|
||||||
|
fmt.Println(data["captcha"])
|
||||||
|
fmt.Println(session.Get("captcha"))
|
||||||
|
c.JSON(401, gin.H{"error": "Captcha failed"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if username == "" || password == "" || len(username) > 20 || !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(username) {
|
if username == "" || password == "" || len(username) > 20 || !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(username) {
|
||||||
c.JSON(422, gin.H{"error": "Invalid username or password"})
|
c.JSON(422, gin.H{"error": "Invalid username or password"})
|
||||||
return
|
return
|
||||||
|
@ -607,89 +650,6 @@ func main() {
|
||||||
c.JSON(200, gin.H{"sub": uniqueid[:255], "name": username})
|
c.JSON(200, gin.H{"sub": uniqueid[:255], "name": username})
|
||||||
})
|
})
|
||||||
|
|
||||||
router.POST("/api/submitkey", func(c *gin.Context) {
|
|
||||||
session := sessions.Default(c)
|
|
||||||
session.Options(sessions.Options{
|
|
||||||
SameSite: 3,
|
|
||||||
})
|
|
||||||
|
|
||||||
var data map[string]interface{}
|
|
||||||
err := c.ShouldBindJSON(&data)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
token := data["access_token"].(string)
|
|
||||||
|
|
||||||
conn := get_db_connection()
|
|
||||||
defer func(conn *sql.DB) {
|
|
||||||
err := conn.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Println("[ERROR] Unknown in /userinfo defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
||||||
c.String(500, "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more detail. Include this error code: cannot_close_db.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}(conn)
|
|
||||||
var blacklisted bool
|
|
||||||
err = conn.QueryRow("SELECT blacklisted FROM blacklist WHERE openid = ? LIMIT 1", token).Scan(&blacklisted)
|
|
||||||
if err == nil {
|
|
||||||
c.JSON(400, gin.H{"error": "Token is in blacklist"})
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
if !errors.Is(err, sql.ErrNoRows) {
|
|
||||||
log.Println("[ERROR] Unknown in /userinfo blacklist at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedtoken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
|
|
||||||
return publicKey, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(401, gin.H{"error": "Malformed token"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var claims jwt.MapClaims
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if parsedtoken.Valid {
|
|
||||||
claims, ok = parsedtoken.Claims.(jwt.MapClaims)
|
|
||||||
if !ok {
|
|
||||||
c.JSON(401, gin.H{"error": "Invalid token claims"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
usersession := claims["session"].(string)
|
|
||||||
exp := claims["exp"].(float64)
|
|
||||||
if int64(exp) < time.Now().Unix() {
|
|
||||||
c.JSON(403, gin.H{"error": "Expired token"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userid, norows := get_user_from_session(usersession)
|
|
||||||
if norows {
|
|
||||||
c.JSON(400, gin.H{"error": "Session does not exist"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _, _, _, norows = get_user(userid)
|
|
||||||
if norows {
|
|
||||||
c.JSON(400, gin.H{"error": "User does not exist"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
session.Set("key", data["public_key"].(string))
|
|
||||||
c.JSON(200, gin.H{"success": "true"})
|
|
||||||
})
|
|
||||||
|
|
||||||
router.POST("/api/getkey", func(c *gin.Context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
router.POST("/api/uniqueid", func(c *gin.Context) {
|
router.POST("/api/uniqueid", func(c *gin.Context) {
|
||||||
var data map[string]interface{}
|
var data map[string]interface{}
|
||||||
err := c.ShouldBindJSON(&data)
|
err := c.ShouldBindJSON(&data)
|
||||||
|
|
|
@ -19,6 +19,8 @@ let usernameBox = document.getElementById("usernameBox")
|
||||||
let passwordBox = document.getElementById("passwordBox")
|
let passwordBox = document.getElementById("passwordBox")
|
||||||
let statusBox = document.getElementById("statusBox")
|
let statusBox = document.getElementById("statusBox")
|
||||||
let signupButton = document.getElementById("signupButton")
|
let signupButton = document.getElementById("signupButton")
|
||||||
|
let captchaBox = document.getElementById("captchaBox")
|
||||||
|
let unique_token = document.getElementById("passthrough").innerText
|
||||||
|
|
||||||
function showElements(yesorno) {
|
function showElements(yesorno) {
|
||||||
if (!yesorno) {
|
if (!yesorno) {
|
||||||
|
@ -37,12 +39,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.getElementById("homeserver").innerText = "Your homeserver is: " + remote + ". "
|
document.getElementById("homeserver").innerText = "Your homeserver is: " + remote + ". "
|
||||||
});
|
});
|
||||||
|
|
||||||
signupButton.addEventListener("click", (event) => {
|
signupButton.addEventListener("click", () => {
|
||||||
async function doStuff() {
|
async function doStuff() {
|
||||||
let username = usernameBox.value
|
let username = usernameBox.value
|
||||||
let password = passwordBox.value
|
let password = passwordBox.value
|
||||||
|
let captcha = captchaBox.value
|
||||||
|
|
||||||
if (username == "") {
|
if (username === "") {
|
||||||
statusBox.innerText = "A username is required!"
|
statusBox.innerText = "A username is required!"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -50,7 +53,7 @@ signupButton.addEventListener("click", (event) => {
|
||||||
statusBox.innerText = "Username cannot be more than 20 characters!"
|
statusBox.innerText = "Username cannot be more than 20 characters!"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (password == "") {
|
if (password === "") {
|
||||||
statusBox.innerText = "A password is required!"
|
statusBox.innerText = "A password is required!"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -58,6 +61,10 @@ signupButton.addEventListener("click", (event) => {
|
||||||
statusBox.innerText = "8 or more characters are required!"
|
statusBox.innerText = "8 or more characters are required!"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (captcha === "") {
|
||||||
|
statusBox.innerText = "Please complete the captcha!"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
showElements(false)
|
showElements(false)
|
||||||
statusBox.innerText = "Creating account, please hold on..."
|
statusBox.innerText = "Creating account, please hold on..."
|
||||||
|
@ -68,14 +75,16 @@ signupButton.addEventListener("click", (event) => {
|
||||||
key = await hashwasm.sha3(key)
|
key = await hashwasm.sha3(key)
|
||||||
}
|
}
|
||||||
return key
|
return key
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
fetch(remote + "/api/signup", {
|
fetch(remote + "/api/signup", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username: username,
|
username: username,
|
||||||
password: await hashpass(password)
|
password: await hashpass(password),
|
||||||
|
captcha: captcha,
|
||||||
|
unique_token: unique_token
|
||||||
}),
|
}),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json; charset=UTF-8"
|
"Content-Type": "application/json; charset=UTF-8"
|
||||||
|
@ -87,14 +96,14 @@ signupButton.addEventListener("click", (event) => {
|
||||||
let responseData = await response.json()
|
let responseData = await response.json()
|
||||||
console.log(responseData)
|
console.log(responseData)
|
||||||
|
|
||||||
if (response.status == 200) {
|
if (response.status === 200) {
|
||||||
statusBox.innerText == "redirecting.."
|
statusBox.innerText = "Redirecting..."
|
||||||
localStorage.setItem("DONOTSHARE-secretkey", responseData["key"])
|
localStorage.setItem("DONOTSHARE-secretkey", responseData["key"])
|
||||||
localStorage.setItem("DONOTSHARE-password", await hashwasm.sha512(password))
|
localStorage.setItem("DONOTSHARE-password", await hashwasm.sha512(password))
|
||||||
|
|
||||||
window.location.href = "/app" + window.location.search
|
window.location.href = "/app" + window.location.search
|
||||||
}
|
}
|
||||||
else if (response.status == 409) {
|
else if (response.status === 409) {
|
||||||
statusBox.innerText = "Username already taken!"
|
statusBox.innerText = "Username already taken!"
|
||||||
showElements(true)
|
showElements(true)
|
||||||
}
|
}
|
||||||
|
@ -112,15 +121,13 @@ signupButton.addEventListener("click", (event) => {
|
||||||
document.getElementById("loginButton").addEventListener("click", function(event) {
|
document.getElementById("loginButton").addEventListener("click", function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
var queryString = window.location.search;
|
const queryString = window.location.search;
|
||||||
var newURL = "/login" + queryString;
|
window.location.href = "/login" + queryString;
|
||||||
window.location.href = newURL;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("privacyButton").addEventListener("click", function(event) {
|
document.getElementById("privacyButton").addEventListener("click", function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
var queryString = window.location.search;
|
const queryString = window.location.search;
|
||||||
var newURL = "/privacy" + queryString;
|
window.location.href = "/privacy" + queryString;
|
||||||
window.location.href = newURL;
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,13 +13,17 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<p class="credit">Image by perga (@pergagreen on discord)</p>
|
<p class="credit">Image by perga (@pergagreen on discord)</p>
|
||||||
|
<p style="display: none;" id="passthrough">{{ .unique_token }}</p>
|
||||||
<img src="/static/img/background.jpg" class="background" alt="">
|
<img src="/static/img/background.jpg" class="background" alt="">
|
||||||
<div class="inoutdiv">
|
<div class="inoutdiv">
|
||||||
<h2 class="w300">Signup</h2>
|
<h2 class="w300">Signup</h2>
|
||||||
<p>Signup for an account</p>
|
<p>Signup for an account</p>
|
||||||
<p id="statusBox"></p>
|
<p id="statusBox"></p>
|
||||||
<input id="usernameBox" type="text" placeholder="Username">
|
<input id="usernameBox" type="text" placeholder="Username">
|
||||||
<input id="passwordBox" type="password" placeholder="Password"><br>
|
<input id="passwordBox" type="password" placeholder="Password">
|
||||||
|
<img src="data:image/png;base64,{{ .captcha_image }}" alt="Captcha">
|
||||||
|
<input id="captchaBox" type="text" placeholder="Captcha">
|
||||||
|
<br>
|
||||||
<button id="signupButton">Signup</button><br><br>
|
<button id="signupButton">Signup</button><br><br>
|
||||||
<p>Already have an account? If so, <a href="/login" id="loginButton">Login</a> instead!</p>
|
<p>Already have an account? If so, <a href="/login" id="loginButton">Login</a> instead!</p>
|
||||||
<p>Please note that it's impossible to reset your password, do not forget it!</p>
|
<p>Please note that it's impossible to reset your password, do not forget it!</p>
|
||||||
|
|
Reference in New Issue