mirror of
https://github.com/Bubka/2FAuth.git
synced 2025-06-27 23:41:56 +02:00
Show content of non-OTP QR code after a live scan for copying or browsing
This commit is contained in:
parent
685d2bae92
commit
96391fd1d5
69
resources/js_vue3/components/QrContentDisplay.vue
Normal file
69
resources/js_vue3/components/QrContentDisplay.vue
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<script setup>
|
||||||
|
const { copy } = useClipboard({ legacy: true })
|
||||||
|
import { useNotifyStore } from '@/stores/notify'
|
||||||
|
import { UseColorMode } from '@vueuse/components'
|
||||||
|
|
||||||
|
const notify = useNotifyStore()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
qrContent: String,
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a string is an url
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
*/
|
||||||
|
function isUrl(str) {
|
||||||
|
var strRegex = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/
|
||||||
|
var re = new RegExp(strRegex)
|
||||||
|
|
||||||
|
return re.test(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens an uri in a new browser window
|
||||||
|
*
|
||||||
|
* @param {string} uri
|
||||||
|
*/
|
||||||
|
function openInBrowser(uri) {
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.setAttribute('href', uri)
|
||||||
|
a.dispatchEvent(new MouseEvent("click", { 'view': window, 'bubbles': true, 'cancelable': true }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies to clipboard and notify
|
||||||
|
*
|
||||||
|
* @param {*} data
|
||||||
|
*/
|
||||||
|
function copyToClipboard(data) {
|
||||||
|
copy(data)
|
||||||
|
notify.success({ text: trans('commons.copied_to_clipboard') })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="too-bad"></div>
|
||||||
|
<div class="block">
|
||||||
|
{{ $t('errors.data_of_qrcode_is_not_valid_URI') }}
|
||||||
|
</div>
|
||||||
|
<UseColorMode v-slot="{ mode }">
|
||||||
|
<div class="block mb-6" :class="mode == 'dark' ? 'has-text-light':'has-text-grey-dark'">{{ qrContent ? qrContent : '[' + trans('commons.nothing') + ']' }}</div>
|
||||||
|
</UseColorMode>
|
||||||
|
<!-- Copy to clipboard -->
|
||||||
|
<div class="block has-text-link" v-if="qrContent">
|
||||||
|
<button class="button is-link is-outlined is-rounded" @click.stop="copyToClipboard(qrContent)">
|
||||||
|
{{ $t('commons.copy_to_clipboard') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<!-- Open in browser -->
|
||||||
|
<div class="block has-text-link" v-if="isUrl(qrContent)" @click="openInBrowser(qrContent)">
|
||||||
|
<button class="button is-link is-outlined is-rounded">
|
||||||
|
<span>{{ $t('commons.open_in_browser') }}</span>
|
||||||
|
<span class="icon is-small">
|
||||||
|
<FontAwesomeIcon :icon="['fas', 'external-link-alt']" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Form from '@/components/formElements/Form'
|
import Form from '@/components/formElements/Form'
|
||||||
import Spinner from '@/components/Spinner.vue'
|
import Spinner from '@/components/Spinner.vue'
|
||||||
|
import QrContentDisplay from '@/components/QrContentDisplay.vue'
|
||||||
import { useBusStore } from '@/stores/bus'
|
import { useBusStore } from '@/stores/bus'
|
||||||
import { UseColorMode } from '@vueuse/components'
|
import { UseColorMode } from '@vueuse/components'
|
||||||
import { useNotifyStore } from '@/stores/notify'
|
import { useNotifyStore } from '@/stores/notify'
|
||||||
@ -18,6 +19,7 @@
|
|||||||
qrcode: null,
|
qrcode: null,
|
||||||
uri: '',
|
uri: '',
|
||||||
}))
|
}))
|
||||||
|
const showQrContent = ref(false)
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
devices.value = (await navigator.mediaDevices.enumerateDevices())
|
devices.value = (await navigator.mediaDevices.enumerateDevices())
|
||||||
@ -65,6 +67,7 @@
|
|||||||
router.push({ name: 'importAccounts' })
|
router.push({ name: 'importAccounts' })
|
||||||
}
|
}
|
||||||
else if (form.uri.slice(0, 15).toLowerCase() !== "otpauth://totp/" && form.uri.slice(0, 15).toLowerCase() !== "otpauth://hotp/") {
|
else if (form.uri.slice(0, 15).toLowerCase() !== "otpauth://totp/" && form.uri.slice(0, 15).toLowerCase() !== "otpauth://hotp/") {
|
||||||
|
showQrContent.value = true
|
||||||
notify.warn({ text: trans('errors.no_valid_otp') })
|
notify.warn({ text: trans('errors.no_valid_otp') })
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -200,4 +203,7 @@
|
|||||||
<ButtonBackCloseCancel action="cancel" :isCapture="true" :useLinkTag="false" @canceled="exitStream()" />
|
<ButtonBackCloseCancel action="cancel" :isCapture="true" :useLinkTag="false" @canceled="exitStream()" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<modal v-model="showQrContent">
|
||||||
|
<QrContentDisplay :qrContent="form.uri" />
|
||||||
|
</modal>
|
||||||
</template>
|
</template>
|
@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Form from '@/components/formElements/Form'
|
import Form from '@/components/formElements/Form'
|
||||||
import OtpDisplay from '@/components/OtpDisplay.vue'
|
import OtpDisplay from '@/components/OtpDisplay.vue'
|
||||||
|
import QrContentDisplay from '@/components/QrContentDisplay.vue'
|
||||||
import FormLockField from '@/components/formelements/FormLockField.vue'
|
import FormLockField from '@/components/formelements/FormLockField.vue'
|
||||||
import twofaccountService from '@/services/twofaccountService'
|
import twofaccountService from '@/services/twofaccountService'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
@ -9,7 +10,6 @@
|
|||||||
import { useNotifyStore } from '@/stores/notify'
|
import { useNotifyStore } from '@/stores/notify'
|
||||||
import { UseColorMode } from '@vueuse/components'
|
import { UseColorMode } from '@vueuse/components'
|
||||||
|
|
||||||
const { copy } = useClipboard({ legacy: true })
|
|
||||||
const $2fauth = inject('2fauth')
|
const $2fauth = inject('2fauth')
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@ -347,39 +347,6 @@
|
|||||||
return str.replace(/(<([^> ]+)>)/ig, "")
|
return str.replace(/(<([^> ]+)>)/ig, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a string is an url
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
*/
|
|
||||||
function isUrl(str) {
|
|
||||||
var strRegex = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/
|
|
||||||
var re = new RegExp(strRegex)
|
|
||||||
|
|
||||||
return re.test(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens an uri in a new browser window
|
|
||||||
*
|
|
||||||
* @param {string} uri
|
|
||||||
*/
|
|
||||||
function openInBrowser(uri) {
|
|
||||||
const a = document.createElement('a')
|
|
||||||
a.setAttribute('href', uri)
|
|
||||||
a.dispatchEvent(new MouseEvent("click", { 'view': window, 'bubbles': true, 'cancelable': true }))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies to clipboard and notify
|
|
||||||
*
|
|
||||||
* @param {*} data
|
|
||||||
*/
|
|
||||||
function copyToClipboard(data) {
|
|
||||||
copy(data)
|
|
||||||
notify.success({ text: trans('commons.copied_to_clipboard') })
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -534,28 +501,7 @@
|
|||||||
</FormWrapper>
|
</FormWrapper>
|
||||||
<!-- alternatives -->
|
<!-- alternatives -->
|
||||||
<modal v-model="showAlternatives">
|
<modal v-model="showAlternatives">
|
||||||
<div class="too-bad"></div>
|
<QrContentDisplay :qrContent="uri" />
|
||||||
<div class="block">
|
|
||||||
{{ $t('errors.data_of_qrcode_is_not_valid_URI') }}
|
|
||||||
</div>
|
|
||||||
<UseColorMode v-slot="{ mode }">
|
|
||||||
<div class="block mb-6" :class="mode == 'dark' ? 'has-text-light':'has-text-grey-dark'">{{ uri }}</div>
|
|
||||||
</UseColorMode>
|
|
||||||
<!-- Copy to clipboard -->
|
|
||||||
<div class="block has-text-link">
|
|
||||||
<button class="button is-link is-outlined is-rounded" @click.stop="copyToClipboard(uri)">
|
|
||||||
{{ $t('commons.copy_to_clipboard') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<!-- Open in browser -->
|
|
||||||
<div class="block has-text-link" v-if="isUrl(uri)" @click="openInBrowser(uri)">
|
|
||||||
<button class="button is-link is-outlined is-rounded">
|
|
||||||
<span>{{ $t('commons.open_in_browser') }}</span>
|
|
||||||
<span class="icon is-small">
|
|
||||||
<FontAwesomeIcon :icon="['fas', 'external-link-alt']" />
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</modal>
|
</modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -75,4 +75,5 @@ return [
|
|||||||
'submit' => 'Submit',
|
'submit' => 'Submit',
|
||||||
'default' => 'Default',
|
'default' => 'Default',
|
||||||
'back_to_home' => 'Back to home',
|
'back_to_home' => 'Back to home',
|
||||||
|
'nothing' => 'nothing',
|
||||||
];
|
];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user