AddPasskey.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. <script setup lang="ts">
  2. import passkey from '@/api/passkey'
  3. import { useUserStore } from '@/pinia'
  4. import { startRegistration } from '@simplewebauthn/browser'
  5. import { message } from 'ant-design-vue'
  6. const emit = defineEmits(['created'])
  7. const user = useUserStore()
  8. const passkeyName = ref('')
  9. const addPasskeyModelOpen = ref(false)
  10. const passkeyEnabled = ref(false)
  11. const regLoading = ref(false)
  12. async function registerPasskey() {
  13. regLoading.value = true
  14. const optionsJSON = await passkey.begin_registration()
  15. const attestationResponse = await startRegistration({ optionsJSON })
  16. await passkey.finish_registration(attestationResponse, passkeyName.value)
  17. emit('created')
  18. message.success($gettext('Register passkey successfully'))
  19. addPasskeyModelOpen.value = false
  20. user.passkeyRawId = attestationResponse.rawId
  21. regLoading.value = false
  22. }
  23. function addPasskey() {
  24. addPasskeyModelOpen.value = true
  25. passkeyName.value = ''
  26. }
  27. passkey.get_config_status().then(r => {
  28. passkeyEnabled.value = r.status
  29. })
  30. </script>
  31. <template>
  32. <div>
  33. <AButton @click="addPasskey">
  34. {{ $gettext('Add a passkey') }}
  35. </AButton>
  36. <AModal
  37. v-model:open="addPasskeyModelOpen"
  38. :title="$gettext('Add a passkey')"
  39. centered
  40. :mask="false"
  41. :mask-closable="!passkeyEnabled"
  42. :closable="!passkeyEnabled"
  43. :footer="passkeyEnabled ? undefined : false"
  44. :confirm-loading="regLoading"
  45. @ok="registerPasskey"
  46. >
  47. <AForm
  48. v-if="passkeyEnabled"
  49. layout="vertical"
  50. >
  51. <div>
  52. <AAlert
  53. class="mb-4"
  54. :message="$gettext('Tips')"
  55. type="info"
  56. >
  57. <template #description>
  58. <p>{{ $gettext('Please enter a name for the passkey you wish to create and click the OK button below.') }}</p>
  59. <p>{{ $gettext('If your browser supports WebAuthn Passkey, a dialog box will appear.') }}</p>
  60. <p>{{ $gettext('Follow the instructions in the dialog to complete the passkey registration process.') }}</p>
  61. </template>
  62. </AAlert>
  63. </div>
  64. <AFormItem :label="$gettext('Name')">
  65. <AInput v-model:value="passkeyName" />
  66. </AFormItem>
  67. </AForm>
  68. <div v-else>
  69. <AAlert
  70. class="mb-4"
  71. :message="$gettext('Warning')"
  72. type="warning"
  73. show-icon
  74. >
  75. <template #description>
  76. <p>{{ $gettext('You have not configured the settings of Webauthn, so you cannot add a passkey.') }}</p>
  77. <p>
  78. {{ $gettext('To ensure security, Webauthn configuration cannot be added through the UI. '
  79. + 'Please manually configure the following in the app.ini configuration file and restart Nginx UI.') }}
  80. </p>
  81. <pre>[webauthn]
  82. # This is the display name
  83. RPDisplayName = Nginx UI
  84. # The domain name of Nginx UI
  85. RPID = localhost
  86. # The list of origin addresses
  87. RPOrigins = http://localhost:3002</pre>
  88. <p>{{ $gettext('Afterwards, refresh this page and click add passkey again.') }}</p>
  89. <p>
  90. {{ $gettext(`Due to the security policies of some browsers, you cannot use passkeys on non-HTTPS websites, except when running on localhost.`) }}
  91. </p>
  92. </template>
  93. </AAlert>
  94. </div>
  95. </AModal>
  96. </div>
  97. </template>
  98. <style scoped lang="less">
  99. </style>