1378 lines
49 KiB
Go
1378 lines
49 KiB
Go
// This code is licensed under the latest version of the GNU Affero General Public License
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"database/sql"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"math/big"
|
|
"os"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"centrifuge.hectabit.org/HectaBit/captcha"
|
|
"github.com/dgrijalva/jwt-go"
|
|
"github.com/gin-contrib/sessions"
|
|
"github.com/gin-contrib/sessions/cookie"
|
|
"github.com/gin-gonic/gin"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
"github.com/spf13/viper"
|
|
"golang.org/x/crypto/scrypt"
|
|
)
|
|
|
|
var (
|
|
conn *sql.DB
|
|
privateKey *rsa.PrivateKey
|
|
publicKey *rsa.PublicKey
|
|
modulus *big.Int
|
|
exponent int
|
|
)
|
|
|
|
func Int64ToBase64URL(num int64) (string, error) {
|
|
numBytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(numBytes, uint64(num))
|
|
startIndex := 0
|
|
for startIndex < len(numBytes) && numBytes[startIndex] == 0 {
|
|
startIndex++
|
|
}
|
|
trimmedBytes := numBytes[startIndex:]
|
|
encoded := base64.URLEncoding.EncodeToString(trimmedBytes)
|
|
return encoded, nil
|
|
}
|
|
|
|
func BigIntToBase64URL(num *big.Int) (string, error) {
|
|
numBytes := num.Bytes()
|
|
startIndex := 0
|
|
for startIndex < len(numBytes) && numBytes[startIndex] == 0 {
|
|
startIndex++
|
|
}
|
|
trimmedBytes := numBytes[startIndex:]
|
|
encoded := base64.URLEncoding.EncodeToString(trimmedBytes)
|
|
return encoded, nil
|
|
}
|
|
|
|
const salt_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
|
|
func genSalt(length int) (string, error) {
|
|
if length <= 0 {
|
|
return "", errors.New("salt length must be greater than 0")
|
|
}
|
|
|
|
salt := make([]byte, length)
|
|
randomBytes := make([]byte, length)
|
|
_, err := rand.Read(randomBytes)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for i := range salt {
|
|
salt[i] = salt_chars[int(randomBytes[i])%len(salt_chars)]
|
|
}
|
|
return string(salt), nil
|
|
}
|
|
|
|
func sha256Base64(s string) string {
|
|
hashed := sha256.Sum256([]byte(s))
|
|
encoded := base64.URLEncoding.EncodeToString(hashed[:])
|
|
encoded = strings.TrimRight(encoded, "=")
|
|
return encoded
|
|
}
|
|
|
|
func hash(password, salt string) (string, error) {
|
|
passwordBytes := []byte(password)
|
|
saltBytes := []byte(salt)
|
|
|
|
derivedKey, err := scrypt.Key(passwordBytes, saltBytes, 32768, 8, 1, 64)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
hashString := fmt.Sprintf("scrypt:32768:8:1$%s$%s", salt, hex.EncodeToString(derivedKey))
|
|
return hashString, nil
|
|
}
|
|
|
|
func verifyHash(werkzeug_hash, password string) (bool, error) {
|
|
parts := strings.Split(werkzeug_hash, "$")
|
|
if len(parts) != 3 || parts[0] != "scrypt:32768:8:1" {
|
|
return false, nil
|
|
}
|
|
salt := parts[1]
|
|
computedHash, err := hash(password, salt)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return werkzeug_hash == computedHash, nil
|
|
}
|
|
|
|
func getUser(id int) (string, string, string, string, error) {
|
|
var created, username, password, uniqueId string
|
|
err := conn.QueryRow("SELECT created, username, uniqueId, password FROM users WHERE id = ? LIMIT 1", id).Scan(&created, &username, &uniqueId, &password)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return "", "", "", "", sql.ErrNoRows
|
|
} else {
|
|
return "", "", "", "", err
|
|
}
|
|
}
|
|
|
|
return created, username, password, uniqueId, nil
|
|
}
|
|
|
|
func getSession(session string) (int, int, error) {
|
|
var id, sessionId int
|
|
err := conn.QueryRow("SELECT sessionid, id FROM sessions WHERE session = ? LIMIT 1", session).Scan(&sessionId, &id)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return sessionId, id, nil
|
|
}
|
|
|
|
func checkUsernameTaken(username string) (int, bool, error) {
|
|
var id int
|
|
err := conn.QueryRow("SELECT id FROM users WHERE lower(username) = ? LIMIT 1", username).Scan(&id)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return 0, true, nil
|
|
} else {
|
|
return 0, true, err
|
|
}
|
|
}
|
|
|
|
return id, false, nil
|
|
}
|
|
|
|
func init_db() {
|
|
if _, err := os.Stat("database.db"); os.IsNotExist(err) {
|
|
if err := generateDB(); err != nil {
|
|
log.Println("[ERROR] Unknown while generating database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
return
|
|
}
|
|
} else {
|
|
log.Print("Proceeding will overwrite the database. Proceed? (y/n) ")
|
|
var answer string
|
|
_, err := fmt.Scanln(&answer)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown while scanning input at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
return
|
|
}
|
|
if answer == "y" || answer == "Y" {
|
|
if err := generateDB(); err != nil {
|
|
log.Println("[ERROR] Unknown while generating database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
return
|
|
}
|
|
} else if answer == ":3" {
|
|
log.Println("[:3] :3")
|
|
} else {
|
|
log.Println("[INFO] Stopped")
|
|
}
|
|
}
|
|
}
|
|
|
|
func generateDB() error {
|
|
db, err := sql.Open("sqlite3", "database.db")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func(db *sql.DB) {
|
|
err := db.Close()
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in generateDB() defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
return
|
|
}
|
|
}(db)
|
|
|
|
schemaBytes, err := os.ReadFile("schema.sql")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = db.Exec(string(schemaBytes))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Println("[INFO] Generated database")
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
if _, err := os.Stat("config.ini"); err == nil {
|
|
log.Println("[INFO] Config loaded at", time.Now().Unix())
|
|
} else if os.IsNotExist(err) {
|
|
log.Println("[FATAL] config.ini does not exist")
|
|
os.Exit(1)
|
|
} else {
|
|
log.Println("[FATAL] File is in quantum uncertainty:", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
viper.SetConfigName("config")
|
|
viper.AddConfigPath("./")
|
|
viper.AutomaticEnv()
|
|
|
|
err := viper.ReadInConfig()
|
|
if err != nil {
|
|
log.Println("[FATAL] Error in config file at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
HOST := viper.GetString("config.HOST")
|
|
PORT := viper.GetInt("config.PORT")
|
|
SECRET_KEY := viper.GetString("config.SECRET_KEY")
|
|
PUBLIC_KEY_PATH := viper.GetString("config.PUBLIC_KEY")
|
|
PRIVATE_KEY_PATH := viper.GetString("config.PRIVATE_KEY")
|
|
|
|
if SECRET_KEY == "supersecretkey" {
|
|
log.Println("[WARNING] Secret key not set. Please set the secret key to a non-default value.")
|
|
}
|
|
|
|
conn, err = sql.Open("sqlite3", "database.db")
|
|
if err != nil {
|
|
log.Fatalln("[FATAL] Cannot open database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
}
|
|
defer func(conn *sql.DB) {
|
|
err := conn.Close()
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in main() defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
}
|
|
}(conn)
|
|
|
|
if len(os.Args) > 1 {
|
|
if os.Args[1] == "init_db" {
|
|
init_db()
|
|
os.Exit(0)
|
|
}
|
|
}
|
|
|
|
privateKeyFile, err := os.ReadFile(PRIVATE_KEY_PATH)
|
|
if err != nil {
|
|
log.Fatal("[ERROR] Cannot read private key:", err)
|
|
}
|
|
|
|
block, _ := pem.Decode(privateKeyFile)
|
|
if block == nil {
|
|
log.Fatal("[ERROR] Failed to parse PEM block containing the private key")
|
|
}
|
|
|
|
privateKeyRaw, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
|
if err != nil {
|
|
log.Fatal("[ERROR] Failed to parse private key:", err)
|
|
}
|
|
|
|
var ok bool
|
|
privateKey, ok = privateKeyRaw.(*rsa.PrivateKey)
|
|
if !ok {
|
|
log.Fatal("[ERROR] Failed to convert private key to RSA private key")
|
|
}
|
|
|
|
pubKeyFile, err := os.ReadFile(PUBLIC_KEY_PATH)
|
|
if err != nil {
|
|
log.Fatal("[ERROR] Cannot read public key:", err)
|
|
}
|
|
|
|
block, _ = pem.Decode(pubKeyFile)
|
|
if block == nil {
|
|
log.Fatal("[ERROR] Failed to parse PEM block containing the public key")
|
|
}
|
|
|
|
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
|
|
if err != nil {
|
|
log.Fatal("[ERROR] Failed to parse public key:", err)
|
|
}
|
|
|
|
publicKey, ok = pubKey.(*rsa.PublicKey)
|
|
if !ok {
|
|
log.Fatal("[ERROR] Failed to convert public key to RSA public key")
|
|
}
|
|
|
|
modulus = privateKey.N
|
|
exponent = privateKey.E
|
|
|
|
gin.SetMode(gin.ReleaseMode)
|
|
router := gin.New()
|
|
store := cookie.NewStore([]byte(SECRET_KEY))
|
|
router.Use(sessions.Sessions("currentSession", store))
|
|
|
|
// Enable CORS
|
|
router.Use(func(c *gin.Context) {
|
|
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "*, Authorization")
|
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "*")
|
|
|
|
// Handle preflight requests
|
|
if c.Request.Method == "OPTIONS" {
|
|
c.AbortWithStatus(200)
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
})
|
|
|
|
router.Static("/static", "./static")
|
|
|
|
router.LoadHTMLGlob("templates/*.html")
|
|
|
|
router.GET("/", func(c *gin.Context) {
|
|
c.Redirect(302, "/login")
|
|
})
|
|
|
|
router.GET("/login", func(c *gin.Context) {
|
|
c.HTML(200, "login.html", gin.H{})
|
|
})
|
|
|
|
router.GET("/signup", func(c *gin.Context) {
|
|
session := sessions.Default(c)
|
|
sessionId, err := genSalt(512)
|
|
if err != nil {
|
|
fmt.Println("[ERROR] Failed to generate session token 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 info. Your error code is: UNKNOWN-SIGNUP-SESSION-GEN")
|
|
return
|
|
}
|
|
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("unique_token", 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) {
|
|
c.HTML(200, "logout.html", gin.H{})
|
|
})
|
|
|
|
router.GET("/app", func(c *gin.Context) {
|
|
name := ""
|
|
if c.Request.URL.Query().Get("client_id") != "" {
|
|
appId := c.Request.URL.Query().Get("client_id")
|
|
err := conn.QueryRow("SELECT name FROM oauth WHERE appId = ? LIMIT 1", appId).Scan(&name)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
c.String(404, "App not found")
|
|
} else {
|
|
log.Println("[ERROR] Unknown in /app at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
c.HTML(200, "main.html", gin.H{"name": name})
|
|
})
|
|
|
|
router.GET("/dashboard", func(c *gin.Context) {
|
|
c.HTML(200, "dashboard.html", gin.H{})
|
|
})
|
|
|
|
router.GET("/account", func(c *gin.Context) {
|
|
c.HTML(200, "acct.html", gin.H{})
|
|
})
|
|
|
|
router.GET("/aeskeyshare", func(c *gin.Context) {
|
|
c.HTML(200, "aeskeyshare.html", gin.H{})
|
|
})
|
|
|
|
router.GET("/.well-known/openid-configuration", func(c *gin.Context) {
|
|
c.HTML(200, "openid.html", gin.H{})
|
|
})
|
|
|
|
router.GET("/api/version", func(c *gin.Context) {
|
|
c.String(200, "Burgerauth Version 1.3")
|
|
})
|
|
|
|
router.POST("/api/signup", func(c *gin.Context) {
|
|
var data map[string]interface{}
|
|
err := c.ShouldBindJSON(&data)
|
|
session := sessions.Default(c)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
return
|
|
}
|
|
|
|
username := data["username"].(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") {
|
|
c.JSON(401, gin.H{"error": "Captcha failed"})
|
|
return
|
|
}
|
|
|
|
if username == "" || password == "" || len(username) > 20 || !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(username) {
|
|
c.JSON(422, gin.H{"error": "Invalid username or password"})
|
|
return
|
|
}
|
|
|
|
_, taken, err := checkUsernameTaken(username)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/signup checkUsernameTaken() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SIGNUP-CHECKUSERNAME"})
|
|
return
|
|
}
|
|
if taken {
|
|
c.JSON(409, gin.H{"error": "Username taken"})
|
|
return
|
|
}
|
|
|
|
salt, err := genSalt(16)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/signup genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SIGNUP-SALT"})
|
|
return
|
|
}
|
|
hashedPassword, err := hash(password, salt)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/signup hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SIGNUP-HASH"})
|
|
return
|
|
}
|
|
|
|
sub, err := genSalt(255)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/signup genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SIGNUP-SUB"})
|
|
return
|
|
}
|
|
_, err = conn.Exec("INSERT INTO users (username, password, created, uniqueid) VALUES (?, ?, ?, ?)", username, hashedPassword, strconv.FormatInt(time.Now().Unix(), 10), sub)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/signup user creation at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
return
|
|
}
|
|
log.Println("[INFO] Added new user at", time.Now().Unix())
|
|
|
|
userid, _, err := checkUsernameTaken(username)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/signup checkUsernameTaken() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SIGNUP-CHECKUSERNAME"})
|
|
return
|
|
}
|
|
|
|
randomChars, err := genSalt(512)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/signup token genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SIGNUP-SESSIONSALT"})
|
|
return
|
|
}
|
|
|
|
_, err = conn.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", randomChars, userid, c.Request.Header.Get("User-Agent"))
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/signup session Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SIGNUP-SESSIONINSERT"})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, gin.H{"key": randomChars})
|
|
})
|
|
|
|
router.POST("/api/login", func(c *gin.Context) {
|
|
var data map[string]interface{}
|
|
err := c.ShouldBindJSON(&data)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
return
|
|
}
|
|
|
|
username := data["username"].(string)
|
|
password := data["password"].(string)
|
|
passwordChange := data["password"].(string)
|
|
newPass := data["password"].(string)
|
|
|
|
userid, taken, err := checkUsernameTaken(username)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/login checkUsernameTaken() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LOGIN-CHECKUSERNAME"})
|
|
return
|
|
}
|
|
if !taken {
|
|
c.JSON(401, gin.H{"error": "User does not exist"})
|
|
return
|
|
}
|
|
|
|
_, _, userPassword, _, err := getUser(userid)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/login getUser() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LOGIN-GETUSER"})
|
|
return
|
|
}
|
|
|
|
passwordCheck, err := verifyHash(userPassword, password)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/login password check at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LOGIN-PASSWORDCHECK"})
|
|
return
|
|
}
|
|
if !passwordCheck {
|
|
c.JSON(401, gin.H{"error": "Incorrect password"})
|
|
return
|
|
}
|
|
|
|
randomChars, err := genSalt(512)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/login token genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LOGIN-SESSIONSALT"})
|
|
return
|
|
}
|
|
|
|
_, err = conn.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", randomChars, userid, c.Request.Header.Get("User-Agent"))
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/login session creation at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LOGIN-SESSIONINSERT"})
|
|
return
|
|
}
|
|
|
|
if passwordChange == "yes" {
|
|
hashPassword, err := hash(newPass, "")
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/login password hash at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LOGIN-PASSWORDHASH"})
|
|
return
|
|
}
|
|
_, err = conn.Exec("UPDATE users SET password = ? WHERE username = ?", hashPassword, username)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/login password change at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LOGIN-PASSWORDCHANGE"})
|
|
return
|
|
}
|
|
}
|
|
|
|
c.JSON(200, gin.H{"key": randomChars})
|
|
})
|
|
|
|
router.POST("/api/userinfo", func(c *gin.Context) {
|
|
var data map[string]interface{}
|
|
err := c.ShouldBindJSON(&data)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
return
|
|
}
|
|
|
|
secretKey := data["secretKey"].(string)
|
|
|
|
_, userid, err := getSession(secretKey)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
c.JSON(401, gin.H{"error": "Invalid session"})
|
|
return
|
|
}
|
|
|
|
created, username, _, _, err := getUser(userid)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
c.JSON(400, gin.H{"error": "User does not exist"})
|
|
return
|
|
} else if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/userinfo getUser() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-USERINFO-GETUSER"})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, gin.H{"username": username, "id": userid, "created": created})
|
|
})
|
|
|
|
router.GET("/userinfo", func(c *gin.Context) {
|
|
token := strings.Fields(c.Request.Header["Authorization"][0])[1]
|
|
|
|
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)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-USERINFO-BLACKLIST"})
|
|
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
|
|
}
|
|
}
|
|
|
|
session := claims["session"].(string)
|
|
exp := claims["exp"].(float64)
|
|
if int64(exp) < time.Now().Unix() {
|
|
c.JSON(403, gin.H{"error": "Expired token"})
|
|
return
|
|
}
|
|
|
|
_, userid, err := getSession(session)
|
|
if err != nil {
|
|
c.JSON(401, gin.H{"error": "Invalid session"})
|
|
return
|
|
}
|
|
|
|
_, username, _, sub, err := getUser(userid)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
c.JSON(400, gin.H{"error": "User does not exist"})
|
|
return
|
|
} else if err != nil {
|
|
log.Println("[ERROR] Unknown in /userinfo getUser() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-USERINFO-GETUSER"})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, gin.H{"sub": sub[:255], "name": username})
|
|
})
|
|
|
|
router.POST("/api/uniqueid", func(c *gin.Context) {
|
|
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)
|
|
|
|
var blacklisted bool
|
|
err = conn.QueryRow("SELECT blacklisted FROM blacklist WHERE token = ? 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 /api/sub blacklist at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-UNIQUEID-BLACKLIST"})
|
|
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
|
|
}
|
|
}
|
|
|
|
session := claims["session"].(string)
|
|
exp := claims["exp"].(float64)
|
|
if int64(exp) < time.Now().Unix() {
|
|
c.JSON(403, gin.H{"error": "Expired token"})
|
|
return
|
|
}
|
|
|
|
_, userid, err := getSession(session)
|
|
if err != nil {
|
|
c.JSON(401, gin.H{"error": "Invalid session"})
|
|
return
|
|
}
|
|
|
|
_, _, _, sub, err := getUser(userid)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
c.JSON(400, gin.H{"error": "User does not exist"})
|
|
return
|
|
} else if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/userinfo getUser() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-UNIQUEID-GETUSER"})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, gin.H{"sub": sub})
|
|
})
|
|
|
|
router.POST("/api/loggedin", func(c *gin.Context) {
|
|
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)
|
|
var blacklisted bool
|
|
err = conn.QueryRow("SELECT blacklisted FROM blacklist WHERE token = ? 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 /api/loggedin blacklist at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LOGGEDIN-BLACKLIST"})
|
|
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
|
|
}
|
|
}
|
|
|
|
session := claims["session"].(string)
|
|
exp := claims["exp"].(float64)
|
|
if int64(exp) < time.Now().Unix() {
|
|
c.JSON(403, gin.H{"error": "Expired token"})
|
|
return
|
|
}
|
|
|
|
_, _, err = getSession(session)
|
|
if err != nil {
|
|
c.JSON(401, gin.H{"error": "Invalid session"})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, gin.H{"appId": claims["aud"]})
|
|
})
|
|
|
|
router.GET("/api/auth", func(c *gin.Context) {
|
|
secretKey, _ := c.Cookie("key")
|
|
appId := c.Request.URL.Query().Get("client_id")
|
|
code := c.Request.URL.Query().Get("code_challenge")
|
|
codeMethod := c.Request.URL.Query().Get("code_challenge_method")
|
|
redirect_uri := c.Request.URL.Query().Get("redirect_uri")
|
|
state := c.Request.URL.Query().Get("state")
|
|
nonce := c.Request.URL.Query().Get("nonce")
|
|
deny := c.Request.URL.Query().Get("deny")
|
|
|
|
var appIdCheck, redirectUriCheck string
|
|
|
|
err := conn.QueryRow("SELECT appId, rdiruri FROM oauth WHERE appId = ? LIMIT 1", appId).Scan(&appIdCheck, &redirectUriCheck)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
fmt.Println(appId)
|
|
c.String(401, "OAuth screening failed")
|
|
} else {
|
|
log.Println("[ERROR] Unknown in /api/auth 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 info. Your error code is: UNKNOWN-API-AUTH-SELECT")
|
|
}
|
|
return
|
|
}
|
|
|
|
if !(redirectUriCheck == redirect_uri) {
|
|
c.String(401, "Redirect URI does not match")
|
|
return
|
|
}
|
|
|
|
if deny == "true" {
|
|
c.Redirect(302, redirect_uri+"?error=access_denied&state="+state)
|
|
return
|
|
}
|
|
|
|
if !(appIdCheck == appId) {
|
|
fmt.Println(appIdCheck, appId)
|
|
c.String(401, "OAuth screening failed")
|
|
return
|
|
}
|
|
|
|
if nonce == "none" {
|
|
nonce, err = genSalt(512)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/auth nonce genSalt() 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 info. Your error code is: UNKNOWN-API-AUTH-NONCE")
|
|
return
|
|
}
|
|
}
|
|
|
|
_, userid, err := getSession(secretKey)
|
|
if err != nil {
|
|
c.String(401, "Invalid session")
|
|
return
|
|
}
|
|
|
|
_, username, _, sub, err := getUser(userid)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
c.String(400, "User does not exist")
|
|
return
|
|
} else if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/userinfo getUser() 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 info. Your error code is: UNKNOWN-API-AUTH-GETUSER")
|
|
return
|
|
}
|
|
|
|
dataTemplate := jwt.MapClaims{
|
|
"sub": sub[:255],
|
|
"iss": "https://auth.hectabit.org",
|
|
"name": username,
|
|
"aud": appId,
|
|
"exp": time.Now().Unix() + 2592000,
|
|
"iat": time.Now().Unix(),
|
|
"auth_time": time.Now().Unix(),
|
|
"session": secretKey,
|
|
"nonce": nonce,
|
|
}
|
|
|
|
dataTemplateTwo := jwt.MapClaims{
|
|
"exp": time.Now().Unix() + 2592000,
|
|
"iat": time.Now().Unix(),
|
|
"session": secretKey,
|
|
"nonce": genSalt(512),
|
|
}
|
|
|
|
tokenTemp := jwt.NewWithClaims(jwt.SigningMethodRS256, dataTemplate)
|
|
tokenTemp.Header["kid"] = "burgerauth"
|
|
jwt_token, err := tokenTemp.SignedString(privateKey)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/auth jwt_token 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 info. Your error code is: UNKOWN-API-AUTH-JWTCANNOTSIGN")
|
|
return
|
|
}
|
|
|
|
secretTemp := jwt.NewWithClaims(jwt.SigningMethodRS256, dataTemplateTwo)
|
|
secretTemp.Header["kid"] = "burgerauth"
|
|
secret_token, err := secretTemp.SignedString(privateKey)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/auth secret_token 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 info. Your error code is: UNKOWN-API-AUTH-JWTCANNOTSIGN.")
|
|
return
|
|
}
|
|
|
|
randomBytes, err := genSalt(512)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/auth randomBytes 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 info. Your error code is: UNKNOWN-API-AUTH-RANDOMBYTES.")
|
|
return
|
|
}
|
|
|
|
_, err = conn.Exec("INSERT INTO logins (appId, secret, nextsecret, code, nextcode, creator, openid, nextopenid, pkce, pkcemethod) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", appId, randomBytes, "none", secret_token, "none", userid, jwt_token, "none", code, codeMethod)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/auth insert 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 info. Your error code is: UNKNOWN-API-AUTH-INSERT.")
|
|
return
|
|
}
|
|
|
|
if randomBytes != "" {
|
|
c.Redirect(302, redirect_uri+"?code="+randomBytes+"&state="+state)
|
|
} else {
|
|
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 info. Your error code is: UNKNOWN-API-AUTH-REDIRECT.")
|
|
log.Println("[ERROR] Secret key not found at", strconv.FormatInt(time.Now().Unix(), 10))
|
|
}
|
|
})
|
|
|
|
router.POST("/api/tokenauth", func(c *gin.Context) {
|
|
err := c.Request.ParseForm()
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid form data"})
|
|
return
|
|
}
|
|
data := c.Request.Form
|
|
|
|
appId := data.Get("client_id")
|
|
code := data.Get("code")
|
|
code_verify := data.Get("code_verifier")
|
|
secret := data.Get("client_secret")
|
|
|
|
var verifyCode bool
|
|
if code_verify == "" {
|
|
verifyCode = false
|
|
} else {
|
|
verifyCode = true
|
|
}
|
|
|
|
var appIdCheck, secretCheck, openid, loginCode, PKCECode, PKCEMethod string
|
|
|
|
err = conn.QueryRow("SELECT o.appId, o.secret, l.openid, l.code, l.PKCECode, l.PKCEMethod FROM oauth AS o JOIN logins AS l ON o.appId = l.appId WHERE o.appId = ? AND l.secret = ? LIMIT 1;", appId, code).Scan(&appIdCheck, &secretCheck, &openid, &loginCode, &PKCECode, &PKCEMethod)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
c.JSON(401, gin.H{"error": "OAuth screening failed"})
|
|
} else {
|
|
log.Println("[ERROR] Unknown in /api/tokenauth at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-TOKENAUTH-SELECT"})
|
|
}
|
|
return
|
|
}
|
|
if appIdCheck != appId {
|
|
c.JSON(401, gin.H{"error": "OAuth screening failed"})
|
|
return
|
|
}
|
|
|
|
if verifyCode {
|
|
if PKCECode == "none" {
|
|
c.JSON(400, gin.H{"error": "Attempted PKCECode exchange with non-PKCECode authentication"})
|
|
return
|
|
} else {
|
|
if PKCEMethod == "S256" {
|
|
if sha256Base64(code_verify) != PKCECode {
|
|
c.JSON(403, gin.H{"error": "Invalid PKCECode code"})
|
|
return
|
|
}
|
|
} else if PKCEMethod == "plain" {
|
|
if code_verify != PKCECode {
|
|
c.JSON(403, gin.H{"error": "Invalid PKCECode code"})
|
|
return
|
|
}
|
|
} else {
|
|
c.JSON(403, gin.H{"error": "Attempted PKCECode exchange without supported PKCECode token method"})
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
if secret != secretCheck {
|
|
c.JSON(401, gin.H{"error": "Invalid secret"})
|
|
return
|
|
}
|
|
}
|
|
|
|
_, err = conn.Exec("DELETE FROM logins WHERE code = ?", loginCode)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/tokenauth delete at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-TOKENAUTH-DELETE"})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, gin.H{"access_token": loginCode, "token_type": "bearer", "expires_in": 2592000, "id_token": openid})
|
|
})
|
|
|
|
router.POST("/api/deleteauth", func(c *gin.Context) {
|
|
var data map[string]interface{}
|
|
err := c.ShouldBindJSON(&data)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
return
|
|
}
|
|
|
|
secretKey := data["secretKey"].(string)
|
|
appId := data["appId"].(string)
|
|
|
|
_, id, err := getSession(secretKey)
|
|
if err != nil {
|
|
c.JSON(401, gin.H{"error": "Invalid session"})
|
|
return
|
|
}
|
|
|
|
_, err = conn.Exec("DELETE FROM oauth WHERE appId = ? AND creator = ?", appId, id)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
c.JSON(400, gin.H{"error": "AppID Not found"})
|
|
} else {
|
|
log.Println("[ERROR] Unknown in /api/deleteauth at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-DELETEAUTH-DELETE"})
|
|
}
|
|
} else {
|
|
c.JSON(200, gin.H{"success": "true"})
|
|
}
|
|
})
|
|
|
|
router.POST("/api/newauth", func(c *gin.Context) {
|
|
var data map[string]interface{}
|
|
err := c.ShouldBindJSON(&data)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
return
|
|
}
|
|
|
|
secretKey := data["secretKey"].(string)
|
|
name := data["name"].(string)
|
|
redirectUri := data["redirectUri"].(string)
|
|
|
|
_, id, err := getSession(secretKey)
|
|
if err != nil {
|
|
c.JSON(401, gin.H{"error": "Invalid session"})
|
|
return
|
|
}
|
|
|
|
var testsecret, testappid string
|
|
secret, err := genSalt(512)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/newauth secretgen at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-NEWAUTH-SECRETGEN"})
|
|
return
|
|
}
|
|
for {
|
|
err := conn.QueryRow("SELECT secret FROM oauth WHERE secret = ?", secret).Scan(&testsecret)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
break
|
|
} else {
|
|
log.Println("[ERROR] Unknown in /api/newauth secretselect at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-NEWAUTH-SECRETSELECT"})
|
|
return
|
|
}
|
|
} else {
|
|
secret, err = genSalt(512)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/newauth secretgen at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-NEWAUTH-SECRETGEN"})
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
appId, err := genSalt(32)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/newauth appidgen at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-NEWAUTH-APPIDGEN"})
|
|
return
|
|
}
|
|
|
|
for {
|
|
err = conn.QueryRow("SELECT appId FROM oauth WHERE appId = ?", appId).Scan(&testappid)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
log.Println("[Info] New Oauth source added with ID:", appId)
|
|
break
|
|
} else {
|
|
log.Println("[ERROR] Unknown in /api/newauth appidcheck at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-NEWAUTH-APPIDCHECK"})
|
|
return
|
|
}
|
|
} else {
|
|
appId, err = genSalt(32)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/newauth appidgen at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-NEWAUTH-LAPPIDGEN"})
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
_, err = conn.Exec("INSERT INTO oauth (name, appId, creator, secret, redirectUri) VALUES (?, ?, ?, ?, ?)", name, appId, id, secret, redirectUri)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/newauth insert at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-NEWAUTH-INSERT"})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, gin.H{"key": secret, "appId": appId})
|
|
})
|
|
|
|
router.POST("/api/listauth", func(c *gin.Context) {
|
|
var data map[string]interface{}
|
|
err := c.ShouldBindJSON(&data)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
return
|
|
}
|
|
|
|
secretKey := data["secretKey"].(string)
|
|
|
|
_, id, err := getSession(secretKey)
|
|
if err != nil {
|
|
c.JSON(401, gin.H{"error": "Invalid session"})
|
|
return
|
|
}
|
|
|
|
rows, err := conn.Query("SELECT appId, name, rdiruri FROM oauth WHERE creator = ? ORDER BY creator DESC", id)
|
|
if err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LISTAUTH-QUERY"})
|
|
return
|
|
}
|
|
defer func(rows *sql.Rows) {
|
|
err := rows.Close()
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/listauth rows close at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LISTAUTH-ROWSCLOSE"})
|
|
return
|
|
}
|
|
}(rows)
|
|
|
|
var dataTemplate []map[string]interface{}
|
|
for rows.Next() {
|
|
var appId, name, redirectUri string
|
|
if err := rows.Scan(&appId, &name, &redirectUri); err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LISTAUTH-SCAN"})
|
|
return
|
|
}
|
|
template := map[string]interface{}{"appId": appId, "name": name, "redirectUri": redirectUri}
|
|
dataTemplate = append(dataTemplate, template)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LISTAUTH-ROWSERR"})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, dataTemplate)
|
|
})
|
|
|
|
router.POST("/api/deleteaccount", func(c *gin.Context) {
|
|
var data map[string]interface{}
|
|
err := c.ShouldBindJSON(&data)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
return
|
|
}
|
|
|
|
secretKey := data["secretKey"].(string)
|
|
|
|
_, id, err := getSession(secretKey)
|
|
if err != nil {
|
|
c.JSON(401, gin.H{"error": "Session does not exist"})
|
|
return
|
|
}
|
|
|
|
_, err = conn.Exec("DELETE FROM userdata WHERE creator = ?", id)
|
|
if err != nil {
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
log.Println("[ERROR] Unknown in /api/deleteaccount userdata at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-DELETEACCT-USERDATA"})
|
|
return
|
|
}
|
|
}
|
|
|
|
_, err = conn.Exec("DELETE FROM logins WHERE creator = ?", id)
|
|
if err != nil {
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
log.Println("[ERROR] Unknown in /api/deleteaccount logins at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-DELETEACCT-LOGINS"})
|
|
return
|
|
}
|
|
}
|
|
|
|
_, err = conn.Exec("DELETE FROM oauth WHERE creator = ?", id)
|
|
if err != nil {
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
log.Println("[ERROR] Unknown in /api/deleteuser oauth at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-DELETEUSER-OAUTH"})
|
|
return
|
|
}
|
|
}
|
|
|
|
_, err = conn.Exec("DELETE FROM users WHERE id = ?", id)
|
|
if err != nil {
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
log.Println("[ERROR] Unknown in /api/deleteuser logins at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-DELETEUSER-USERS"})
|
|
return
|
|
}
|
|
}
|
|
|
|
c.JSON(200, gin.H{"success": "true"})
|
|
})
|
|
|
|
router.POST("/api/sessions/list", func(c *gin.Context) {
|
|
var data map[string]interface{}
|
|
err := c.ShouldBindJSON(&data)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
return
|
|
}
|
|
|
|
secretKey := data["secretKey"].(string)
|
|
|
|
_, id, err := getSession(secretKey)
|
|
if err != nil {
|
|
c.JSON(401, gin.H{"error": "Session does not exist"})
|
|
return
|
|
}
|
|
|
|
rows, err := conn.Query("SELECT sessionid, session, device FROM sessions WHERE id = ? ORDER BY id DESC", id)
|
|
if err != nil {
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
log.Println("[ERROR] Unknown in /api/sessions/list at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST"})
|
|
return
|
|
}
|
|
}
|
|
defer func(rows *sql.Rows) {
|
|
err := rows.Close()
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/sessions/list rows close at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ROWSCLOSE"})
|
|
return
|
|
}
|
|
}(rows)
|
|
|
|
var dataTemplate []map[string]interface{}
|
|
for rows.Next() {
|
|
var id, sessionId, device string
|
|
thisSession := false
|
|
if err := rows.Scan(&id, &sessionId, &device); err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-SCAN"})
|
|
return
|
|
}
|
|
if sessionId == secretKey {
|
|
thisSession = true
|
|
}
|
|
template := map[string]interface{}{"id": sessionId, "thisSession": thisSession, "device": device}
|
|
dataTemplate = append(dataTemplate, template)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ERR"})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, dataTemplate)
|
|
})
|
|
|
|
router.POST("/api/sessions/remove", func(c *gin.Context) {
|
|
var data map[string]interface{}
|
|
err := c.ShouldBindJSON(&data)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
return
|
|
}
|
|
|
|
secretKey := data["secretKey"].(string)
|
|
sessionId := data["sessionId"].(string)
|
|
|
|
_, id, err := getSession(secretKey)
|
|
if err != nil {
|
|
c.JSON(401, gin.H{"error": "Session does not exist"})
|
|
return
|
|
}
|
|
|
|
_, err = conn.Exec("DELETE FROM sessions WHERE sessionid = ? AND id = ?", sessionId, id)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
c.JSON(422, gin.H{"error": "SessionID Not found"})
|
|
} else {
|
|
log.Println("[ERROR] Unknown in /api/sessions/remove at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-SESSIONS-REMOVE"})
|
|
}
|
|
} else {
|
|
c.JSON(200, gin.H{"success": "true"})
|
|
}
|
|
})
|
|
|
|
router.POST("/api/listusers", func(c *gin.Context) {
|
|
var data map[string]interface{}
|
|
err := c.ShouldBindJSON(&data)
|
|
if err != nil {
|
|
c.JSON(400, gin.H{"error": "Invalid JSON"})
|
|
return
|
|
}
|
|
|
|
masterKey := data["masterKey"].(string)
|
|
|
|
if masterKey == SECRET_KEY {
|
|
rows, err := conn.Query("SELECT * FROM users ORDER BY id DESC")
|
|
if err != nil {
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
log.Println("[ERROR] Unknown in /api/listusers at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LISTUSERS-QUERY"})
|
|
return
|
|
}
|
|
}
|
|
defer func(rows *sql.Rows) {
|
|
err := rows.Close()
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /api/listusers rows close at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LISTUSERS-ROWSCLOSE"})
|
|
return
|
|
}
|
|
}(rows)
|
|
|
|
var datatemplate []map[string]interface{}
|
|
for rows.Next() {
|
|
var id, username string
|
|
if err := rows.Scan(&id, &username); err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LISTUSERS-SCAN"})
|
|
return
|
|
}
|
|
template := map[string]interface{}{"id": id, "username": username}
|
|
datatemplate = append(datatemplate, template)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-API-LISTUSERS-ERR"})
|
|
return
|
|
}
|
|
|
|
c.JSON(200, datatemplate)
|
|
}
|
|
})
|
|
|
|
router.GET("/.well-known/jwks.json", func(c *gin.Context) {
|
|
mod, err := BigIntToBase64URL(modulus)
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /well-known/jwks.json modulus at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-JWKS-MODULUS"})
|
|
return
|
|
}
|
|
|
|
exp, err := Int64ToBase64URL(int64(exponent))
|
|
if err != nil {
|
|
log.Println("[ERROR] Unknown in /well-known/jwks.json exponent at", strconv.FormatInt(time.Now().Unix(), 10)+":", err)
|
|
c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgerauth and refer to the docs for more info. Your error code is: UNKNOWN-JWKS-EXPONENT"})
|
|
return
|
|
}
|
|
keys := gin.H{
|
|
"keys": []gin.H{
|
|
{
|
|
"kty": "RSA",
|
|
"alg": "RS256",
|
|
"use": "sig",
|
|
"kid": "burgerauth",
|
|
"n": mod,
|
|
"e": exp,
|
|
},
|
|
},
|
|
}
|
|
|
|
c.JSON(200, keys)
|
|
})
|
|
|
|
log.Println("[INFO] Server started at", time.Now().Unix())
|
|
log.Println("[INFO] Welcome to Burgerauth! Today we are running on IP " + HOST + " on port " + strconv.Itoa(PORT) + ".")
|
|
err = router.Run(HOST + ":" + strconv.Itoa(PORT))
|
|
if err != nil {
|
|
log.Fatalln("[FATAL] Server failed to begin operations at", time.Now().Unix(), err)
|
|
}
|
|
}
|