Removed web project (galvanic authentication support in IL2CPP universal patch)

Moved instance ID to header
User instances for profile management
.. other stuff
This commit is contained in:
2025-03-22 21:57:45 -04:00
parent 73e9b72ad4
commit 6cdd0946f4
42 changed files with 663 additions and 3833 deletions

View File

@@ -1,53 +1,107 @@
import { APIUtils, getSrcIpDefault } from "../apiutils.ts";
import { APIUtils, NoBody } from "../apiutils.ts";
// @ts-types = "npm:@types/express"
import express from "express";
import { User } from "../data/users.ts";
import Recaptcha from "../data/recaptcha.ts";
import { Config } from "../config.ts";
import crypto from "node:crypto";
import Logging from "@proxnet/undead-logging";
const log = new Logging("UserRoute");
const config = Config.getConfig();
export const route = APIUtils.createRouter('/user');
type CreateUserBody = {
username: string,
password: string,
recaptcha: string
interface AuthRequestSec {
timestamp: number,
nonce: string,
server_id: string
}
type CreatedUserResponse = {
uuid: string,
backupcode: string
interface AuthRequestRoot {
client_id: string,
message: AuthRequestSec,
signature: string,
pubkey: string
}
const rateLimit = new APIUtils.RateLimiter(10, 1);
const rateLimit = new APIUtils.RateLimiter(60, 1);
route.router.post('/create',
route.router.post('/auth',
rateLimit.middle(),
express.json(),
APIUtils.checkBodyTypes<CreateUserBody>({ username: "test", password: "test", recaptcha: "test" }),
APIUtils.checkBodyTypes<AuthRequestRoot>({
client_id: "asdf",
message: {
timestamp: 0,
nonce: "asdf",
server_id: "asdf"
},
signature: "asdf",
pubkey: "asdf"
}),
async (rq, rs) => {
const body = rq.body as CreateUserBody;
async (rq: express.Request<NoBody, NoBody, AuthRequestRoot>, rs: express.Response) => {
const recaptchaStatus = await Recaptcha.siteVerify(body.recaptcha, getSrcIpDefault(rq));
if (recaptchaStatus) {
const userinit = await User.init({ username: body.username, password: body.password });
if (userinit == null) {
rs.statusCode = 400;
rs.json(APIUtils.genericResponseFormat(true, "Username is already taken"));
} else {
function authFailed(msg: string) {
rs.json(APIUtils.genericResponseFormat(true, msg));
}
const res: CreatedUserResponse = {
uuid: userinit.user.getUuid(),
backupcode: userinit.backupcode
}
rs.json(APIUtils.genericResponseFormat(false, "User created successfully", res));
if (rq.body.message.server_id !== config.public.serverId) {
log.w(`Auth request failed (serverId mismatch), config error?\n given ID: '${rq.body.message.server_id}'\n our ID: '${config.public.serverId}'`);
authFailed('Authentication request not intended for this server.');
return;
}
try {
const verify = crypto.createVerify('SHA256');
verify.update(JSON.stringify(rq.body.message));
verify.end();
const publicKey = await crypto.subtle.importKey(
"spki",
(Uint8Array.from(atob(rq.body.pubkey), c => c.charCodeAt(0))).buffer,
{ name: "ECDSA", namedCurve: "P-256" },
false,
["verify"]
);
const messageBytes = new TextEncoder().encode(JSON.stringify(rq.body.message));
const signatureBytes = Uint8Array.from(atob(rq.body.signature), c => c.charCodeAt(0));
const isValid = await crypto.subtle.verify(
{ name: "ECDSA", hash: "SHA-256" },
publicKey,
signatureBytes.buffer,
messageBytes
);
if (!isValid) {
log.w(`Auth failed for clientId '${rq.body.client_id}'`);
authFailed('Authentication request failed.');
return;
}
} catch (err) {
log.d(`Error when verifying auth request: ${err}`);
authFailed('Authentication request failed.');
return;
}
else {
rs.statusCode = 400;
rs.json(APIUtils.genericResponseFormat(true, "ReCAPTCHA error"));
let user = new User(rq.body.client_id);
if (!(await user.exists())) {
const obj = await User.init({ client_id: rq.body.client_id, pubkey: rq.body.pubkey });
if (obj == null) {
rs.sendStatus(500);
return;
} else user = obj;
}
if (await user.hasNonce(rq.body.message.nonce)) {
log.w(`Client '${rq.body.client_id}' has already used nonce. Replay attack?`);
authFailed('Authentication request failed.');
return;
} else user.addNonce(rq.body.message.nonce);
const token = await user.getToken();
rs.json({ token: token });
}
);