package main

import (
	// Fulgens libraries
	library "git.ailur.dev/ailur/fg-library/v2"
	authLibrary "git.ailur.dev/ailur/fg-nucleus-library"
	"git.ailur.dev/ailur/pow"

	// Standard libraries
	"bytes"
	"errors"
	"strconv"
	"strings"
	"time"

	// Secondary libraries
	"crypto/ed25519"
	"crypto/rand"
	"crypto/sha256"
	"database/sql"
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"html/template"
	"io/fs"
	"net/http"

	// Extra libraries
	"golang.org/x/crypto/argon2"

	// External libraries
	"github.com/cespare/xxhash/v2"
	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
	_ "modernc.org/sqlite"
)

var ServiceInformation = library.Service{
	Name: "Authentication",
	Permissions: library.Permissions{
		Authenticate:              false, // This service *is* the authentication service
		Database:                  true,  // This service does require database access
		BlobStorage:               false, // This service does not require blob storage
		InterServiceCommunication: true,  // This service does require inter-service communication
		Resources:                 true,  // This service does require its HTTP templates and static files to be served
	},
	ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000004"),
}

var serviceIDBytes []byte

func logFunc(message string, messageType uint64, information library.ServiceInitializationInformation) {
	// Log the message to the logger service
	information.Outbox <- library.InterServiceMessage{
		ServiceID:    ServiceInformation.ServiceID,
		ForServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000002"), // Logger service
		MessageType:  messageType,
		SentAt:       time.Now(),
		Message:      message,
	}
}

func ensureTrailingSlash(url string) string {
	if !strings.HasSuffix(url, "/") {
		return url + "/"
	}
	return url
}

func randomChars(length int) (string, error) {
	var saltChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	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] = saltChars[int(randomBytes[i])%len(saltChars)]
	}
	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 renderTemplate(statusCode int, w http.ResponseWriter, data map[string]interface{}, templatePath string, information library.ServiceInitializationInformation) {
	var err error
	var requestedTemplate *template.Template
	// Output ls of the resource directory
	requestedTemplate, err = template.ParseFS(information.ResourceDir, "templates/"+templatePath)
	if err != nil {
		logFunc(err.Error(), 2, information)
		renderString(500, w, "Sorry, something went wrong on our end. Error code: 01. Please report to the administrator.", information)
	} else {
		w.WriteHeader(statusCode)
		if strings.HasSuffix(templatePath, ".html") {
			w.Header().Set("Content-Type", "text/html")
		} else if strings.HasSuffix(templatePath, ".json") {
			w.Header().Set("Content-Type", "application/json")
		} else {
			w.Header().Set("Content-Type", "text/plain")
		}
		err = requestedTemplate.Execute(w, data)
		if err != nil {
			logFunc(err.Error(), 2, information)
			renderString(500, w, "Sorry, something went wrong on our end. Error code: 02. Please report to the administrator.", information)
		}
	}
}

func renderString(statusCode int, w http.ResponseWriter, data string, information library.ServiceInitializationInformation) {
	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(statusCode)
	_, err := w.Write([]byte(data))
	if err != nil {
		logFunc(err.Error(), 2, information)
	}
}

func renderJSON(statusCode int, w http.ResponseWriter, data map[string]interface{}, information library.ServiceInitializationInformation) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(statusCode)
	err := json.NewEncoder(w).Encode(data)
	if err != nil {
		logFunc(err.Error(), 2, information)
	}
}

func verifyJwt(token string, publicKey ed25519.PublicKey, mem *sql.DB) ([]byte, jwt.MapClaims, bool) {
	parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
		return publicKey, nil
	})
	if err != nil {
		return nil, nil, false
	}

	if !parsedToken.Valid {
		return nil, nil, false
	}

	claims, ok := parsedToken.Claims.(jwt.MapClaims)
	if !ok {
		return nil, nil, false
	}

	// Check if the token expired
	date, err := claims.GetExpirationTime()
	if err != nil {
		return nil, claims, false
	}

	if date.Before(time.Now()) {
		return nil, claims, false
	}

	// Check if the session exists
	var userId []byte
	err = mem.QueryRow("SELECT id FROM sessions WHERE session = ?", claims["session"]).Scan(&userId)
	if err != nil {
		return nil, claims, false
	}

	return userId, claims, true
}

func Main(information library.ServiceInitializationInformation) {
	var conn library.Database
	var mem *sql.DB
	var publicKey ed25519.PublicKey
	var privateKey ed25519.PrivateKey
	// Load the configuration
	privacyPolicy := information.Configuration["privacyPolicy"].(string)
	hostName := information.Configuration["url"].(string)
	testAppIsAvailable := information.Configuration["testAppEnabled"].(bool)
	testAppIsInternalApp, ok := information.Configuration["testAppIsInternalApp"].(bool)
	if !ok {
		testAppIsAvailable = false
	}
	identifier := information.Configuration["identifier"].(string)
	adminKey := information.Configuration["adminKey"].(string)

	var err error
	serviceIDBytes, err = ServiceInformation.ServiceID.MarshalBinary()
	if err != nil {
		logFunc(err.Error(), 3, information)
	}

	// Initiate a connection to the database
	// Call service ID 1 to get the database connection information
	information.Outbox <- library.InterServiceMessage{
		ServiceID:    ServiceInformation.ServiceID,
		ForServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"), // Service initialization service
		MessageType:  1,                                                      // Request connection information
		SentAt:       time.Now(),
		Message:      nil,
	}

	// Wait for the response
	response := <-information.Inbox
	if response.MessageType == 2 {
		// This is the connection information
		// Set up the database connection
		conn = response.Message.(library.Database)
		if conn.DBType == library.Sqlite {
			// Create the global table
			// Uniqueness check is a hack to ensure we only have one global row
			_, err := conn.DB.Exec("CREATE TABLE IF NOT EXISTS global (key BLOB NOT NULL, uniquenessCheck BOOLEAN NOT NULL UNIQUE CHECK (uniquenessCheck = true) DEFAULT true)")
			if err != nil {
				logFunc(err.Error(), 3, information)
			}
			// Create the users table
			_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS users (id BLOB PRIMARY KEY NOT NULL UNIQUE, created INTEGER NOT NULL, username TEXT NOT NULL UNIQUE, publicKey BLOB NOT NULL)")
			if err != nil {
				logFunc(err.Error(), 3, information)
			}
			// Create the oauth table
			_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS oauth (appId TEXT NOT NULL UNIQUE, secret TEXT, creator BLOB NOT NULL, redirectUri TEXT NOT NULL, name TEXT NOT NULL, keyShareUri TEXT NOT NULL DEFAULT '', scopes TEXT NOT NULL DEFAULT '[\"openid\"]')")
			if err != nil {
				logFunc(err.Error(), 3, information)
			}
		} else {
			// Create the global table
			// Uniqueness check is a hack to ensure we only have one global row
			_, err := conn.DB.Exec("CREATE TABLE IF NOT EXISTS global (key BYTEA NOT NULL, uniquenessCheck BOOLEAN NOT NULL UNIQUE CHECK (uniquenessCheck = true) DEFAULT true)")
			if err != nil {
				logFunc(err.Error(), 3, information)
			}
			// Create the users table
			_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS users (id BYTEA PRIMARY KEY NOT NULL UNIQUE, created INTEGER NOT NULL, username TEXT NOT NULL UNIQUE, publicKey BYTEA NOT NULL)")
			if err != nil {
				logFunc(err.Error(), 3, information)
			}
			// Create the oauth table
			_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS oauth (appId TEXT NOT NULL UNIQUE, secret TEXT, creator BYTEA NOT NULL, redirectUri TEXT NOT NULL, name TEXT NOT NULL, keyShareUri TEXT NOT NULL DEFAULT '', scopes TEXT NOT NULL DEFAULT '[\"openid\"]')")
			if err != nil {
				logFunc(err.Error(), 3, information)
			}
		}
		// Set up the in-memory cache
		mem, err = sql.Open("sqlite", "file:"+ServiceInformation.ServiceID.String()+"?mode=memory&cache=shared")
		if err != nil {
			logFunc(err.Error(), 3, information)
		}
		// Drop the tables if they exist
		_, err = mem.Exec("DROP TABLE IF EXISTS sessions")
		if err != nil {
			logFunc(err.Error(), 3, information)
		}
		_, err = mem.Exec("DROP TABLE IF EXISTS logins")
		if err != nil {
			logFunc(err.Error(), 3, information)
		}
		_, err = mem.Exec("DROP TABLE IF EXISTS spent")
		if err != nil {
			logFunc(err.Error(), 3, information)
		}
		_, err = mem.Exec("DROP TABLE IF EXISTS challengeResponse")
		if err != nil {
			logFunc(err.Error(), 3, information)
		}
		// Create the sessions table
		_, err = mem.Exec("CREATE TABLE sessions (id BLOB NOT NULL, session TEXT NOT NULL, device TEXT NOT NULL DEFAULT '?')")
		if err != nil {
			logFunc(err.Error(), 3, information)
		}
		// Create the logins table
		_, err = mem.Exec("CREATE TABLE logins (appId TEXT NOT NULL, exchangeCode TEXT NOT NULL UNIQUE, pkce TEXT, pkceMethod TEXT, openid BOOLEAN NOT NULL, userId BLOB NOT NULL UNIQUE, nonce TEXT NOT NULL DEFAULT '', token TEXT NOT NULL)")
		if err != nil {
			logFunc(err.Error(), 3, information)
		}
		// Create the spent PoW table
		_, err = mem.Exec("CREATE TABLE spent (hash BLOB NOT NULL UNIQUE, expires INTEGER NOT NULL)")
		if err != nil {
			logFunc(err.Error(), 3, information)
		}
		// Create the challenge-response table
		_, err = mem.Exec("CREATE TABLE challengeResponse (challenge TEXT NOT NULL UNIQUE, userId BLOB NOT NULL, expires INTEGER NOT NULL)")
		if err != nil {
			logFunc(err.Error(), 3, information)
		}
	} else {
		// This is an error message
		// Log the error message to the logger service
		logFunc(response.Message.(error).Error(), 3, information)
	}

	// Set up the signing keys
	// Check if the global table has the keys
	err = conn.DB.QueryRow("SELECT key FROM global LIMIT 1").Scan(&privateKey)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			// Generate a new key
			var err error
			publicKey, privateKey, err = ed25519.GenerateKey(rand.Reader)
			if err != nil {
				logFunc(err.Error(), 3, information)
			}
			// Insert the key into the global table
			_, err = conn.DB.Exec("INSERT INTO global (key) VALUES ($1)", privateKey)
			if err != nil {
				logFunc(err.Error(), 3, information)
			}
		} else {
			logFunc(err.Error(), 3, information)
		}
	} else {
		publicKey = privateKey.Public().(ed25519.PublicKey)
	}

	// Set up the test app
	_, err = conn.DB.Exec("DELETE FROM oauth WHERE appId = 'TestApp-DoNotUse'")
	if err != nil {
		testAppIsAvailable = false
		logFunc(err.Error(), 2, information)
	}

	if testAppIsInternalApp {
		_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES ('TestApp-DoNotUse', 'none', $1, 'Test App', $2, '[\"openid\", \"clientKeyShare\"]', $3)", serviceIDBytes, ensureTrailingSlash(hostName)+"testApp", ensureTrailingSlash(hostName)+"keyExchangeTester")
	} else {
		testAppCreator, err := uuid.New().MarshalBinary()
		if err != nil {
			testAppIsAvailable = false
			logFunc(err.Error(), 2, information)
		}

		_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES ('TestApp-DoNotUse', 'none', $1, 'Test App', $2, '[\"openid\", \"clientKeyShare\"]', $3)", testAppCreator, ensureTrailingSlash(hostName)+"testApp", ensureTrailingSlash(hostName)+"keyExchangeTester")
	}
	if err != nil {
		testAppIsAvailable = false
		logFunc(err.Error(), 2, information)
	}

	// Set up the router
	router := information.Router

	// Add the CORS middleware
	disableCors := func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Access-Control-Allow-Origin", "*")
			w.Header().Set("Access-Control-Allow-Headers", "authorization, content-type")
			w.Header().Set("Access-Control-Allow-Methods", "*")
			next.ServeHTTP(w, r)
		})
	}

	router.Use(disableCors)

	disableCorsHandleFunc := func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "POST")
		w.Header().Set("Access-Control-Allow-Headers", "authorization, content-type")
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(200)
	}

	// Set up the static routes
	staticDir, err := fs.Sub(information.ResourceDir, "static")
	if err != nil {
		logFunc(err.Error(), 3, information)
	} else {
		router.Handle("/static/*", http.StripPrefix("/static/", http.FileServerFS(staticDir)))
	}

	router.Get("/login", func(w http.ResponseWriter, r *http.Request) {
		renderTemplate(200, w, map[string]interface{}{
			"identifier": identifier,
		}, "login.html", information)
	})

	router.Get("/signup", func(w http.ResponseWriter, r *http.Request) {
		renderTemplate(200, w, map[string]interface{}{
			"identifier": identifier,
		}, "signup.html", information)
	})

	router.Get("/logout", func(w http.ResponseWriter, r *http.Request) {
		renderTemplate(200, w, map[string]interface{}{
			"identifier": identifier,
		}, "logout.html", information)
	})

	router.Get("/privacy", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, privacyPolicy, 301)
	})

	router.Get("/testApp", func(w http.ResponseWriter, r *http.Request) {
		if testAppIsAvailable {
			renderTemplate(200, w, map[string]interface{}{
				"identifier": identifier,
			}, "testApp.html", information)
		} else {
			renderTemplate(200, w, map[string]interface{}{
				"identifier": identifier,
			}, "testAppNotAvailable.html", information)
		}
	})

	router.Get("/authorize", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Query().Get("client_id") != "" {
			if conn.DBType == library.Sqlite {
				var name string
				var creator []byte
				err := conn.DB.QueryRow("SELECT name, creator FROM oauth WHERE appId = $1", r.URL.Query().Get("client_id")).Scan(&name, &creator)
				if err != nil {
					if errors.Is(err, sql.ErrNoRows) {
						renderString(404, w, "App not found", information)
					} else {
						logFunc(err.Error(), 2, information)
						renderString(500, w, "Sorry, something went wrong on our end. Error code: 03. Please report to the administrator.", information)
					}
					return
				}

				if !bytes.Equal(creator, serviceIDBytes) {
					renderTemplate(200, w, map[string]interface{}{
						"identifier": identifier,
						"name":       name,
					}, "authorize.html", information)
				} else {
					renderTemplate(200, w, map[string]interface{}{
						"identifier": identifier,
						"name":       name,
					}, "autoAccept.html", information)
				}
			} else {
				var name string
				var creator uuid.UUID
				err := conn.DB.QueryRow("SELECT name, creator FROM oauth WHERE appId = $1", r.URL.Query().Get("client_id")).Scan(&name, &creator)
				if err != nil {
					if errors.Is(err, sql.ErrNoRows) {
						renderString(404, w, "App not found", information)
					} else {
						logFunc(err.Error(), 2, information)
						renderString(500, w, "Sorry, something went wrong on our end. Error code: 03. Please report to the administrator.", information)
					}
					return
				}

				if creator != ServiceInformation.ServiceID {
					renderTemplate(200, w, map[string]interface{}{
						"identifier": identifier,
						"name":       name,
					}, "authorize.html", information)
				} else {
					renderTemplate(200, w, map[string]interface{}{
						"identifier": identifier,
						"name":       name,
					}, "autoAccept.html", information)
				}
			}
		} else {
			http.Redirect(w, r, "/dashboard", 301)
		}
	})

	router.Get("/dashboard", func(w http.ResponseWriter, r *http.Request) {
		renderTemplate(200, w, map[string]interface{}{
			"identifier": identifier,
		}, "dashboard.html", information)
	})

	router.Get("/clientKeyShare", func(w http.ResponseWriter, r *http.Request) {
		// Parse the JWT from the query string
		if r.URL.Query().Get("accessToken") == "" {
			renderString(400, w, "No token provided", information)
			return
		}

		// Verify the JWT
		_, claims, ok := verifyJwt(r.URL.Query().Get("accessToken"), publicKey, mem)
		if !ok {
			renderString(401, w, "Invalid token", information)
			return
		}

		// Check if they have the clientKeyShare scope
		var scopes string
		err = conn.DB.QueryRow("SELECT scopes FROM oauth WHERE appId = $1", claims["aud"]).Scan(&scopes)
		if err != nil {
			renderString(500, w, "Sorry, something went wrong on our end. Error code: 20. Please report to the administrator.", information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Unmarshal the scopes
		var scopesArray []string
		err = json.Unmarshal([]byte(scopes), &scopesArray)
		if err != nil {
			renderString(500, w, "Sorry, something went wrong on our end. Error code: 21. Please report to the administrator.", information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Check if the clientKeyShare scope is present
		var hasClientKeyShare bool
		for _, scope := range scopesArray {
			if scope == "clientKeyShare" {
				hasClientKeyShare = true
				break
			}
		}
		if !hasClientKeyShare {
			renderString(403, w, "Missing scope", information)
			return
		}

		// Check it's not an openid token
		if claims["isOpenID"] == true {
			renderString(400, w, "Invalid token", information)
		} else {
			renderTemplate(200, w, map[string]interface{}{
				"identifier": identifier,
			}, "clientKeyShare.html", information)
		}
	})

	router.Get("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
		renderTemplate(200, w, map[string]interface{}{
			"hostName": hostName,
		}, "openid.json", information)
	})

	router.Post("/api/changePassword", func(w http.ResponseWriter, r *http.Request) {
		type changePassword struct {
			Session     string `json:"session"`
			NewPassword string `json:"newPassword"`
		}
		var data changePassword
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the session exists
		var userId []byte
		err = mem.QueryRow("SELECT id FROM sessions WHERE session = ?", data.Session).Scan(&userId)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid session"}, information)
			return
		}

		// Generate a new salt
		// We want it to be binary data, not alphanumerical, so we don't use randomChars
		salt := make([]byte, 16)
		_, err = rand.Read(salt)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "04"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Decode the new password
		newPassword, err := base64.StdEncoding.DecodeString(data.NewPassword)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Hash the password
		hashedPassword := argon2.IDKey(newPassword, salt, 64, 4096, 1, 32)

		// Update the password
		_, err = conn.DB.Exec("UPDATE users SET password = $1, salt = $2 WHERE id = $3", hashedPassword, salt, userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "05"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Invalidate all sessions
		_, err = mem.Exec("DELETE FROM sessions WHERE id = ?", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "06"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Return success
		renderJSON(200, w, map[string]interface{}{"success": true}, information)
	})

	router.Post("/api/signup", func(w http.ResponseWriter, r *http.Request) {
		type signup struct {
			Username    string `json:"username"`
			PublicKey   string `json:"publicKey"`
			ProofOfWork string `json:"proofOfWork"`
		}
		var data signup
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the difficulty, timestamp and resource are correct
		powSlice := strings.Split(data.ProofOfWork, ":")
		if powSlice[0] != "2" || powSlice[3] != "fg-auth-signup" {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid PoW"}, information)
			return
		}

		timestamp, err := strconv.ParseInt(powSlice[1], 10, 64)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid PoW"}, information)
			return
		}

		if time.Unix(timestamp, 0).Add(time.Minute*10).Compare(time.Now()) < 0 {
			renderJSON(400, w, map[string]interface{}{"error": "PoW expired"}, information)
			return
		}

		// Verify the PoW
		if !ailur_pow.VerifyPoW(data.ProofOfWork) {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid PoW"}, information)
			return
		}

		// Check if the PoW is spent
		hash := make([]byte, 8)
		binary.LittleEndian.PutUint64(hash, xxhash.Sum64String(data.ProofOfWork))
		_, err = mem.Exec("INSERT INTO spent (hash, expires) VALUES (?, ?)", hash, time.Now().Unix()+60)
		if err != nil {
			if strings.Contains(err.Error(), "UNIQUE constraint failed") {
				renderJSON(400, w, map[string]interface{}{"error": "Proof of work already spent"}, information)
				return
			} else {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "07"}, information)
				logFunc(err.Error(), 2, information)
				return
			}
		}

		// Decode the public key
		publicKey, err := base64.StdEncoding.DecodeString(data.PublicKey)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Try to insert the user
		userID, err := uuid.New().MarshalBinary()
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "08"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		_, err = conn.DB.Exec("INSERT INTO users (id, created, username, publicKey) VALUES ($1, $2, $3, $4)", userID, time.Now().Unix(), data.Username, publicKey)
		if err != nil {
			if strings.Contains(err.Error(), "UNIQUE constraint failed") {
				renderJSON(409, w, map[string]interface{}{"error": "Username already taken"}, information)
			} else {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "09"}, information)
				logFunc(err.Error(), 2, information)
			}
			return
		}

		// Create a new session
		// We want the session token to be somewhat legible, so we use randomChars
		// As a trade-off for this, we use a longer session token
		session, err := randomChars(512)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "10"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Insert the session
		_, err = mem.Exec("INSERT INTO sessions (id, session, device) VALUES (?, ?, ?)", userID, session, r.Header.Get("User-Agent"))

		// Return success, as well as the session token
		renderJSON(200, w, map[string]interface{}{"key": session}, information)
	})

	router.Post("/api/loginChallenge", func(w http.ResponseWriter, r *http.Request) {
		type login struct {
			Username string `json:"username"`
		}

		var data login
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Get the id for the user
		var userId []byte
		err = conn.DB.QueryRow("SELECT id FROM users WHERE username = $1", data.Username).Scan(&userId)
		if err != nil {
			if errors.Is(err, sql.ErrNoRows) {
				renderJSON(401, w, map[string]interface{}{"error": "Invalid username"}, information)
			} else {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "12"}, information)
				logFunc(err.Error(), 2, information)
			}
			return
		}

		// Generate a new challenge
		challenge, err := randomChars(512)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "53"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Insert the challenge with one minute expiration
		_, err = mem.Exec("INSERT INTO challengeResponse (challenge, userId, expires) VALUES (?, ?, ?)", challenge, userId, time.Now().Unix()+60)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "53"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Return the challenge
		renderJSON(200, w, map[string]interface{}{"challenge": challenge}, information)
	})

	router.Post("/api/login", func(w http.ResponseWriter, r *http.Request) {
		type login struct {
			Username  string `json:"username"`
			Signature string `json:"signature"`
		}

		var data login
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Try to select the user
		var userId []byte
		var publicKey []byte
		err = conn.DB.QueryRow("SELECT id, publicKey FROM users WHERE username = $1", data.Username).Scan(&userId, &publicKey)
		if err != nil {
			if errors.Is(err, sql.ErrNoRows) {
				renderJSON(401, w, map[string]interface{}{"error": "Invalid username"}, information)
			} else {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "12"}, information)
				logFunc(err.Error(), 2, information)
			}
			return
		}

		// Decode the challenge
		signature, err := base64.StdEncoding.DecodeString(data.Signature)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Verify the challenge
		// Select the current challenge from the database
		var challenge string
		err = mem.QueryRow("SELECT challenge FROM challengeResponse WHERE userId = ?", userId).Scan(&challenge)
		if err != nil {
			if errors.Is(err, sql.ErrNoRows) {
				renderJSON(401, w, map[string]interface{}{"error": "Invalid challenge"}, information)
			} else {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "52"}, information)
				logFunc(err.Error(), 2, information)
			}
			return
		}

		// Check if the challenge is correct by verifying the signature
		if !ed25519.Verify(publicKey, []byte(challenge), signature) {
			renderJSON(401, w, map[string]interface{}{"error": "Invalid signature"}, information)
			return
		}

		// Delete the challenge
		_, err = mem.Exec("DELETE FROM challengeResponse WHERE userId = ?", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "53"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Create a new session
		// We want the session token to be somewhat legible, so we use randomChars
		// As a trade-off for this, we use a longer session token
		session, err := randomChars(512)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "13"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Insert the session
		_, err = mem.Exec("INSERT INTO sessions (id, session, device) VALUES (?, ?, ?)", userId, session, r.Header.Get("User-Agent"))
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "14"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Return success, as well as the session token
		renderJSON(200, w, map[string]interface{}{"key": session}, information)
	})

	router.Post("/api/userinfo", func(w http.ResponseWriter, r *http.Request) {
		type userinfo struct {
			Token string `json:"token"`
		}

		var data userinfo
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the session exists
		var userId []byte
		err = mem.QueryRow("SELECT id FROM sessions WHERE session = ?", data.Token).Scan(&userId)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid session"}, information)
			return
		}

		// Get the username and the creation date
		var username string
		var created int64
		err = conn.DB.QueryRow("SELECT username, created FROM users WHERE id = $1", userId).Scan(&username, &created)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "15"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Return the username and the creation date
		renderJSON(200, w, map[string]interface{}{"username": username, "created": created}, information)
	})

	// This exists to make sure you get logged out immediately with an invalidated session
	router.Post("/api/loggedIn", func(w http.ResponseWriter, r *http.Request) {
		type loggedIn struct {
			Token string `json:"token"`
		}

		var data loggedIn
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the session exists
		var session string
		err = mem.QueryRow("SELECT session FROM sessions WHERE session = ?", data.Token).Scan(&session)
		if err != nil {
			if errors.Is(err, sql.ErrNoRows) {
				renderJSON(401, w, map[string]interface{}{"error": "Invalid session"}, information)
			} else {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "16"}, information)
				logFunc(err.Error(), 2, information)
			}
		} else {
			if session == data.Token {
				// Return success
				renderJSON(200, w, map[string]interface{}{"success": true}, information)
			} else {
				// I don't know why this happens, but it does
				renderJSON(401, w, map[string]interface{}{"error": "Invalid session"}, information)
			}
		}
	})

	// Via the magic of JWTs, this support **both** access tokens and OpenID tokens. I love signing things.
	// Just a shame in 15 years when Y2Q happens. That'll be a pain.
	router.Get("/api/oauth/userinfo", func(w http.ResponseWriter, r *http.Request) {
		// Parse the JWT
		if r.Header.Get("Authorization") == "" {
			renderJSON(401, w, map[string]interface{}{"error": "No token provided"}, information)
			return
		}

		// Verify the JWT
		userId, claims, ok := verifyJwt(strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer "), publicKey, mem)
		if !ok {
			renderJSON(401, w, map[string]interface{}{"error": "Invalid token"}, information)
			return
		}

		// Check if they have the openid scope
		var scopes string
		err = conn.DB.QueryRow("SELECT scopes FROM oauth WHERE appId = $1", claims["aud"]).Scan(&scopes)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "17"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Unmarshal the scopes
		var scopesArray []string
		err = json.Unmarshal([]byte(scopes), &scopesArray)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "18"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Check if the openid scope is present
		var hasOpenid bool
		for _, scope := range scopesArray {
			if scope == "openid" {
				hasOpenid = true
				break
			}
		}
		if !hasOpenid {
			renderJSON(403, w, map[string]interface{}{"error": "Missing scope"}, information)
			return
		}

		// Get the username
		var username string
		err := conn.DB.QueryRow("SELECT username FROM users WHERE id = $1", userId).Scan(&username)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "19"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Return the username and id as sub
		renderJSON(200, w, map[string]interface{}{"username": username, "sub": uuid.Must(uuid.FromBytes(userId)).String()}, information)
	})

	router.Options("/api/oauth/userinfo", disableCorsHandleFunc)

	router.Post("/api/authorize", func(w http.ResponseWriter, r *http.Request) {
		type authorize struct {
			AppId       string `json:"appId"`
			PKCECode    string `json:"PKCECode"`
			PKCEMethod  string `json:"PKCEMethod"`
			RedirectUri string `json:"redirectUri"`
			Nonce       string `json:"nonce"`
			Deny        bool   `json:"deny"`
			Token       string `json:"token"`
		}

		var data authorize
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Verify the AppID, redirectUri and scopes
		var appId, redirectUri, scopes string
		err = conn.DB.QueryRow("SELECT appId, redirectUri, scopes FROM oauth WHERE appId = $1", data.AppId).Scan(&appId, &redirectUri, &scopes)
		if err != nil {
			renderJSON(404, w, map[string]interface{}{"error": "App not found"}, information)
			return
		}

		if ensureTrailingSlash(redirectUri) != ensureTrailingSlash(data.RedirectUri) || appId != data.AppId {
			renderJSON(401, w, map[string]interface{}{"error": "OAuth screening failed"}, information)
			return
		}

		// If denied, return success
		if data.Deny {
			renderJSON(200, w, map[string]interface{}{"success": true}, information)
			return
		}

		// Unmarshal the scopes
		var scopesArray []string
		err = json.Unmarshal([]byte(scopes), &scopesArray)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "22"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Check if the OpenID scope is present
		var hasOpenid bool
		for _, scope := range scopesArray {
			if scope == "openid" {
				hasOpenid = true
				break
			}
		}

		// Check for the session
		var userId []byte
		err = mem.QueryRow("SELECT id FROM sessions WHERE session = ?", data.Token).Scan(&userId)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid session"}, information)
			return
		}

		// Delete all ongoing logins for this user
		_, err = mem.Exec("DELETE FROM logins WHERE userId = ?", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "23"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Check for nonce
		if data.Nonce == "" {
			// The nonce must actually be in a URL, so we can't have pure-binary data
			data.Nonce, err = randomChars(512)
			if err != nil {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "24"}, information)
				logFunc(err.Error(), 2, information)
				return
			}
		}

		// Create the exchange code
		exchangeCode, err := randomChars(512)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "25"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Insert the login
		if data.Nonce != "" {
			_, err = mem.Exec("INSERT INTO logins (appId, exchangeCode, pkce, pkceMethod, openid, userId, token, nonce) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", data.AppId, exchangeCode, data.PKCECode, data.PKCEMethod, hasOpenid, userId, data.Token, data.Nonce)
		} else {
			_, err = mem.Exec("INSERT INTO logins (appId, exchangeCode, pkce, pkceMethod, openid, userId, token) VALUES (?, ?, ?, ?, ?, ?, ?)", data.AppId, exchangeCode, data.PKCECode, data.PKCEMethod, hasOpenid, userId, data.Token)
		}
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "26"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Return success
		renderJSON(200, w, map[string]interface{}{"exchangeCode": exchangeCode}, information)
	})

	router.Post("/api/oauth/token", func(w http.ResponseWriter, r *http.Request) {
		// Parse the form data
		err := r.ParseForm()
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid form data"}, information)
			return
		}

		// Declare the variables
		var appId, nonce, code string
		var userId []byte
		var openid bool

		// Check if the PKCE is present
		if r.Form.Get("code_verifier") != "" {
			// It is - get the PKCE code and method
			var pkceCode, pkceMethod string
			err = mem.QueryRow("SELECT pkce, pkceMethod, appId, userId, nonce, openid, token FROM logins WHERE exchangeCode = ?", r.Form.Get("code")).Scan(&pkceCode, &pkceMethod, &appId, &userId, &nonce, &openid, &code)
			if err != nil {
				renderJSON(404, w, map[string]interface{}{"error": "Code not found"}, information)
				return
			}

			// Check the appId matches
			if appId != r.Form.Get("client_id") {
				renderJSON(401, w, map[string]interface{}{"error": "OAuth screening failed"}, information)
				return
			}

			// Check the PKCE
			if pkceMethod == "S256" {
				if sha256Base64(r.Form.Get("code_verifier")) != pkceCode {
					renderJSON(403, w, map[string]interface{}{"error": "Invalid PKCE code"}, information)
					return
				}
			} else if pkceMethod == "plain" {
				if r.Form.Get("code_verifier") != pkceCode {
					renderJSON(403, w, map[string]interface{}{"error": "Invalid PKCE code"}, information)
					return
				}
			} else {
				renderJSON(400, w, map[string]interface{}{"error": "Invalid PKCE method"}, information)
				return
			}
		} else {
			// It isn't - don't get the PKCE code and method
			err = mem.QueryRow("SELECT appId, userId, nonce, openid, token FROM logins WHERE exchangeCode = ?", r.Form.Get("code")).Scan(&appId, &userId, &nonce, &openid, &code)
			if err != nil {
				renderJSON(404, w, map[string]interface{}{"error": "Code not found"}, information)
				return
			}

			// Check the appId matches
			if appId != r.Form.Get("client_id") {
				renderJSON(401, w, map[string]interface{}{"error": "OAuth screening failed"}, information)
				return
			}

			// Verify the secret
			var secret string
			err = conn.DB.QueryRow("SELECT \"secret\" FROM oauth WHERE appId = $1", r.Form.Get("client_id")).Scan(&secret)
			if err != nil {
				renderJSON(404, w, map[string]interface{}{"error": "App not found"}, information)
				return
			}

			if secret != r.Form.Get("client_secret") {
				renderJSON(401, w, map[string]interface{}{"error": "Invalid client secret"}, information)
				return
			}
		}

		// Delete the login
		_, err = mem.Exec("DELETE FROM logins WHERE userId = ?", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "27"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Generate a new nonce if it doesn't exist
		if nonce == "" {
			nonce, err = randomChars(512)
			if err != nil {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "28"}, information)
				logFunc(err.Error(), 2, information)
				return
			}
		}

		// Create the JWTs
		var openIDTokenString string
		if openid {
			var username string
			err := conn.DB.QueryRow("SELECT username FROM users WHERE id = $1", userId).Scan(&username)
			if err != nil {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "29"}, information)
				logFunc(err.Error(), 2, information)
				return
			}

			openIDTemplate := jwt.MapClaims{
				"sub":       uuid.Must(uuid.FromBytes(userId)).String(),
				"iss":       hostName,
				"name":      username,
				"aud":       appId,
				"exp":       time.Now().Add(time.Hour * 24 * 7).Unix(),
				"iat":       time.Now().Unix(),
				"auth_time": time.Now().Unix(),
				"session":   code,
				"nonce":     nonce,
				"isOpenID":  true,
			}

			openIDToken := jwt.NewWithClaims(jwt.SigningMethodEdDSA, openIDTemplate)
			openIDToken.Header["kid"] = base64.StdEncoding.EncodeToString(publicKey[:8])
			openIDTokenString, err = openIDToken.SignedString(privateKey)
			if err != nil {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "30"}, information)
				logFunc(err.Error(), 2, information)
				return
			}
		}

		accessTokenTemplate := jwt.MapClaims{
			"exp":      time.Now().Add(time.Hour * 24 * 7).Unix(),
			"iat":      time.Now().Unix(),
			"session":  code,
			"nonce":    nonce,
			"aud":      appId,
			"isOpenID": false,
			"sub":      uuid.Must(uuid.FromBytes(userId)).String(),
		}

		accessToken := jwt.NewWithClaims(jwt.SigningMethodEdDSA, accessTokenTemplate)
		accessToken.Header["kid"] = base64.StdEncoding.EncodeToString(publicKey[:8])
		accessTokenString, err := accessToken.SignedString(privateKey)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "31"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Delete all ongoing logins for this user
		_, err = mem.Exec("DELETE FROM logins WHERE userId = ?", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "32"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Return the tokens
		if openid {
			renderJSON(200, w, map[string]interface{}{"access_token": accessTokenString, "id_token": openIDTokenString, "token_type": "Bearer", "expires_in": 604800}, information)
		} else {
			renderJSON(200, w, map[string]interface{}{"access_token": accessTokenString, "token_type": "Bearer", "expires_in": 604800}, information)
		}
	})

	router.Options("/api/oauth/token", disableCorsHandleFunc)

	router.Post("/api/oauth/remove", func(w http.ResponseWriter, r *http.Request) {
		type remove struct {
			Token string `json:"token"`
			AppID string `json:"appId"`
		}

		var data remove
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the session exists
		var userId []byte
		err = mem.QueryRow("SELECT id FROM sessions WHERE session = ?", data.Token).Scan(&userId)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid session"}, information)
			return
		}

		// Delete the oauth entry
		_, err = conn.DB.Exec("DELETE FROM oauth WHERE appId = $1 AND creator = $2", data.AppID, userId)
		if err != nil {
			if errors.Is(err, sql.ErrNoRows) {
				renderJSON(404, w, map[string]interface{}{"error": "App not found"}, information)
			} else {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "33"}, information)
				logFunc(err.Error(), 2, information)
			}
		} else {
			renderJSON(200, w, map[string]interface{}{"success": true}, information)
		}
	})

	router.Post("/api/oauth/add", func(w http.ResponseWriter, r *http.Request) {

		// Conveniently, we use this one for ISB as well, so we can re-use the struct
		var data authLibrary.OAuthInformation
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the session exists
		var userId []byte
		err = mem.QueryRow("SELECT id FROM sessions WHERE session = ?", data.Token).Scan(&userId)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid session"}, information)
			return
		}

		// Generate a new secret
		// It must be able to be sent via JSON, so we can't have pure-binary data
		secret, err := randomChars(512)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "34"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Generate a new appId
		// It must be able to be sent via JSON, so we can't have pure-binary data
		appId, err := randomChars(32)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "35"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Validate the scopes
		var clientKeyShare bool
		for _, scope := range data.Scopes {
			if scope != "openid" && scope != "clientKeyShare" {
				renderJSON(400, w, map[string]interface{}{"error": "Invalid scope"}, information)
				return
			} else {
				if scope == "clientKeyShare" {
					clientKeyShare = true
				} else if scope != "openid" {
					logFunc("An impossible logic error has occurred, please move away from radiation or use ECC RAM", 1, information)
					renderJSON(400, w, map[string]interface{}{"error": "Invalid scope"}, information)
					return
				}
			}
		}

		// Marshal the scopes
		scopes, err := json.Marshal(data.Scopes)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "36"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Insert the oauth entry
		if clientKeyShare {
			_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES ($1, $2, $3, $4, $5, $6, $7)", appId, secret, userId, data.Name, data.RedirectUri, scopes, data.KeyShareUri)
		} else {
			_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes) VALUES ($1, $2, $3, $4, $5, $6)", appId, secret, userId, data.Name, data.RedirectUri, scopes)
		}
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "37"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Return the appId and secret
		renderJSON(200, w, map[string]interface{}{"appId": appId, "key": secret}, information)
	})

	router.Post("/api/oauth/list", func(w http.ResponseWriter, r *http.Request) {
		type list struct {
			Token string `json:"token"`
		}

		var data list
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the session exists
		var userId []byte
		err = mem.QueryRow("SELECT id FROM sessions WHERE session = ?", data.Token).Scan(&userId)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid session"}, information)
			return
		}

		// Get the apps
		rows, err := conn.DB.Query("SELECT appId, name, redirectUri, scopes, keyShareUri FROM oauth WHERE creator = $1", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "38"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		var dataOut []map[string]interface{}
		for rows.Next() {
			var appId, name, redirectUri, scopes, keyShareUri string
			err = rows.Scan(&appId, &name, &redirectUri, &scopes, &keyShareUri)
			if err != nil {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "39"}, information)
				logFunc(err.Error(), 2, information)
				return
			}

			// Marshal the scopes
			var scopesArray []string
			err = json.Unmarshal([]byte(scopes), &scopesArray)
			if err != nil {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "40"}, information)
				logFunc(err.Error(), 2, information)
				return
			}

			dataOut = append(dataOut, map[string]interface{}{
				"appId":       appId,
				"name":        name,
				"redirectUri": redirectUri,
				"scopes":      scopesArray,
				"keyShareUri": keyShareUri,
			})
		}
		err = rows.Err()
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "41"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Close the rows
		err = rows.Close()
		if err != nil {
			// Memory leak, but we can't do anything about it
			logFunc(err.Error(), 1, information)
		}

		// Return the apps
		renderJSON(200, w, map[string]interface{}{
			"apps": dataOut,
		}, information)
	})

	router.Post("/api/deleteAccount", func(w http.ResponseWriter, r *http.Request) {
		type deleteAccount struct {
			Token string `json:"token"`
		}

		var data deleteAccount
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the session exists
		var userId []byte
		err = mem.QueryRow("SELECT id FROM sessions WHERE session = ?", data.Token).Scan(&userId)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid session"}, information)
			return
		}

		// Delete the user
		_, err = conn.DB.Exec("DELETE FROM users WHERE id = $1", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "42"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Delete the user's sessions
		_, err = mem.Exec("DELETE FROM sessions WHERE id = ?", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "43"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Delete the user's oauth entries
		_, err = conn.DB.Exec("DELETE FROM oauth WHERE creator = ?", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "44"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Delete the user's active logins
		_, err = mem.Exec("DELETE FROM logins WHERE userId = ?", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "45"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Return success
		renderJSON(200, w, map[string]interface{}{"success": true}, information)
	})

	router.Post("/api/session/list", func(w http.ResponseWriter, r *http.Request) {
		type list struct {
			Token string `json:"token"`
		}

		var data list
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the session exists
		var userId []byte
		err = mem.QueryRow("SELECT id FROM sessions WHERE session = ?", data.Token).Scan(&userId)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid session"}, information)
			return
		}

		// Get the sessions
		rows, err := mem.Query("SELECT session, device FROM sessions WHERE id = ?", userId)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "46"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		var dataOut []map[string]interface{}
		for rows.Next() {
			var session, device string
			err = rows.Scan(&session, &device)
			if err != nil {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "47"}, information)
				logFunc(err.Error(), 2, information)
				return
			}
			dataOut = append(dataOut, map[string]interface{}{
				"session": session,
				"device":  device,
			})
		}
		err = rows.Err()
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "48"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Close the rows
		err = rows.Close()
		if err != nil {
			// Memory leak, but we can't do anything about it
			logFunc(err.Error(), 1, information)
		}

		// Return the sessions
		renderJSON(200, w, map[string]interface{}{
			"sessions": dataOut,
		}, information)
	})

	router.Post("/api/session/remove", func(w http.ResponseWriter, r *http.Request) {
		type remove struct {
			Token   string `json:"token"`
			Session string `json:"session"`
		}

		var data remove
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the session exists
		var userId []byte
		err = mem.QueryRow("SELECT id FROM sessions WHERE session = ?", data.Token).Scan(&userId)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid session"}, information)
			return
		}

		// Delete the session
		_, err = mem.Exec("DELETE FROM sessions WHERE id = $1 AND session = ?", userId, data.Session)
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "49"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Return success
		renderJSON(200, w, map[string]interface{}{"success": true}, information)
	})

	router.Post("/api/listUsers", func(w http.ResponseWriter, r *http.Request) {
		type listUsers struct {
			AdminKey string `json:"adminKey"`
		}

		var data listUsers
		err = json.NewDecoder(r.Body).Decode(&data)
		if err != nil {
			renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
			return
		}

		// Check if the admin key is correct
		// Finally, I get to use the admin key
		// In previous versions, the admin key was very important - it handled server cookie-based secrets
		// as well as JWTs - but now it's just a glorified password
		if data.AdminKey != adminKey {
			renderJSON(401, w, map[string]interface{}{"error": "Invalid admin key"}, information)
			return
		}

		// Get the users
		rows, err := conn.DB.Query("SELECT id, username, created FROM users")
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "50"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		var dataOut []map[string]interface{}
		for rows.Next() {
			var id []byte
			var username string
			var created int64
			err = rows.Scan(&id, &username, &created)
			if err != nil {
				renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "51"}, information)
				logFunc(err.Error(), 2, information)
				return
			}
			dataOut = append(dataOut, map[string]interface{}{
				// Also finally get a chance to represent UUIDs as strings. Everywhere else I just use them as bytes.
				"id":       uuid.Must(uuid.FromBytes(id)).String(),
				"username": username,
				"created":  created,
			})
		}
		err = rows.Err()
		if err != nil {
			renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "52"}, information)
			logFunc(err.Error(), 2, information)
			return
		}

		// Close the rows
		err = rows.Close()
		if err != nil {
			// Memory leak, but we can't do anything about it
			logFunc(err.Error(), 1, information)
		}

		// Return the users
		renderJSON(200, w, map[string]interface{}{
			"users": dataOut,
		}, information)
	})

	router.Get("/.well-known/keys.json", func(w http.ResponseWriter, r *http.Request) {
		// Return the public key
		renderJSON(200, w, map[string]interface{}{
			"keys": []map[string]interface{}{
				{
					"kty": "OKP",
					"alg": "EdDSA",
					"use": "sig",
					"kid": base64.StdEncoding.EncodeToString(publicKey[:8]),
					"x":   base64.RawURLEncoding.EncodeToString(publicKey),
					"crv": "Ed25519",
				},
			},
		}, information)
	})

	go func() {
		for {
			// Sleep for half an hour
			time.Sleep(time.Minute * 30)

			// Delete everything in the spent and challenge-response tables that has expired
			affected, err := mem.Exec("DELETE FROM spent WHERE expires < ?", time.Now().Unix())
			if err != nil {
				logFunc(err.Error(), 1, information)
			} else {
				affectedCount, err := affected.RowsAffected()
				if err != nil {
					logFunc(err.Error(), 1, information)
				} else {
					affected, err := mem.Exec("DELETE FROM challengeResponse WHERE expires < ?", time.Now().Unix())
					if err != nil {
						logFunc(err.Error(), 1, information)
					} else {
						affectedCount2, err := affected.RowsAffected()
						if err != nil {
							logFunc(err.Error(), 1, information)
						} else {
							logFunc("Cleanup complete, deleted "+strconv.FormatInt(affectedCount+affectedCount2, 10)+" entries", 0, information)
						}
					}
				}
			}
		}
	}()

	go func() {
		for {
			// Wait for a message
			message := <-information.Inbox

			if message.ServiceID != uuid.MustParse("00000000-0000-0000-0000-000000000001") {
				// Check the message type
				switch message.MessageType {
				case 0:
					// A service would like to know our hostname
					// Send it to them
					information.Outbox <- library.InterServiceMessage{
						MessageType:  0,
						ServiceID:    ServiceInformation.ServiceID,
						ForServiceID: message.ServiceID,
						Message:      hostName,
						SentAt:       time.Now(),
					}
				case 1:
					// A service would like to register a new OAuth entry
					// Generate a new secret
					// It must be able to be sent via JSON, so we can't have pure-binary data
					secret, err := randomChars(512)
					if err != nil {
						information.Outbox <- library.InterServiceMessage{
							MessageType:  1,
							ServiceID:    ServiceInformation.ServiceID,
							ForServiceID: message.ServiceID,
							Message:      "36",
							SentAt:       time.Now(),
						}
						logFunc(err.Error(), 2, information)
						return
					}

					// Generate a new appId
					// It must be able to be sent via JSON, so we can't have pure-binary data
					appId, err := randomChars(32)
					if err != nil {
						information.Outbox <- library.InterServiceMessage{
							MessageType:  1,
							ServiceID:    ServiceInformation.ServiceID,
							ForServiceID: message.ServiceID,
							Message:      "37",
							SentAt:       time.Now(),
						}
						logFunc(err.Error(), 2, information)
						return
					}

					// Validate the scopes
					var clientKeyShare bool
					for _, scope := range message.Message.(authLibrary.OAuthInformation).Scopes {
						if scope != "openid" && scope != "clientKeyShare" {
							information.Outbox <- library.InterServiceMessage{
								MessageType:  2,
								ServiceID:    ServiceInformation.ServiceID,
								ForServiceID: message.ServiceID,
								Message:      "Invalid scope",
								SentAt:       time.Now(),
							}
							return
						} else {
							if scope == "clientKeyShare" {
								clientKeyShare = true
							} else if scope != "openid" {
								logFunc("An impossible logic error has occurred, please move away from radiation or use ECC RAM", 1, information)
								information.Outbox <- library.InterServiceMessage{
									MessageType:  2,
									ServiceID:    ServiceInformation.ServiceID,
									ForServiceID: message.ServiceID,
									Message:      "Invalid scope",
									SentAt:       time.Now(),
								}
								return
							}
						}
					}

					// Marshal the scopes
					scopes, err := json.Marshal(message.Message.(authLibrary.OAuthInformation).Scopes)
					if err != nil {
						information.Outbox <- library.InterServiceMessage{
							MessageType:  1,
							ServiceID:    ServiceInformation.ServiceID,
							ForServiceID: message.ServiceID,
							Message:      "38",
							SentAt:       time.Now(),
						}
						logFunc(err.Error(), 2, information)
						return
					}

					// Insert the oauth entry
					if clientKeyShare {
						_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES ($1, $2, $3, $4, $5, $6, $7)", appId, secret, serviceIDBytes, message.Message.(authLibrary.OAuthInformation).Name, message.Message.(authLibrary.OAuthInformation).RedirectUri, scopes, message.Message.(authLibrary.OAuthInformation).KeyShareUri)
					} else {
						_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes) VALUES ($1, $2, $3, $4, $5, $6)", appId, secret, serviceIDBytes, message.Message.(authLibrary.OAuthInformation).Name, message.Message.(authLibrary.OAuthInformation).RedirectUri, scopes)
					}
					if err != nil {
						information.Outbox <- library.InterServiceMessage{
							MessageType:  1,
							ServiceID:    ServiceInformation.ServiceID,
							ForServiceID: message.ServiceID,
							Message:      "39",
							SentAt:       time.Now(),
						}
						logFunc(err.Error(), 2, information)
						return
					}

					// Return the appId and secret
					information.Outbox <- library.InterServiceMessage{
						MessageType:  0,
						ServiceID:    ServiceInformation.ServiceID,
						ForServiceID: message.ServiceID,
						Message: authLibrary.OAuthResponse{
							AppID:     appId,
							SecretKey: secret,
						},
						SentAt: time.Now(),
					}
				case 2:
					// A service would like to have the public key
					// Send it to them
					information.Outbox <- library.InterServiceMessage{
						MessageType:  2,
						ServiceID:    ServiceInformation.ServiceID,
						ForServiceID: message.ServiceID,
						Message:      publicKey,
						SentAt:       time.Now(),
					}
				}
			}
		}
	}()
}
