Use standard URL parsing - WIP 1

This commit is contained in:
Manav Rathi 2024-05-23 21:01:18 +05:30
parent 1712bf60cb
commit d814b6cdf0
No known key found for this signature in database

View file

@ -1,5 +1,5 @@
import { ensure } from "@/utils/ensure";
import { HOTP, TOTP } from "otpauth";
import { URI } from "vscode-uri";
/**
* A parsed representation of an *OTP code URI.
@ -32,7 +32,7 @@ export interface Code {
/** The (HMAC) algorithm used by the OTP generator. */
algorithm: "sha1" | "sha256" | "sha512";
/** The original string from which this code was generated. */
uriString?: string;
uriString: string;
}
/**
@ -47,50 +47,25 @@ export interface Code {
* otpauth://totp/ACME:user@example.org?algorithm=SHA1&digits=6&issuer=acme&period=30&secret=ALPHANUM
*/
export const codeFromURIString = (id: string, uriString: string): Code => {
const santizedRawData = uriString
.replaceAll("+", "%2B")
.replaceAll(":", "%3A")
.replaceAll("\r", "")
// trim quotes
.replace(/^"|"$/g, "");
const uriParams = {};
const searchParamsString =
decodeURIComponent(santizedRawData).split("?")[1];
searchParamsString.split("&").forEach((pair) => {
const [key, value] = pair.split("=");
uriParams[key] = value;
});
const uri = URI.parse(santizedRawData);
let uriPath = decodeURIComponent(uri.path);
if (uriPath.startsWith("/otpauth://") || uriPath.startsWith("otpauth://")) {
uriPath = uriPath.split("otpauth://")[1];
} else if (uriPath.startsWith("otpauth%3A//")) {
uriPath = uriPath.split("otpauth%3A//")[1];
}
const url = new URL(uriString);
return {
id,
type: _getType(uriPath),
type: parseType(url),
account: _getAccount(uriPath),
issuer: _getIssuer(uriPath, uriParams),
digits: parseDigits(uriParams),
period: parsePeriod(uriParams),
secret: parseSecret(uriParams),
algorithm: parseAlgorithm(uriParams),
digits: parseDigits(url),
period: parsePeriod(url),
secret: parseSecret(url),
algorithm: parseAlgorithm(url),
uriString,
};
};
const _getType = (uriPath: string): Code["type"] => {
const oauthType = uriPath.split("/")[0].substring(0);
if (oauthType.toLowerCase() === "totp") {
return "totp";
} else if (oauthType.toLowerCase() === "hotp") {
return "hotp";
}
throw new Error(`Unsupported format with host ${oauthType}`);
const parseType = (url: URL): Code["type"] => {
const t = url.host.toLowerCase();
if (t == "totp" || t == "hotp") return t;
throw new Error(`Unsupported code with host ${t}`);
};
const _getAccount = (uriPath: string): string => {
@ -131,14 +106,14 @@ const _getIssuer = (uriPath: string, uriParams: { get?: any }): string => {
}
};
const parseDigits = (uriParams): number =>
parseInt(uriParams["digits"] ?? "", 10) || 6;
const parseDigits = (url: URL): number =>
parseInt(url.searchParams.get("digits") ?? "", 10) || 6;
const parsePeriod = (uriParams): number =>
parseInt(uriParams["period"] ?? "", 10) || 30;
const parsePeriod = (url: URL): number =>
parseInt(url.searchParams.get("period") ?? "", 10) || 30;
const parseAlgorithm = (uriParams): Code["algorithm"] => {
switch (uriParams["algorithm"]?.toLowerCase()) {
const parseAlgorithm = (url: URL): Code["algorithm"] => {
switch (url.searchParams.get("algorithm")?.toLowerCase()) {
case "sha256":
return "sha256";
case "sha512":
@ -148,8 +123,8 @@ const parseAlgorithm = (uriParams): Code["algorithm"] => {
}
};
const parseSecret = (uriParams): string =>
uriParams["secret"].replaceAll(" ", "").toUpperCase();
const parseSecret = (url: URL): string =>
ensure(url.searchParams.get("secret")).replaceAll(" ", "").toUpperCase();
/**
* Generate a pair of OTPs (one time passwords) from the given {@link code}.