generate qrcode on frontend, added manual key copy

This commit is contained in:
Spitap
2023-06-30 16:28:00 +02:00
parent a1e03ac117
commit 399eaf5709
5 changed files with 62 additions and 25 deletions
+2 -1
View File
@@ -23,7 +23,8 @@
"vue-gettext": "^2.1.12",
"vue-router": "^3.2.0",
"vuetify": "^2.6.10",
"vuex": "^3.4.0"
"vuex": "^3.4.0",
"qrcode.vue": "~1.7.0"
},
"devDependencies": {
"@babel/core": "^7.12.16",
+2 -2
View File
@@ -16,8 +16,8 @@ export default {
startTFASetup () {
return repository.post(`${resource}/tfa/setup/`)
},
getQRCodeForTFASetup () {
return repository.get(`${resource}/tfa/setup/qr_code/`)
getKeyForTFASetup () {
return repository.get(`${resource}/tfa/setup/key/`)
},
finalizeTFASetup (pinCode) {
const payload = { pin_code: pinCode }
@@ -5,10 +5,29 @@
<span class="text-subtitle-1"><translate>Two factor authentication</translate></span>
</v-card-title>
<v-card-text>
<template v-if="qrCode">
<template v-if="qrURL">
<v-row>
<v-col cols="4">
<div v-html="qrCode"></div>
<v-row rows="2">
<qrcode-vue
:value="qrURL"
:size="200"
render-as="svg"
level="H"
class= "qrcode"
/>
</v-row>
<v-row>
<v-btn color="primary" @click="copyKey" class="key">
Click here to copy the key
<v-icon
v-if="clicked"
color="success"
>
mdi-check-all
</v-icon>
</v-btn>
</v-row>
</v-col>
<v-col cols="6">
<v-alert type="info">
@@ -95,10 +114,12 @@
import account from '@/api/account'
import Cookies from 'js-cookie'
import RecoveryCodesResetDialog from './RecoveryCodesResetDialog'
import QrcodeVue from 'qrcode.vue'
export default {
components: {
RecoveryCodesResetDialog
RecoveryCodesResetDialog,
QrcodeVue
},
props: {
account: Object
@@ -107,20 +128,27 @@ export default {
return {
newTokens: [],
pinCode: null,
qrCode: null,
key: null,
qrURL: null,
clicked: false,
showCodesResetDialog: false,
tokens: []
}
},
methods: {
getQRCode () {
account.getQRCodeForTFASetup().then(resp => {
this.qrCode = resp.data
getKey () {
account.getKeyForTFASetup().then(resp => {
this.key = resp.data.key
this.qrURL = resp.data.url
})
},
copyKey () {
navigator.clipboard.writeText(this.key)
this.clicked = true
},
startTFASetup () {
account.startTFASetup().then(resp => {
this.getQRCode()
this.getKey()
})
},
async finalizeTFASetup () {
@@ -129,7 +157,8 @@ export default {
return
}
const resp = await account.finalizeTFASetup(this.pinCode)
this.qrCode = null
this.key = null
this.qrURL = null
this.tokens = resp.data.tokens
Cookies.set('token', resp.data.access, { sameSite: 'strict' })
Cookies.set('refreshToken', resp.data.refresh, { sameSite: 'strict' })
@@ -153,8 +182,18 @@ export default {
},
created () {
if (!this.account.tfa_enabled) {
this.getQRCode()
this.getKey()
}
}
}
</script>
<style>
.qrcode {
margin-left:20px;
}
.key {
margin-left:10px;
margin-top:10px;
}
</style>
+5
View File
@@ -6350,6 +6350,11 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qrcode.vue@~1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/qrcode.vue/-/qrcode.vue-1.7.0.tgz#c54b2934f0650c10d92785d08aaad36c55e0fc56"
integrity sha512-R7t6Y3fDDtcU7L4rtqwGUDP9xD64gJhIwpfjhRCTKmBoYF6SS49PIJHRJ048cse6OI7iwTwgyy2C46N9Ygoc6g==
qs@6.10.3:
version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
+4 -12
View File
@@ -1,10 +1,5 @@
"""Core API v2 viewsets."""
import io
import qrcode
import qrcode.image.svg
from django_otp.plugins.otp_static.models import StaticDevice, StaticToken
from drf_spectacular.utils import extend_schema
from rest_framework import filters, permissions, response, viewsets
@@ -102,19 +97,16 @@ class AccountViewSet(core_v1_viewsets.AccountViewSet):
"access": str(refresh.access_token)
})
@action(methods=["get"], detail=False, url_path="tfa/setup/qr_code")
def tfa_setup_get_qr_code(self, request):
@action(methods=["get"], detail=False, url_path="tfa/setup/key")
def tfa_setup_get_key(self, request):
"""Get a QR code to finalize the setup process."""
if request.user.tfa_enabled:
return response.Response(status=404)
device = request.user.totpdevice_set.first()
if not device:
return response.Response(status=404)
factory = qrcode.image.svg.SvgPathImage
img = qrcode.make(device.config_url, image_factory=factory)
buf = io.BytesIO()
img.save(buf)
return response.Response(buf.getvalue(), content_type="application/xml")
return response.Response({"key": device.key, "url":device.config_url},
content_type="application/json")
@extend_schema(
request=core_v1_serializers.CheckTFASetupSerializer