fix(otp-generator): better computation of token

This commit is contained in:
Corentin Thomasset 2023-03-31 00:49:45 +02:00 committed by Corentin THOMASSET
parent 15cb03347c
commit 5281824b5d
2 changed files with 21 additions and 26 deletions

View file

@ -1,15 +1,19 @@
import { computedAsync } from '@vueuse/core'; import { computedAsync, watchThrottled } from '@vueuse/core';
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
export { computedRefreshable, computedRefreshableAsync }; export { computedRefreshable, computedRefreshableAsync };
function computedRefreshable<T>(getter: () => T) { function computedRefreshable<T>(getter: () => T, { throttle }: { throttle?: number } = {}) {
const dirty = ref(true); const dirty = ref(true);
let value: T; let value: T;
const update = () => (dirty.value = true); const update = () => (dirty.value = true);
if (throttle) {
watchThrottled(getter, update, { throttle });
} else {
watch(getter, update); watch(getter, update);
}
const computedValue = computed(() => { const computedValue = computed(() => {
if (dirty.value) { if (dirty.value) {

View file

@ -65,11 +65,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { Refresh } from '@vicons/tabler'; import { Refresh } from '@vicons/tabler';
import { useTimestamp, whenever } from '@vueuse/core'; import { useTimestamp, useWindowFocus, whenever } from '@vueuse/core';
import { useThemeVars } from 'naive-ui'; import { useThemeVars } from 'naive-ui';
import { useStyleStore } from '@/stores/style.store'; import { useStyleStore } from '@/stores/style.store';
import InputCopyable from '@/components/InputCopyable.vue'; import InputCopyable from '@/components/InputCopyable.vue';
import { useValidation } from '@/composable/validation'; import { useValidation } from '@/composable/validation';
import { computedRefreshable } from '@/composable/computedRefreshable';
import { generateTOTP, buildKeyUri, generateSecret, base32toHex, getCounterFromTime } from './otp.service'; import { generateTOTP, buildKeyUri, generateSecret, base32toHex, getCounterFromTime } from './otp.service';
import { useQRCode } from '../qr-code-generator/useQRCode'; import { useQRCode } from '../qr-code-generator/useQRCode';
import TokenDisplay from './token-display.vue'; import TokenDisplay from './token-display.vue';
@ -78,8 +79,18 @@ const now = useTimestamp();
const interval = computed(() => (now.value / 1000) % 30); const interval = computed(() => (now.value / 1000) % 30);
const theme = useThemeVars(); const theme = useThemeVars();
const styleStore = useStyleStore(); const styleStore = useStyleStore();
const secret = ref(generateSecret());
const tokens = ref(buildTokens()); const [secret, refreshSecret] = computedRefreshable(generateSecret);
const [tokens] = computedRefreshable(
() => ({
previous: generateTOTP({ key: secret.value, now: now.value - 30000 }),
current: generateTOTP({ key: secret.value, now: now.value }),
next: generateTOTP({ key: secret.value, now: now.value + 30000 }),
}),
{ throttle: 500 },
);
const keyUri = computed(() => buildKeyUri({ secret: secret.value })); const keyUri = computed(() => buildKeyUri({ secret: secret.value }));
const { qrcode } = useQRCode({ const { qrcode } = useQRCode({
@ -104,26 +115,6 @@ const { attrs: secretValidationAttrs } = useValidation({
}, },
], ],
}); });
// watch + whenever to prevent token to be refresh every raf
watch([secret], refreshToken);
whenever(() => Math.floor(interval.value) === 0, refreshToken);
function refreshSecret() {
secret.value = generateSecret();
}
function refreshToken() {
tokens.value = buildTokens();
}
function buildTokens() {
return {
previous: generateTOTP({ key: secret.value, now: now.value - 30000 }),
current: generateTOTP({ key: secret.value, now: now.value }),
next: generateTOTP({ key: secret.value, now: now.value + 30000 }),
};
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>