|
| 1 | +<script setup lang="ts"> |
| 2 | +import { onMounted, reactive, computed, ComputedRef, useAttrs, ref } from 'vue'; |
| 3 | +import QRCode from 'qrcode'; |
| 4 | +
|
| 5 | +import { Auth, Logger } from 'aws-amplify'; |
| 6 | +import { getActorState, SignInState, translate } from '@aws-amplify/ui'; |
| 7 | +
|
| 8 | +import { useAuth } from '../composables/useAuth'; |
| 9 | +
|
| 10 | +const attrs = useAttrs(); |
| 11 | +const emit = defineEmits(['confirmSetupTOTPSubmit', 'backToSignInClicked']); |
| 12 | +
|
| 13 | +const { state, send } = useAuth(); |
| 14 | +const actorState = computed(() => |
| 15 | + getActorState(state.value) |
| 16 | +) as ComputedRef<SignInState>; |
| 17 | +
|
| 18 | +let qrCode = reactive({ |
| 19 | + qrCodeImageSource: '', |
| 20 | + isLoading: true, |
| 21 | +}); |
| 22 | +let secretKey = ref(''); |
| 23 | +let copyTextLabel = ref(translate('COPY')); |
| 24 | +
|
| 25 | +function copyText() { |
| 26 | + navigator.clipboard.writeText(secretKey.value); |
| 27 | + copyTextLabel.value = translate('COPIED'); |
| 28 | +} |
| 29 | +
|
| 30 | +// lifecycle hooks |
| 31 | +
|
| 32 | +onMounted(async () => { |
| 33 | + const logger = new Logger('SetupTOTP-logger'); |
| 34 | + const { user } = actorState.value.context; |
| 35 | + if (!user) { |
| 36 | + return; |
| 37 | + } |
| 38 | + try { |
| 39 | + secretKey.value = await Auth.setupTOTP(user); |
| 40 | + const issuer = 'AWSCognito'; |
| 41 | + const totpCode = `otpauth://totp/${issuer}:${user.username}?secret=${secretKey}&issuer=${issuer}`; |
| 42 | + qrCode.qrCodeImageSource = await QRCode.toDataURL(totpCode); |
| 43 | + } catch (error) { |
| 44 | + logger.error(error); |
| 45 | + } finally { |
| 46 | + qrCode.isLoading = false; |
| 47 | + } |
| 48 | +}); |
| 49 | +
|
| 50 | +// Computed Properties |
| 51 | +const backSignInText = computed(() => translate('Back to Sign In')); |
| 52 | +const confirmText = computed(() => translate('Confirm')); |
| 53 | +const codeText = computed(() => translate('Code')); |
| 54 | +
|
| 55 | +// Methods |
| 56 | +const onInput = (e: Event): void => { |
| 57 | + const { name, value } = <HTMLInputElement>e.target; |
| 58 | + send({ |
| 59 | + type: 'CHANGE', |
| 60 | + //@ts-ignore |
| 61 | + data: { name, value }, |
| 62 | + }); |
| 63 | +}; |
| 64 | +
|
| 65 | +const onSetupTOTPSubmit = (e: Event): void => { |
| 66 | + if (attrs?.onConfirmSetupTOTPSubmit) { |
| 67 | + emit('confirmSetupTOTPSubmit', e); |
| 68 | + } else { |
| 69 | + submit(e); |
| 70 | + } |
| 71 | +}; |
| 72 | +
|
| 73 | +const submit = (e: Event): void => { |
| 74 | + const formData = new FormData(<HTMLFormElement>e.target); |
| 75 | + send({ |
| 76 | + type: 'SUBMIT', |
| 77 | + //@ts-ignore |
| 78 | + data: { |
| 79 | + //@ts-ignore |
| 80 | + ...Object.fromEntries(formData), |
| 81 | + }, |
| 82 | + }); |
| 83 | +}; |
| 84 | +
|
| 85 | +const onBackToSignInClicked = (): void => { |
| 86 | + if (attrs?.onBackToSignInClicked) { |
| 87 | + emit('backToSignInClicked'); |
| 88 | + } else { |
| 89 | + send({ |
| 90 | + type: 'SIGN_IN', |
| 91 | + }); |
| 92 | + } |
| 93 | +}; |
| 94 | +</script> |
| 95 | + |
1 | 96 | <template>
|
2 | 97 | <slot v-bind="$attrs" name="confirmSetupTOTPI">
|
3 | 98 | <base-wrapper v-bind="$attrs">
|
|
31 | 126 | width="228"
|
32 | 127 | height="228"
|
33 | 128 | />
|
| 129 | + <base-wrapper class="amplify-flex" data-amplify-copy> |
| 130 | + <div>{{ secretKey }}</div> |
| 131 | + <base-wrapper data-amplify-copy-svg @click="copyText"> |
| 132 | + <div data-amplify-copy-tooltip>{{ copyTextLabel }}</div> |
| 133 | + <svg |
| 134 | + width="24" |
| 135 | + height="24" |
| 136 | + viewBox="0 0 24 24" |
| 137 | + fill="none" |
| 138 | + xmlns="http://www.w3.org/2000/svg" |
| 139 | + > |
| 140 | + <path |
| 141 | + d="M16 1H4C2.9 1 2 1.9 2 3V17H4V3H16V1ZM15 5H8C6.9 5 6.01 5.9 6.01 7L6 21C6 22.1 6.89 23 7.99 23H19C20.1 23 21 22.1 21 21V11L15 5ZM8 21V7H14V12H19V21H8Z" |
| 142 | + fill="black" |
| 143 | + /> |
| 144 | + </svg> |
| 145 | + </base-wrapper> |
| 146 | + </base-wrapper> |
34 | 147 | <base-wrapper
|
35 | 148 | class="amplify-flex amplify-field amplify-textfield"
|
36 | 149 | style="flex-direction: column"
|
|
94 | 207 | </base-wrapper>
|
95 | 208 | </slot>
|
96 | 209 | </template>
|
97 |
| - |
98 |
| -<script setup lang="ts"> |
99 |
| -import { onMounted, reactive, computed, ComputedRef, useAttrs } from 'vue'; |
100 |
| -import QRCode from 'qrcode'; |
101 |
| -
|
102 |
| -import { Auth, Logger } from 'aws-amplify'; |
103 |
| -import { getActorState, SignInState, translate } from '@aws-amplify/ui'; |
104 |
| -
|
105 |
| -import { useAuth } from '../composables/useAuth'; |
106 |
| -
|
107 |
| -const attrs = useAttrs(); |
108 |
| -const emit = defineEmits(['confirmSetupTOTPSubmit', 'backToSignInClicked']); |
109 |
| -
|
110 |
| -const { state, send } = useAuth(); |
111 |
| -const actorState = computed(() => |
112 |
| - getActorState(state.value) |
113 |
| -) as ComputedRef<SignInState>; |
114 |
| -
|
115 |
| -let qrCode = reactive({ |
116 |
| - qrCodeImageSource: '', |
117 |
| - isLoading: true, |
118 |
| -}); |
119 |
| -
|
120 |
| -// lifecycle hooks |
121 |
| -
|
122 |
| -onMounted(async () => { |
123 |
| - const logger = new Logger('SetupTOTP-logger'); |
124 |
| - const { user } = actorState.value.context; |
125 |
| - if (!user) { |
126 |
| - return; |
127 |
| - } |
128 |
| - try { |
129 |
| - const secretKey = await Auth.setupTOTP(user); |
130 |
| - const issuer = 'AWSCognito'; |
131 |
| - const totpCode = `otpauth://totp/${issuer}:${user.username}?secret=${secretKey}&issuer=${issuer}`; |
132 |
| - qrCode.qrCodeImageSource = await QRCode.toDataURL(totpCode); |
133 |
| - } catch (error) { |
134 |
| - logger.error(error); |
135 |
| - } finally { |
136 |
| - qrCode.isLoading = false; |
137 |
| - } |
138 |
| -}); |
139 |
| -
|
140 |
| -// Computed Properties |
141 |
| -const backSignInText = computed(() => translate('Back to Sign In')); |
142 |
| -const confirmText = computed(() => translate('Confirm')); |
143 |
| -const codeText = computed(() => translate('Code')); |
144 |
| -
|
145 |
| -// Methods |
146 |
| -const onInput = (e: Event): void => { |
147 |
| - const { name, value } = <HTMLInputElement>e.target; |
148 |
| - send({ |
149 |
| - type: 'CHANGE', |
150 |
| - //@ts-ignore |
151 |
| - data: { name, value }, |
152 |
| - }); |
153 |
| -}; |
154 |
| -
|
155 |
| -const onSetupTOTPSubmit = (e: Event): void => { |
156 |
| - if (attrs?.onConfirmSetupTOTPSubmit) { |
157 |
| - emit('confirmSetupTOTPSubmit', e); |
158 |
| - } else { |
159 |
| - submit(e); |
160 |
| - } |
161 |
| -}; |
162 |
| -
|
163 |
| -const submit = (e: Event): void => { |
164 |
| - const formData = new FormData(<HTMLFormElement>e.target); |
165 |
| - send({ |
166 |
| - type: 'SUBMIT', |
167 |
| - //@ts-ignore |
168 |
| - data: { |
169 |
| - //@ts-ignore |
170 |
| - ...Object.fromEntries(formData), |
171 |
| - }, |
172 |
| - }); |
173 |
| -}; |
174 |
| -
|
175 |
| -const onBackToSignInClicked = (): void => { |
176 |
| - if (attrs?.onBackToSignInClicked) { |
177 |
| - emit('backToSignInClicked'); |
178 |
| - } else { |
179 |
| - send({ |
180 |
| - type: 'SIGN_IN', |
181 |
| - }); |
182 |
| - } |
183 |
| -}; |
184 |
| -</script> |
|
0 commit comments