(feat): better connection management, utility functions & tests

This commit is contained in:
Alois 2026-04-14 01:37:18 +02:00
commit 822dfea577
14 changed files with 448 additions and 39 deletions

57
dist/index.js vendored
View file

@ -13,17 +13,24 @@ export class TAuthClient {
identifier;
privateKey;
publicKey;
saveSession;
redirectUrl;
schema;
httpServer;
clientMap = new Map();
constructor({ frontendUrl, identifier, privateKey, publicKey, redirectUrl, appData, httpServer, htmlSuccessPage, }) {
appData;
constructor({ frontendUrl, identifier, privateKey, publicKey, saveSession, redirectUrl, appData, httpServer, htmlSuccessPage, }) {
this.frontendUrl = frontendUrl;
this.identifier = identifier;
this.privateKey = privateKey;
this.publicKey = publicKey;
if (typeof saveSession !== "function") {
throw new Error("[tauth] saveSession must be provided as a function");
}
this.saveSession = saveSession;
this.redirectUrl = new URL(redirectUrl);
this.schema = createSchema(appData);
this.appData = appData;
this.httpServer = httpServer ?? {
port: 7878,
hostname: "localhost",
@ -62,8 +69,7 @@ export class TAuthClient {
}
if ((await this.solveChallenge(userId, originalChallenge)) ===
potentialChallenge) {
const { ip_address } = await fetch("https://omega.tensamin.net/api/get/omikron/" + userId).then((res) => res.json());
this.createTTP(userId, potentialSessionId, ip_address);
await this.saveSession(userId, potentialSessionId);
return new Response(htmlSuccessPage || (await readFile("./index.html")), {
headers: {
"Content-Type": "text/html",
@ -133,6 +139,48 @@ export class TAuthClient {
}));
this.clientMap.get(`${userId}:${sessionId}`)?.connect();
}
async loadData(userId, sessionId) {
const connection = await this.createTemporaryTTPConnection(userId, sessionId);
const rawAppData = await connection.send("load_app_data", {});
const appData = JSON.parse(rawAppData.data.app_data);
const safeAppData = this.appData.safeParse(appData);
await connection.close();
if (!safeAppData.success) {
throw new Error(`Invalid app data: ${JSON.stringify(safeAppData.error.issues)}`);
}
return safeAppData.data;
}
async saveData(userId, sessionId, data) {
const connection = await this.createTemporaryTTPConnection(userId, sessionId);
const safeData = this.appData.safeParse(data);
if (!safeData.success) {
throw new Error(`Invalid app data: ${JSON.stringify(safeData.error.issues)}`);
}
await connection.send("save_app_data", {
app_data: JSON.stringify(safeData.data),
});
await connection.close();
}
async createTemporaryTTPConnection(userId, sessionId) {
const { ip_address } = await fetch("https://omega.tensamin.net/api/get/omikron/" + userId).then((res) => res.json());
let temporaryClient;
temporaryClient = createTransportClient(this.schema, {
url: ip_address,
onReadyStateChange: (stateIndex) => {
const state = getFriendlyReadyState(stateIndex);
if (state === "OPEN") {
temporaryClient?.send("identification", {
app_identifier: this.identifier,
app_session_id: sessionId,
app_public_key: this.publicKey,
user_id: userId,
});
}
},
});
temporaryClient.connect();
return temporaryClient;
}
generateLink(challenge) {
return new URL(`?identifier=${this.identifier}&redirect=${this.redirectUrl}${challenge ? `&challenge=${challenge}` : ""}`, this.frontendUrl).toString();
}
@ -159,3 +207,6 @@ export function generateKeyPair() {
public: toBase64(pub),
};
}
export * from "./crypto";
export * from "./schema";
export * from "./user";