import { useThemeVars, NProgress, NImage } from 'naive-ui';
import { openBlock, createElementBlock, createElementVNode, defineComponent, toRefs, unref, withCtx, createTextVNode, toDisplayString, createVNode, withModifiers, useSSRContext, computed, ref, isRef } from 'vue';
import { a as __unplugin_components_0, u as useStyleStore, _ as _export_sfc } from './chunk-6003391e.js';
import { _ as _sfc_main$2 } from './chunk-8109fd17.js';
import { _ as __unplugin_components_3$1 } from './chunk-4e7a6a8d.js';
import { ssrRenderAttrs, ssrRenderComponent, ssrInterpolate, ssrRenderStyle } from 'vue/server-renderer';
import { useTimestamp } from '@vueuse/core';
import { u as useQRCode } from './chunk-aa632c49.js';
import { HmacSHA1, enc } from 'crypto-js';
import _ from 'lodash';
import { c as createToken } from './chunk-264f08b8.js';
import { u as useCopy } from './chunk-77c5cc16.js';
import { _ as _sfc_main$3 } from './chunk-de61ec1c.js';
import { c as computedRefreshable } from './chunk-cc665c88.js';
import 'pinia';
import './chunk-11f44f81.js';
import './chunk-35c3d701.js';
import 'qrcode';
import './chunk-95ec8cf7.js';
const _hoisted_1 = {
viewBox: "0 0 24 24",
width: "1.2em",
height: "1.2em"
};
const _hoisted_2 = /*#__PURE__*/createElementVNode("path", {
fill: "currentColor",
d: "M17.65 6.35A7.958 7.958 0 0 0 12 4a8 8 0 0 0-8 8a8 8 0 0 0 8 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18a6 6 0 0 1-6-6a6 6 0 0 1 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35Z"
}, null, -1);
const _hoisted_3 = [
_hoisted_2
];
function render(_ctx, _cache) {
return (openBlock(), createElementBlock("svg", _hoisted_1, _hoisted_3))
}
const __unplugin_components_3 = { name: 'mdi-refresh', render };
/* vite-plugin-components disabled */
function hexToBytes(hex) {
return (hex.match(/.{1,2}/g) ?? []).map((char) => Number.parseInt(char, 16));
}
function computeHMACSha1(message, key) {
return HmacSHA1(enc.Hex.parse(message), enc.Hex.parse(base32toHex(key))).toString(enc.Hex);
}
function base32toHex(base32) {
const base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
const bits = base32.toUpperCase().replace(/=+$/, "").split("").map((value) => base32Chars.indexOf(value).toString(2).padStart(5, "0")).join("");
const hex = (bits.match(/.{1,8}/g) ?? []).map((chunk) => Number.parseInt(chunk, 2).toString(16).padStart(2, "0")).join("");
return hex;
}
function generateHOTP({ key, counter = 0 }) {
const digest = computeHMACSha1(counter.toString(16).padStart(16, "0"), key);
const bytes = hexToBytes(digest);
const offset = bytes[19] & 15;
const v = (bytes[offset] & 127) << 24 | (bytes[offset + 1] & 255) << 16 | (bytes[offset + 2] & 255) << 8 | bytes[offset + 3] & 255;
const code = String(v % 1e6).padStart(6, "0");
return code;
}
function getCounterFromTime({ now, timeStep }) {
return Math.floor(now / 1e3 / timeStep);
}
function generateTOTP({ key, now = Date.now(), timeStep = 30 }) {
const counter = getCounterFromTime({ now, timeStep });
return generateHOTP({ key, counter });
}
function buildKeyUri({
secret,
app = "IT-Tools",
account = "demo-user",
algorithm = "SHA1",
digits = 6,
period = 30
}) {
const params = {
issuer: app,
secret,
algorithm,
digits,
period
};
const paramsString = _(params).map((value, key) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
return `otpauth://totp/${encodeURIComponent(app)}:${encodeURIComponent(account)}?${paramsString}`;
}
function generateSecret() {
return createToken({ length: 16, alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" });
}
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
__name: "token-display",
__ssrInlineRender: true,
props: {
tokens: {}
},
setup(__props) {
const props = __props;
const { copy: copyPrevious, isJustCopied: previousCopied } = useCopy({ createToast: false });
const { copy: copyCurrent, isJustCopied: currentCopied } = useCopy({ createToast: false });
const { copy: copyNext, isJustCopied: nextCopied } = useCopy({ createToast: false });
const { tokens } = toRefs(props);
return (_ctx, _push, _parent, _attrs) => {
const _component_c_tooltip = _sfc_main$2;
const _component_c_button = __unplugin_components_0;
_push(`
Previous
Current OTP
Next
`);
_push(ssrRenderComponent(_component_c_tooltip, {
tooltip: unref(previousCopied) ? "Copied !" : "Copy previous OTP",
position: "bottom",
"flex-1": ""
}, {
default: withCtx((_, _push2, _parent2, _scopeId) => {
if (_push2) {
_push2(ssrRenderComponent(_component_c_button, {
"data-test-id": "previous-otp",
"w-full": "",
"important:h-12": "",
"important:rounded-r-none": "",
"important:font-mono": "",
onClick: ($event) => unref(copyPrevious)(unref(tokens).previous)
}, {
default: withCtx((_2, _push3, _parent3, _scopeId2) => {
if (_push3) {
_push3(`${ssrInterpolate(unref(tokens).previous)}`);
} else {
return [
createTextVNode(toDisplayString(unref(tokens).previous), 1)
];
}
}),
_: 1
}, _parent2, _scopeId));
} else {
return [
createVNode(_component_c_button, {
"data-test-id": "previous-otp",
"w-full": "",
"important:h-12": "",
"important:rounded-r-none": "",
"important:font-mono": "",
onClick: withModifiers(($event) => unref(copyPrevious)(unref(tokens).previous), ["prevent"])
}, {
default: withCtx(() => [
createTextVNode(toDisplayString(unref(tokens).previous), 1)
]),
_: 1
}, 8, ["onClick"])
];
}
}),
_: 1
}, _parent));
_push(ssrRenderComponent(_component_c_tooltip, {
tooltip: unref(currentCopied) ? "Copied !" : "Copy current OTP",
position: "bottom",
"flex-1": "",
"flex-basis-5xl": ""
}, {
default: withCtx((_, _push2, _parent2, _scopeId) => {
if (_push2) {
_push2(ssrRenderComponent(_component_c_button, {
"data-test-id": "current-otp",
"w-full": "",
"important:border-x": "1px solid gray op-40",
"important:h-12": "",
"important:rounded-0": "",
"important:text-22px": "",
onClick: ($event) => unref(copyCurrent)(unref(tokens).current)
}, {
default: withCtx((_2, _push3, _parent3, _scopeId2) => {
if (_push3) {
_push3(`${ssrInterpolate(unref(tokens).current)}`);
} else {
return [
createTextVNode(toDisplayString(unref(tokens).current), 1)
];
}
}),
_: 1
}, _parent2, _scopeId));
} else {
return [
createVNode(_component_c_button, {
"data-test-id": "current-otp",
"w-full": "",
"important:border-x": "1px solid gray op-40",
"important:h-12": "",
"important:rounded-0": "",
"important:text-22px": "",
onClick: withModifiers(($event) => unref(copyCurrent)(unref(tokens).current), ["prevent"])
}, {
default: withCtx(() => [
createTextVNode(toDisplayString(unref(tokens).current), 1)
]),
_: 1
}, 8, ["onClick"])
];
}
}),
_: 1
}, _parent));
_push(ssrRenderComponent(_component_c_tooltip, {
tooltip: unref(nextCopied) ? "Copied !" : "Copy next OTP",
position: "bottom",
"flex-1": ""
}, {
default: withCtx((_, _push2, _parent2, _scopeId) => {
if (_push2) {
_push2(ssrRenderComponent(_component_c_button, {
"data-test-id": "next-otp",
"w-full": "",
"important:h-12": "",
"important:rounded-l-none": "",
onClick: ($event) => unref(copyNext)(unref(tokens).next)
}, {
default: withCtx((_2, _push3, _parent3, _scopeId2) => {
if (_push3) {
_push3(`${ssrInterpolate(unref(tokens).next)}`);
} else {
return [
createTextVNode(toDisplayString(unref(tokens).next), 1)
];
}
}),
_: 1
}, _parent2, _scopeId));
} else {
return [
createVNode(_component_c_button, {
"data-test-id": "next-otp",
"w-full": "",
"important:h-12": "",
"important:rounded-l-none": "",
onClick: withModifiers(($event) => unref(copyNext)(unref(tokens).next), ["prevent"])
}, {
default: withCtx(() => [
createTextVNode(toDisplayString(unref(tokens).next), 1)
]),
_: 1
}, 8, ["onClick"])
];
}
}),
_: 1
}, _parent));
_push(`
`);
};
}
});
const _sfc_setup$1 = _sfc_main$1.setup;
_sfc_main$1.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/tools/otp-code-generator-and-validator/token-display.vue");
return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0;
};
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "otp-code-generator-and-validator",
__ssrInlineRender: true,
setup(__props) {
const now = useTimestamp();
const interval = computed(() => now.value / 1e3 % 30);
const theme = useThemeVars();
const styleStore = useStyleStore();
const secret = ref(generateSecret());
function refreshSecret() {
secret.value = generateSecret();
}
const [tokens] = computedRefreshable(
() => ({
previous: generateTOTP({ key: secret.value, now: now.value - 3e4 }),
current: generateTOTP({ key: secret.value, now: now.value }),
next: generateTOTP({ key: secret.value, now: now.value + 3e4 })
}),
{ throttle: 500 }
);
const keyUri = computed(() => buildKeyUri({ secret: secret.value }));
const { qrcode } = useQRCode({
text: keyUri,
color: {
background: computed(() => styleStore.isDarkTheme ? "#ffffff" : "#00000000"),
foreground: "#000000"
},
options: { width: 210 }
});
const secretValidationRules = [
{
message: "Secret should be a base32 string",
validator: (value) => value.toUpperCase().match(/^[A-Z234567]+$/)
},
{
message: "Please set a secret",
validator: (value) => value !== ""
}
];
return (_ctx, _push, _parent, _attrs) => {
const _component_c_input_text = __unplugin_components_3$1;
const _component_c_tooltip = _sfc_main$2;
const _component_c_button = __unplugin_components_0;
const _component_icon_mdi_refresh = __unplugin_components_3;
const _component_n_progress = NProgress;
const _component_n_image = NImage;
_push(``);
_push(ssrRenderComponent(_component_c_input_text, {
value: unref(secret),
"onUpdate:value": ($event) => isRef(secret) ? secret.value = $event : null,
label: "Secret",
placeholder: "Paste your TOTP secret...",
"mb-5": "",
"validation-rules": secretValidationRules
}, {
suffix: withCtx((_, _push2, _parent2, _scopeId) => {
if (_push2) {
_push2(ssrRenderComponent(_component_c_tooltip, { tooltip: "Generate a new random secret" }, {
default: withCtx((_2, _push3, _parent3, _scopeId2) => {
if (_push3) {
_push3(ssrRenderComponent(_component_c_button, {
circle: "",
variant: "text",
size: "small",
onClick: refreshSecret
}, {
default: withCtx((_3, _push4, _parent4, _scopeId3) => {
if (_push4) {
_push4(ssrRenderComponent(_component_icon_mdi_refresh, null, null, _parent4, _scopeId3));
} else {
return [
createVNode(_component_icon_mdi_refresh)
];
}
}),
_: 1
}, _parent3, _scopeId2));
} else {
return [
createVNode(_component_c_button, {
circle: "",
variant: "text",
size: "small",
onClick: refreshSecret
}, {
default: withCtx(() => [
createVNode(_component_icon_mdi_refresh)
]),
_: 1
})
];
}
}),
_: 1
}, _parent2, _scopeId));
} else {
return [
createVNode(_component_c_tooltip, { tooltip: "Generate a new random secret" }, {
default: withCtx(() => [
createVNode(_component_c_button, {
circle: "",
variant: "text",
size: "small",
onClick: refreshSecret
}, {
default: withCtx(() => [
createVNode(_component_icon_mdi_refresh)
]),
_: 1
})
]),
_: 1
})
];
}
}),
_: 1
}, _parent));
_push(`
`);
_push(ssrRenderComponent(_sfc_main$1, { tokens: unref(tokens) }, null, _parent));
_push(ssrRenderComponent(_component_n_progress, {
percentage: 100 * unref(interval) / 30,
color: unref(theme).primaryColor,
"show-indicator": false
}, null, _parent));
_push(`
Next in ${ssrInterpolate(String(Math.floor(30 - unref(interval))).padStart(2, "0"))}s
`);
_push(ssrRenderComponent(_component_n_image, { src: unref(qrcode) }, null, _parent));
_push(ssrRenderComponent(_component_c_button, {
href: unref(keyUri),
target: "_blank"
}, {
default: withCtx((_, _push2, _parent2, _scopeId) => {
if (_push2) {
_push2(` Open Key URI in new tab `);
} else {
return [
createTextVNode(" Open Key URI in new tab ")
];
}
}),
_: 1
}, _parent));
_push(`
`);
_push(ssrRenderComponent(_sfc_main$3, {
label: "Secret in hexadecimal",
value: unref(base32toHex)(unref(secret)),
readonly: "",
placeholder: "Secret in hex will be displayed here",
"mb-5": ""
}, null, _parent));
_push(ssrRenderComponent(_sfc_main$3, {
label: "Epoch",
value: Math.floor(unref(now) / 1e3).toString(),
readonly: "",
"mb-5": "",
placeholder: "Epoch in sec will be displayed here"
}, null, _parent));
_push(`
Iteration
`);
_push(ssrRenderComponent(_sfc_main$3, {
value: String(unref(getCounterFromTime)({ now: unref(now), timeStep: 30 })),
readonly: "",
label: "Count:",
"label-position": "left",
"label-width": "90px",
"label-align": "right",
placeholder: "Iteration count will be displayed here"
}, null, _parent));
_push(ssrRenderComponent(_sfc_main$3, {
value: unref(getCounterFromTime)({ now: unref(now), timeStep: 30 }).toString(16).padStart(16, "0"),
readonly: "",
placeholder: "Iteration count in hex will be displayed here",
"label-position": "left",
"label-width": "90px",
"label-align": "right",
label: "Padded hex:"
}, null, _parent));
_push(`
`);
};
}
});
/* unplugin-vue-components disabled */const otpCodeGeneratorAndValidator_vue_vue_type_style_index_0_scoped_388f1b9b_lang = '';
const _sfc_setup = _sfc_main.setup;
_sfc_main.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue");
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
};
const otpCodeGeneratorAndValidator = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-388f1b9b"]]);
export { otpCodeGeneratorAndValidator as default };