Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | import * as OTPAuth from "otpauth";
import qrcode from "qrcode-generator";
import qrcodeParser from "qrcode-parser";
import { OTPAlgorithms } from "../Interfaces/Credential/OTPConfigInterface";
import { assertDocumentAvailable } from "../Util/DomEnvironment";
/**
* OTP helpers. Token generation ({@link OTPService.updateOTP}) and otpauth URI building
* ({@link OTPService.getDataUrlFromCurrentOTPValues}) are SW-safe (no DOM).
* QR image rendering ({@link OTPService.updateQRFromCurrentOTPValues}) and QR file parsing
* ({@link OTPService.parseOTPQrCodeFromInputFileData}) require a DOM and are guarded for MV3.
*/
export class OTPService {
static mergeDefaultOTPConfig = (otp) => {
const defaults = {
algorithm: "SHA1",
period: 30,
digits: 6,
};
Object.assign(otp, { ...defaults, ...otp });
};
static getSecretString = (secret) => {
if (typeof secret == "object") {
return secret.utf8;
}
return secret;
};
static getDataUrlFromCurrentOTPValues = (otp) => {
const totp = new OTPAuth.TOTP({
issuer: otp.issuer,
label: otp.label,
algorithm: otp.algorithm,
digits: otp.digits,
period: otp.period,
secret: otp.secret
});
return decodeURIComponent(totp.toString());
};
/**
* Updates otp.qr_uri with a new QR code image and otpauth data URL based on the raw otp values.
* Requires a DOM (canvas). Not available in MV3 service workers. Generate the token/URI there
* with {@link OTPService.updateOTP} / {@link OTPService.getDataUrlFromCurrentOTPValues}, and
* render the QR in a page/popup/offscreen document.
* @param otp
* @throws Error when called without a DOM
*/
static updateQRFromCurrentOTPValues = (otp) => {
assertDocumentAvailable("OTPService.updateQRFromCurrentOTPValues");
if (otp.qr_uri === undefined) {
otp.qr_uri = {};
}
const typeNumberAutoDetect = 0;
const errorCorrectionLevel = 'L'; // L = 7%
const qr = qrcode(typeNumberAutoDetect, errorCorrectionLevel);
if (typeof otp.qr_uri == "object") {
otp.qr_uri.qrData = OTPService.getDataUrlFromCurrentOTPValues(otp);
}
else if (typeof otp.qr_uri == "string") {
otp.qr_uri = {
qrData: otp.qr_uri,
image: null
};
}
qr.addData(otp.qr_uri.qrData);
qr.make();
const cellSize = 4;
const padding = 0;
const canvas = document.createElement("canvas");
canvas.width = canvas.height = qr.getModuleCount() * cellSize + padding * 2;
const context = canvas.getContext("2d");
qr.renderTo2dContext(context, cellSize);
otp.qr_uri.image = canvas.toDataURL("image/png");
canvas.remove();
};
/**
* Returns the current token of the given OTP configuration.
* @param otp
*/
static updateOTP = (otp) => {
if (!otp || !otp.secret || otp.secret === "") {
return;
}
if (typeof otp.secret == "string" && otp.secret.includes(' ')) {
otp.secret = otp.secret.replaceAll(' ', '');
}
OTPService.mergeDefaultOTPConfig(otp);
const totp = new OTPAuth.TOTP({
issuer: otp.issuer,
label: otp.label,
algorithm: otp.algorithm,
digits: otp.digits,
period: otp.period,
secret: otp.secret
});
return totp.generate();
};
/**
* Parse input file asynchronous as QR code, extract the TOTP values and return the IOTPConfig, that's built from it.
* Requires a DOM (qrcode-parser uses browser image APIs). Not available in MV3 service workers.
* @param input
* @throws Error when called without a DOM
*/
static parseOTPQrCodeFromInputFileData = async (input) => {
assertDocumentAvailable("OTPService.parseOTPQrCodeFromInputFileData");
return await qrcodeParser(input).then((otpUrl) => {
const uri = new URL(otpUrl);
const type = (uri.href.indexOf('totp/') !== -1) ? 'totp' : 'hotp';
const label = uri.pathname.replace('//' + type + '/', '');
const otp = {
type: type,
label: decodeURIComponent(label),
qr_uri: {
qrData: decodeURIComponent(otpUrl),
image: input
},
issuer: uri.searchParams.get('issuer'),
secret: uri.searchParams.get('secret'),
algorithm: uri.searchParams.get('algorithm') && OTPAlgorithms.includes(uri.searchParams.get('algorithm'))
? uri.searchParams.get('algorithm')
: "SHA1",
period: uri.searchParams.get('period') ? parseInt(uri.searchParams.get('period')) : 30,
digits: uri.searchParams.get('digits') ? parseInt(uri.searchParams.get('digits')) : 6,
};
return otp;
});
};
}
|