AddPasskey.vue 3.5 KB

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