Spaces:
Building
Building
Download client/modules/accessKey.ts from Felladrin/MiniSearch: direct link, hf CLI and curl.
- Browser
- Download file 3.88 kB
-
https://huggingface.co/spaces/Felladrin/MiniSearch/resolve/main/client/modules/accessKey.ts
- Command line
-
hf download hf://spaces/Felladrin/MiniSearch/client/modules/accessKey.ts
-
curl -L -o accessKey.ts https://huggingface.co/spaces/Felladrin/MiniSearch/resolve/main/client/modules/accessKey.ts
3.88 kB
| import { notifications } from "@mantine/notifications"; | |
| import { ARGON2_PARAMETERS } from "@shared/argon2Parameters"; | |
| import { argon2id } from "hash-wasm"; | |
| import { addLogEntry } from "./logEntries"; | |
| const ACCESS_KEY_STORAGE_KEY = "accessKeyHash"; | |
| interface StoredAccessKey { | |
| hash: string; | |
| timestamp: number; | |
| } | |
| async function hashAccessKey(accessKey: string): Promise<string> { | |
| const salt = new Uint8Array(16); | |
| crypto.getRandomValues(salt); | |
| return argon2id({ | |
| password: accessKey, | |
| salt, | |
| ...ARGON2_PARAMETERS, | |
| outputType: "encoded", | |
| }); | |
| } | |
| /** | |
| * Validates the key against the server; on success the hash is stored locally | |
| * so later loads can go through `verifyStoredAccessKey`. | |
| */ | |
| export type AccessKeyResult = "valid" | "invalid" | "rateLimited"; | |
| export async function validateAccessKey( | |
| accessKey: string, | |
| ): Promise<AccessKeyResult> { | |
| try { | |
| const hash = await hashAccessKey(accessKey); | |
| const response = await fetch("/api/validate-access-key", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ accessKeyHash: hash }), | |
| }); | |
| // A 429 is "try again in a moment", not "wrong key": keep the two apart so | |
| // the caller can show a refusal message instead of a wrong-key error. | |
| if (response.status === 429) { | |
| addLogEntry("Access key validation rate-limited"); | |
| notifications.show({ | |
| title: "Too many attempts", | |
| message: "Please wait a few seconds and try again", | |
| color: "yellow", | |
| position: "top-right", | |
| }); | |
| return "rateLimited"; | |
| } | |
| const data = await response.json(); | |
| if (data.valid) { | |
| const storedData: StoredAccessKey = { | |
| hash, | |
| timestamp: Date.now(), | |
| }; | |
| localStorage.setItem(ACCESS_KEY_STORAGE_KEY, JSON.stringify(storedData)); | |
| addLogEntry("Access key hash stored"); | |
| return "valid"; | |
| } | |
| return "invalid"; | |
| } catch (error) { | |
| addLogEntry(`Error validating access key: ${error}`); | |
| notifications.show({ | |
| title: "Error validating access key", | |
| message: "Please contact the administrator", | |
| color: "red", | |
| position: "top-right", | |
| }); | |
| return "invalid"; | |
| } | |
| } | |
| /** | |
| * Re-validates the stored hash, still usable for `timeoutHours` since it was | |
| * first validated; 0 means every load asks for the key again. | |
| */ | |
| export async function verifyStoredAccessKey( | |
| timeoutHours: number, | |
| ): Promise<boolean> { | |
| if (timeoutHours === 0) return false; | |
| const storedData = localStorage.getItem(ACCESS_KEY_STORAGE_KEY); | |
| if (!storedData) return false; | |
| try { | |
| const { hash, timestamp }: StoredAccessKey = JSON.parse(storedData); | |
| const expirationTime = timeoutHours * 60 * 60 * 1000; | |
| if (Date.now() - timestamp > expirationTime) { | |
| localStorage.removeItem(ACCESS_KEY_STORAGE_KEY); | |
| addLogEntry("Stored access key expired"); | |
| return false; | |
| } | |
| const response = await fetch("/api/validate-access-key", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ accessKeyHash: hash }), | |
| }); | |
| // A 429 must not be read as "wrong key": the stored hash may be fine and | |
| // the caller simply hit the rate limiter. Keep the hash on disk so a | |
| // later load can still validate it. | |
| if (response.status === 429) { | |
| addLogEntry("Stored access key validation rate-limited"); | |
| return false; | |
| } | |
| const data = await response.json(); | |
| if (!data.valid) { | |
| localStorage.removeItem(ACCESS_KEY_STORAGE_KEY); | |
| addLogEntry("Stored access key is no longer valid"); | |
| return false; | |
| } | |
| addLogEntry("Using stored access key"); | |
| return true; | |
| } catch (error) { | |
| addLogEntry(`Error verifying stored access key: ${error}`); | |
| localStorage.removeItem(ACCESS_KEY_STORAGE_KEY); | |
| return false; | |
| } | |
| } | |