Add client info and device info to token

This commit is contained in:
2024-02-01 12:38:09 +08:00
parent 9247c1ec63
commit 39b2a59ec9
4 changed files with 124 additions and 5 deletions

View File

@@ -7,6 +7,7 @@ import { MdTonalButton } from "../server/dmodule.ts";
import { set_state } from "../server/state.ts";
import pbkdf2Hmac from "pbkdf2-hmac/?target=es2022";
import { encodeBase64 as encode } from "std/encoding/base64.ts";
import { UserAgent } from "std/http/user_agent.ts";
type Props = {
show: boolean;
@@ -46,6 +47,17 @@ export default class Login extends Component<Props> {
if (document.location.protocol === "https:") {
b.append("secure", "1");
}
b.append("client", "fresh");
b.append("client_version", "0.0.1");
b.append("client_platform", "web");
const ua = new UserAgent(navigator.userAgent);
let name = ua.browser.name;
if (name && ua.browser.version) {
name += " " + ua.browser.version;
}
if (name) {
b.append("device", name);
}
const re2 = await fetch("/api/token", { method: "PUT", body: b });
const token = await re2.json();
if (!token.ok) {

71
db.ts
View File

@@ -155,6 +155,10 @@ export type Token = {
http_only: boolean;
secure: boolean;
last_used: Date;
client: string | null;
device: string | null;
client_version: string | null;
client_platform: string | null;
};
type TokenRaw = {
id: number;
@@ -164,6 +168,10 @@ type TokenRaw = {
http_only: number;
secure: number;
last_used: string;
client: string | null;
device: string | null;
client_version: string | null;
client_platform: string | null;
};
const ALL_TABLES = [
"version",
@@ -257,7 +265,11 @@ const TOKEN_TABLE = `CREATE TABLE token (
expired TEXT,
http_only BOOLEAN,
secure BOOLEAN,
last_used TEXT
last_used TEXT,
client TEXT,
device TEXT,
client_version TEXT,
client_platform TEXT
);`;
function escape_fields(fields: string, namespace: string) {
@@ -280,7 +292,7 @@ export class EhDb {
#base_path: string;
#db_path: string;
#use_ffi = false;
readonly version = parse_ver("1.0.0-11");
readonly version = parse_ver("1.0.0-12");
constructor(base_path: string) {
this.#base_path = base_path;
this.#db_path = join(base_path, "data.db");
@@ -434,6 +446,12 @@ export class EhDb {
"UPDATE token SET last_used = '1970-01-01T00:00:00.000Z';",
);
}
if (compare_ver(v, parse_ver("1.0.0-12")) === -1) {
this.db.execute("ALTER TABLE token ADD client TEXT;");
this.db.execute("ALTER TABLE token ADD device TEXT;");
this.db.execute("ALTER TABLE token ADD client_version TEXT;");
this.db.execute("ALTER TABLE token ADD client_platform TEXT;");
}
this.#write_version();
if (need_optimize) this.optimize();
}
@@ -640,13 +658,17 @@ export class EhDb {
added: number,
http_only: boolean,
secure: boolean,
client: string | null,
device: string | null,
client_version: string | null,
client_platform: string | null,
): Token {
let token = randomstring();
while (this.get_token(token)) {
token = randomstring();
}
this.db.query(
"INSERT INTO token (uid, token, expired, http_only, secure, last_used) VALUES (?, ?, ?, ?, ?, ?);",
"INSERT INTO token (uid, token, expired, http_only, secure, last_used, client, device, client_version, client_platform) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
[
uid,
token,
@@ -654,6 +676,10 @@ export class EhDb {
http_only,
secure,
new Date(added),
client,
device,
client_version,
client_platform,
],
);
const t = this.get_token(token);
@@ -1289,6 +1315,45 @@ export class EhDb {
if (!t) throw Error("Failed to update token.");
return t;
}
update_token_info(
token: string,
client: string | null,
device: string | null,
client_version: string | null,
client_platform: string | null,
): Token {
if (
client !== null || device !== null || client_version !== null ||
client_platform !== null
) {
const args = [];
const sets = [];
if (client !== null) {
sets.push("client = ?");
args.push(client);
}
if (device !== null) {
sets.push("device = ?");
args.push(device);
}
if (client_version !== null) {
sets.push("client_version = ?");
args.push(client_version);
}
if (client_platform !== null) {
sets.push("client_platform = ?");
args.push(client_platform);
}
args.push(token);
this.db.query(
`UPDATE token SET ${sets.join(", ")} WHERE token = ?;`,
args,
);
}
const t = this.get_token(token);
if (!t) throw Error("Failed to update token.");
return t;
}
update_token_last_used(token: string) {
this.db.query(
"UPDATE token SET last_used = ? WHERE token = ?;",

View File

@@ -80,6 +80,10 @@ export const handler: Handlers = {
const set_cookie = await parse_bool(data.get("set_cookie"), false);
const http_only = await parse_bool(data.get("http_only"), true);
const secure = await parse_bool(data.get("secure"), false);
const client = await get_string(data.get("client"));
const client_version = await get_string(data.get("client_version"));
const client_platform = await get_string(data.get("client_platform"));
const device = await get_string(data.get("device"));
const m = get_task_manager();
const u = m.db.get_user_by_name(username);
if (!u) return return_error(4, USER_PASSWORD_ERROR);
@@ -89,7 +93,16 @@ export const handler: Handlers = {
if (!isEqual(pa, password)) {
return return_error(4, USER_PASSWORD_ERROR);
}
const token = m.db.add_token(u.id, now, http_only, secure);
const token = m.db.add_token(
u.id,
now,
http_only,
secure,
client,
device,
client_version,
client_platform,
);
const headers: HeadersInit = {};
if (set_cookie) {
headers["Set-Cookie"] =
@@ -99,4 +112,33 @@ export const handler: Handlers = {
}
return return_data(token, 201, headers);
},
async PATCH(req, ctx) {
try {
const data = await req.formData();
let t = await get_string(data.get("token"));
const ttoken = <Token | undefined> ctx.state.token;
if (!t && ttoken) t = ttoken.token;
if (!t) return return_error(1, "token not specififed.");
const client = await get_string(data.get("client"));
const client_version = await get_string(data.get("client_version"));
const client_platform = await get_string(
data.get("client_platform"),
);
const device = await get_string(data.get("device"));
const m = get_task_manager();
const token = m.db.get_token(t);
if (!token) return return_error(404, "token not found.");
return return_data(
m.db.update_token_info(
t,
client,
device,
client_version,
client_platform,
),
);
} catch (e) {
return return_error(500, e.message);
}
},
};

View File

@@ -37,7 +37,7 @@ export async function startServer(path: string) {
await load_translation(task_manager.aborts);
setInterval(() => {
task_manager?.db.remove_expired_token();
}, 86_400_000)
}, 86_400_000);
return start(manifest, {
signal: task_manager.aborts,
plugins: [twindPlugin(twindConfig)],