This commit is contained in:
Manav Rathi 2024-05-23 13:36:16 +05:30
parent 206be5c16f
commit 5e2261f793
No known key found for this signature in database
3 changed files with 46 additions and 53 deletions

View file

@ -175,7 +175,7 @@ const CodeDisplay: React.FC<CodeDisplay> = ({ codeInfo }) => {
const generateCodes = () => {
try {
const currentTime = new Date().getTime();
if (codeInfo.type.toLowerCase() === "totp") {
if (codeInfo.type === "totp") {
const totp = new TOTP({
secret: codeInfo.secret,
algorithm: codeInfo.algorithm,
@ -188,7 +188,7 @@ const CodeDisplay: React.FC<CodeDisplay> = ({ codeInfo }) => {
timestamp: currentTime + codeInfo.period * 1000,
}),
);
} else if (codeInfo.type.toLowerCase() === "hotp") {
} else if (codeInfo.type === "hotp") {
const hotp = new HOTP({
secret: codeInfo.secret,
counter: 0,
@ -417,7 +417,7 @@ function BadCodeInfo({ codeInfo, codeErr }) {
<div>
{showRawData ? (
<div onClick={() => setShowRawData(false)}>
{codeInfo.rawData ?? "no raw data"}
{codeInfo.uriString ?? "(no raw data)"}
</div>
) : (
<div onClick={() => setShowRawData(true)}>Show rawData</div>

View file

@ -1,54 +1,47 @@
import { URI } from "vscode-uri";
type Type = "totp" | "TOTP" | "hotp" | "HOTP";
type Algorithm = "sha1" | "sha256" | "sha512";
export class Code {
// id for the corresponding auth entity
/**
* A parsed representation of an xOTP code URI.
*
* This is all the data we need to drive a OTP generator.
*/
export interface Code {
/** The uniquue id for the corresponding auth entity. */
id?: String;
/** The type of the code. */
type: "totp" | "hotp";
/** The user's account or email for which this code is used. */
account: string;
/** The name of the entity that issued this code. */
issuer: string;
/** Number of digits in the code. */
digits: number;
/**
* The time period (in seconds) for which a single OTP generated from this
* code remains valid.
*/
period: number;
/** The secret that is used to drive the OTP generator. */
secret: string;
algorithm: Algorithm;
type: Type;
rawData?: string;
constructor(
account: string,
issuer: string,
digits: number,
period: number,
secret: string,
algorithm: Algorithm,
type: Type,
rawData?: string,
id?: string,
) {
this.account = account;
this.issuer = issuer;
this.digits = digits;
this.period = period;
this.secret = secret;
this.algorithm = algorithm;
this.type = type;
this.rawData = rawData;
this.id = id;
}
/** The (hashing) algorithim used by the OTP generator. */
algorithm: "sha1" | "sha256" | "sha512";
/** The original string from which this code was generated. */
uriString?: string;
}
/**
* Convert a "raw" OTP secret URL into its parse representation, a {@link Code}.
* Convert a OTP code URI into its parse representation, a {@link Code}.
*
* An example {@link rawData}:
* @param id A unique ID of this code within the auth app.
*
* otpauth://totp/account:user@example.org?algorithm=SHA1&digits=6&issuer=issuer&period=30&secret=ALPHANUM
* @param uriString A string specifying how to generate a TOTP/HOTP/Steam OTP
* code. These strings are of the form:
*
* - (TOTP)
* otpauth://totp/account:user@example.org?algorithm=SHA1&digits=6&issuer=issuer&period=30&secret=ALPHANUM
*/
export const codeFromRawData = (id: string, rawData: string): Code => {
let santizedRawData = rawData
export const codeFromURIString = (id: string, uriString: string): Code => {
let santizedRawData = uriString
.replace(/\+/g, "%2B")
.replace(/:/g, "%3A")
.replaceAll("\r", "");
@ -78,17 +71,17 @@ export const codeFromRawData = (id: string, rawData: string): Code => {
uriPath = uriPath.split("otpauth%3A//")[1];
}
return new Code(
_getAccount(uriPath),
_getIssuer(uriPath, uriParams),
parseDigits(uriParams),
parsePeriod(uriParams),
getSanitizedSecret(uriParams),
parseAlgorithm(uriParams),
_getType(uriPath),
rawData,
return {
id,
);
type: _getType(uriPath),
account: _getAccount(uriPath),
issuer: _getIssuer(uriPath, uriParams),
digits: parseDigits(uriParams),
period: parsePeriod(uriParams),
secret: getSanitizedSecret(uriParams),
algorithm: parseAlgorithm(uriParams),
uriString,
};
};
const _getAccount = (uriPath: string): string => {
@ -135,7 +128,7 @@ const parseDigits = (uriParams): number =>
const parsePeriod = (uriParams): number =>
parseInt(uriParams["period"] ?? "", 10) || 30;
const parseAlgorithm = (uriParams): Algorithm => {
const parseAlgorithm = (uriParams): Code["algorithm"] => {
switch (uriParams["algorithm"]?.toLowerCase()) {
case "sha256":
return "sha256";
@ -146,7 +139,7 @@ const parseAlgorithm = (uriParams): Algorithm => {
}
};
const _getType = (uriPath: string): Type => {
const _getType = (uriPath: string): Code["type"] => {
const oauthType = uriPath.split("/")[0].substring(0);
if (oauthType.toLowerCase() === "totp") {
return "totp";

View file

@ -6,7 +6,7 @@ import { getEndpoint } from "@ente/shared/network/api";
import { getToken } from "@ente/shared/storage/localStorage/helpers";
import { getActualKey } from "@ente/shared/user";
import { HttpStatusCode } from "axios";
import { Code, codeFromRawData } from "services/code";
import { codeFromURIString, type Code } from "services/code";
const ENDPOINT = getEndpoint();
@ -33,7 +33,7 @@ export const getAuthCodes = async (): Promise<Code[]> => {
entity.header,
authenticatorKey,
);
return codeFromRawData(entity.id, decryptedCode);
return codeFromURIString(entity.id, decryptedCode);
} catch (e) {
log.error(`failed to parse codeId = ${entity.id}`);
return null;