@adminforth/two-factors-auth 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build.log +2 -2
- package/custom/TwoFaModal.vue +20 -10
- package/custom/TwoFactorsConfirmation.vue +6 -5
- package/custom/TwoFactorsSetup.vue +23 -24
- package/dist/custom/TwoFaModal.vue +20 -10
- package/dist/custom/TwoFactorsConfirmation.vue +6 -5
- package/dist/custom/TwoFactorsSetup.vue +23 -24
- package/dist/index.js +6 -3
- package/index.ts +6 -3
- package/package.json +1 -1
- package/types.ts +2 -0
package/build.log
CHANGED
|
@@ -11,5 +11,5 @@ custom/package-lock.json
|
|
|
11
11
|
custom/package.json
|
|
12
12
|
custom/tsconfig.json
|
|
13
13
|
|
|
14
|
-
sent 31,
|
|
15
|
-
total size is 30,
|
|
14
|
+
sent 31,326 bytes received 134 bytes 62,920.00 bytes/sec
|
|
15
|
+
total size is 30,840 speedup is 0.98
|
package/custom/TwoFaModal.vue
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 top-0 bottom-0 left-0 right-0"
|
|
3
3
|
v-if ="modelShow">
|
|
4
4
|
<div class="relative bg-white dark:bg-gray-700 rounded-lg shadow p-6 w-full max-w-md">
|
|
5
|
-
<div id="mfaCode-label" class="mb-4 text-gray-700 dark:text-gray-100">
|
|
5
|
+
<div id="mfaCode-label" class="mb-4 text-gray-700 dark:text-gray-100 text-center">
|
|
6
6
|
{{ $t('Please enter your authenticator code') }}
|
|
7
7
|
</div>
|
|
8
8
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
<script setup lang="ts">
|
|
36
36
|
import VOtpInput from 'vue3-otp-input';
|
|
37
|
-
import { ref, nextTick,
|
|
37
|
+
import { ref, nextTick, watch } from 'vue';
|
|
38
38
|
import { useUserStore } from '@/stores/user';
|
|
39
39
|
import { useI18n } from 'vue-i18n';
|
|
40
40
|
declare global {
|
|
@@ -131,14 +131,24 @@
|
|
|
131
131
|
emit('rejected', new Error('cancelled'));
|
|
132
132
|
emit('closed');
|
|
133
133
|
}
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
|
|
135
|
+
watch(modelShow, async (open) => {
|
|
136
|
+
if (open) {
|
|
136
137
|
await nextTick();
|
|
137
138
|
tagOtpInputs();
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
console.log('adding paste listener');
|
|
140
|
+
window.addEventListener('paste', handlePaste);
|
|
141
|
+
} else {
|
|
142
|
+
console.log('re paste listener');
|
|
143
|
+
window.removeEventListener('paste', handlePaste);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
143
147
|
</script>
|
|
144
|
-
|
|
148
|
+
|
|
149
|
+
<style scoped>
|
|
150
|
+
:deep(.otp-input-container) {
|
|
151
|
+
display: flex;
|
|
152
|
+
gap: 0.75rem;
|
|
153
|
+
}
|
|
154
|
+
</style>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<v-otp-input
|
|
19
19
|
ref="code"
|
|
20
20
|
container-class="grid grid-cols-6 gap-3 w-full"
|
|
21
|
-
input-classes="bg-gray-50 text-center
|
|
21
|
+
input-classes="bg-gray-50 text-center justify-center otp-input border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-10 h-10 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
22
22
|
:num-inputs="6"
|
|
23
23
|
inputType="number"
|
|
24
24
|
inputmode="numeric"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
<div class="mt-6 flex justify-center">
|
|
32
32
|
<LinkButton
|
|
33
33
|
to="/login"
|
|
34
|
-
class="w-
|
|
34
|
+
class="w-full"
|
|
35
35
|
>
|
|
36
36
|
{{$t('Back to login')}}
|
|
37
37
|
</LinkButton>
|
|
@@ -130,15 +130,16 @@
|
|
|
130
130
|
}
|
|
131
131
|
</script>
|
|
132
132
|
|
|
133
|
-
<style lang='scss'>
|
|
133
|
+
<style scoped lang='scss'>
|
|
134
134
|
.vue3-2fa-code-input {
|
|
135
135
|
display: flex;
|
|
136
136
|
justify-content: center;
|
|
137
137
|
gap: 1rem;
|
|
138
138
|
margin-bottom: 1rem;
|
|
139
139
|
}
|
|
140
|
-
.otp-input {
|
|
141
|
-
|
|
140
|
+
:deep(.otp-input-container) {
|
|
141
|
+
display: flex;
|
|
142
|
+
gap: 0.75rem;
|
|
142
143
|
}
|
|
143
144
|
.vue3-2fa-code-input-box {
|
|
144
145
|
&[type='text'] {
|
|
@@ -15,29 +15,27 @@
|
|
|
15
15
|
<div class="p-10 w-full max-w-md max-h-full custom-auth-wrapper" >
|
|
16
16
|
<div class="m-3" >{{$t('Scan this QR code with your authenticator app or open by')}} <a class="text-blue-600" :href="totpUri">{{$t('click')}}</a></div>
|
|
17
17
|
<div class="flex justify-center m-3" >
|
|
18
|
-
<img :src="totpQrCode" class="min-w-[200px]
|
|
18
|
+
<img :src="totpQrCode" class="min-w-[200px] min-h-[200px]" alt="QR code" />
|
|
19
19
|
</div>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
</div>
|
|
36
|
-
<div class="my-4 w-full flex justify-center p-2" ref="otpRoot">
|
|
20
|
+
<div class="m-1">{{$t('Or copy this code to app manually:')}}</div>
|
|
21
|
+
<div class="w-full">
|
|
22
|
+
<div class="relative">
|
|
23
|
+
<label for="npm-install-copy-text" class="sr-only">{{$t('Label')}}</label>
|
|
24
|
+
<input id="npm-install-copy-text" type="text" class="col-span-10 bg-gray-50 border border-gray-300 text-gray-500 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full px-2.5 py-4 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500 pr-12" :value="totp.newSecret" readonly>
|
|
25
|
+
<button @click="onCopyClick" data-copy-to-clipboard-target="npm-install-copy-text" class="absolute end-2.5 top-1/2 -translate-y-1/2 text-gray-900 dark:text-gray-400 hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-600 dark:hover:bg-gray-700 rounded-lg py-2 px-2.5 inline-flex items-center justify-center bg-white border-gray-200 border">
|
|
26
|
+
<span id="default-message" class="inline-flex items-center">
|
|
27
|
+
<svg class="w-3 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 20">
|
|
28
|
+
<path d="M16 1h-3.278A1.992 1.992 0 0 0 11 0H7a1.993 1.993 0 0 0-1.722 1H2a2 2 0 0 0-2 2v15a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2Zm-3 14H5a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2Zm0-4H5a1 1 0 0 1 0-2h8a1 1 0 1 1 0 2Zm0-5H5a1 1 0 0 1 0-2h2V2h4v2h2a1 1 0 1 1 0 2Z"/>
|
|
29
|
+
</svg>
|
|
30
|
+
</span>
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="my-4 w-full flex justify-center" ref="otpRoot">
|
|
37
35
|
<v-otp-input
|
|
38
36
|
ref="code"
|
|
39
37
|
container-class="grid grid-cols-6 gap-3 w-full"
|
|
40
|
-
input-classes="otp-input bg-gray-50 text-center border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block h-[
|
|
38
|
+
input-classes="otp-input bg-gray-50 text-center border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block h-[46px] w-full dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
41
39
|
:num-inputs="6"
|
|
42
40
|
inputType="number"
|
|
43
41
|
inputmode="numeric"
|
|
@@ -48,7 +46,7 @@
|
|
|
48
46
|
/>
|
|
49
47
|
</div>
|
|
50
48
|
<!-- <Vue2FACodeInput v-model="code" autofocus /> -->
|
|
51
|
-
<div class="flex flex-row gap-2.5
|
|
49
|
+
<div class="flex flex-row gap-2.5 h-12">
|
|
52
50
|
<LinkButton to="/login" class="w-full">
|
|
53
51
|
{{$t('Back to login')}}
|
|
54
52
|
</LinkButton>
|
|
@@ -220,8 +218,9 @@ const handleSkip = async () => {
|
|
|
220
218
|
}
|
|
221
219
|
}
|
|
222
220
|
</script>
|
|
223
|
-
<style>
|
|
224
|
-
.otp-input {
|
|
225
|
-
|
|
226
|
-
|
|
221
|
+
<style scoped>
|
|
222
|
+
:deep(.otp-input-container) {
|
|
223
|
+
display: flex;
|
|
224
|
+
gap: 0.75rem;
|
|
225
|
+
}
|
|
227
226
|
</style>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 top-0 bottom-0 left-0 right-0"
|
|
3
3
|
v-if ="modelShow">
|
|
4
4
|
<div class="relative bg-white dark:bg-gray-700 rounded-lg shadow p-6 w-full max-w-md">
|
|
5
|
-
<div id="mfaCode-label" class="mb-4 text-gray-700 dark:text-gray-100">
|
|
5
|
+
<div id="mfaCode-label" class="mb-4 text-gray-700 dark:text-gray-100 text-center">
|
|
6
6
|
{{ $t('Please enter your authenticator code') }}
|
|
7
7
|
</div>
|
|
8
8
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
<script setup lang="ts">
|
|
36
36
|
import VOtpInput from 'vue3-otp-input';
|
|
37
|
-
import { ref, nextTick,
|
|
37
|
+
import { ref, nextTick, watch } from 'vue';
|
|
38
38
|
import { useUserStore } from '@/stores/user';
|
|
39
39
|
import { useI18n } from 'vue-i18n';
|
|
40
40
|
declare global {
|
|
@@ -131,14 +131,24 @@
|
|
|
131
131
|
emit('rejected', new Error('cancelled'));
|
|
132
132
|
emit('closed');
|
|
133
133
|
}
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
|
|
135
|
+
watch(modelShow, async (open) => {
|
|
136
|
+
if (open) {
|
|
136
137
|
await nextTick();
|
|
137
138
|
tagOtpInputs();
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
console.log('adding paste listener');
|
|
140
|
+
window.addEventListener('paste', handlePaste);
|
|
141
|
+
} else {
|
|
142
|
+
console.log('re paste listener');
|
|
143
|
+
window.removeEventListener('paste', handlePaste);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
143
147
|
</script>
|
|
144
|
-
|
|
148
|
+
|
|
149
|
+
<style scoped>
|
|
150
|
+
:deep(.otp-input-container) {
|
|
151
|
+
display: flex;
|
|
152
|
+
gap: 0.75rem;
|
|
153
|
+
}
|
|
154
|
+
</style>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<v-otp-input
|
|
19
19
|
ref="code"
|
|
20
20
|
container-class="grid grid-cols-6 gap-3 w-full"
|
|
21
|
-
input-classes="bg-gray-50 text-center
|
|
21
|
+
input-classes="bg-gray-50 text-center justify-center otp-input border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-10 h-10 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
22
22
|
:num-inputs="6"
|
|
23
23
|
inputType="number"
|
|
24
24
|
inputmode="numeric"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
<div class="mt-6 flex justify-center">
|
|
32
32
|
<LinkButton
|
|
33
33
|
to="/login"
|
|
34
|
-
class="w-
|
|
34
|
+
class="w-full"
|
|
35
35
|
>
|
|
36
36
|
{{$t('Back to login')}}
|
|
37
37
|
</LinkButton>
|
|
@@ -130,15 +130,16 @@
|
|
|
130
130
|
}
|
|
131
131
|
</script>
|
|
132
132
|
|
|
133
|
-
<style lang='scss'>
|
|
133
|
+
<style scoped lang='scss'>
|
|
134
134
|
.vue3-2fa-code-input {
|
|
135
135
|
display: flex;
|
|
136
136
|
justify-content: center;
|
|
137
137
|
gap: 1rem;
|
|
138
138
|
margin-bottom: 1rem;
|
|
139
139
|
}
|
|
140
|
-
.otp-input {
|
|
141
|
-
|
|
140
|
+
:deep(.otp-input-container) {
|
|
141
|
+
display: flex;
|
|
142
|
+
gap: 0.75rem;
|
|
142
143
|
}
|
|
143
144
|
.vue3-2fa-code-input-box {
|
|
144
145
|
&[type='text'] {
|
|
@@ -15,29 +15,27 @@
|
|
|
15
15
|
<div class="p-10 w-full max-w-md max-h-full custom-auth-wrapper" >
|
|
16
16
|
<div class="m-3" >{{$t('Scan this QR code with your authenticator app or open by')}} <a class="text-blue-600" :href="totpUri">{{$t('click')}}</a></div>
|
|
17
17
|
<div class="flex justify-center m-3" >
|
|
18
|
-
<img :src="totpQrCode" class="min-w-[200px]
|
|
18
|
+
<img :src="totpQrCode" class="min-w-[200px] min-h-[200px]" alt="QR code" />
|
|
19
19
|
</div>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
</div>
|
|
36
|
-
<div class="my-4 w-full flex justify-center p-2" ref="otpRoot">
|
|
20
|
+
<div class="m-1">{{$t('Or copy this code to app manually:')}}</div>
|
|
21
|
+
<div class="w-full">
|
|
22
|
+
<div class="relative">
|
|
23
|
+
<label for="npm-install-copy-text" class="sr-only">{{$t('Label')}}</label>
|
|
24
|
+
<input id="npm-install-copy-text" type="text" class="col-span-10 bg-gray-50 border border-gray-300 text-gray-500 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full px-2.5 py-4 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500 pr-12" :value="totp.newSecret" readonly>
|
|
25
|
+
<button @click="onCopyClick" data-copy-to-clipboard-target="npm-install-copy-text" class="absolute end-2.5 top-1/2 -translate-y-1/2 text-gray-900 dark:text-gray-400 hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-600 dark:hover:bg-gray-700 rounded-lg py-2 px-2.5 inline-flex items-center justify-center bg-white border-gray-200 border">
|
|
26
|
+
<span id="default-message" class="inline-flex items-center">
|
|
27
|
+
<svg class="w-3 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 20">
|
|
28
|
+
<path d="M16 1h-3.278A1.992 1.992 0 0 0 11 0H7a1.993 1.993 0 0 0-1.722 1H2a2 2 0 0 0-2 2v15a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2Zm-3 14H5a1 1 0 0 1 0-2h8a1 1 0 0 1 0 2Zm0-4H5a1 1 0 0 1 0-2h8a1 1 0 1 1 0 2Zm0-5H5a1 1 0 0 1 0-2h2V2h4v2h2a1 1 0 1 1 0 2Z"/>
|
|
29
|
+
</svg>
|
|
30
|
+
</span>
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="my-4 w-full flex justify-center" ref="otpRoot">
|
|
37
35
|
<v-otp-input
|
|
38
36
|
ref="code"
|
|
39
37
|
container-class="grid grid-cols-6 gap-3 w-full"
|
|
40
|
-
input-classes="otp-input bg-gray-50 text-center border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block h-[
|
|
38
|
+
input-classes="otp-input bg-gray-50 text-center border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block h-[46px] w-full dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
41
39
|
:num-inputs="6"
|
|
42
40
|
inputType="number"
|
|
43
41
|
inputmode="numeric"
|
|
@@ -48,7 +46,7 @@
|
|
|
48
46
|
/>
|
|
49
47
|
</div>
|
|
50
48
|
<!-- <Vue2FACodeInput v-model="code" autofocus /> -->
|
|
51
|
-
<div class="flex flex-row gap-2.5
|
|
49
|
+
<div class="flex flex-row gap-2.5 h-12">
|
|
52
50
|
<LinkButton to="/login" class="w-full">
|
|
53
51
|
{{$t('Back to login')}}
|
|
54
52
|
</LinkButton>
|
|
@@ -220,8 +218,9 @@ const handleSkip = async () => {
|
|
|
220
218
|
}
|
|
221
219
|
}
|
|
222
220
|
</script>
|
|
223
|
-
<style>
|
|
224
|
-
.otp-input {
|
|
225
|
-
|
|
226
|
-
|
|
221
|
+
<style scoped>
|
|
222
|
+
:deep(.otp-input-container) {
|
|
223
|
+
display: flex;
|
|
224
|
+
gap: 0.75rem;
|
|
225
|
+
}
|
|
227
226
|
</style>
|
package/dist/index.js
CHANGED
|
@@ -83,6 +83,9 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
83
83
|
const userName = adminUser.dbUser[adminforth.config.auth.usernameField];
|
|
84
84
|
const brandName = adminforth.config.customization.brandName;
|
|
85
85
|
const brandNameSlug = adminforth.config.customization.brandNameSlug;
|
|
86
|
+
const issuerName = (this.options.customBrendPrefix && this.options.customBrendPrefix.trim())
|
|
87
|
+
? this.options.customBrendPrefix.trim()
|
|
88
|
+
: brandName;
|
|
86
89
|
const authResource = adminforth.config.resources.find((res) => res.resourceId === adminforth.config.auth.usersResourceId);
|
|
87
90
|
const authPk = authResource.columns.find((col) => col.primaryKey).name;
|
|
88
91
|
const userPk = adminUser.dbUser[authPk];
|
|
@@ -95,11 +98,11 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
95
98
|
}
|
|
96
99
|
const userCanSkipSetup = this.options.usersFilterToAllowSkipSetup ? this.options.usersFilterToAllowSkipSetup(adminUser) : false;
|
|
97
100
|
if (!secret) {
|
|
98
|
-
const tempSecret = twofactor.generateSecret({ name:
|
|
101
|
+
const tempSecret = twofactor.generateSecret({ name: issuerName, account: userName });
|
|
99
102
|
newSecret = tempSecret.secret;
|
|
100
103
|
}
|
|
101
104
|
else {
|
|
102
|
-
const value = this.adminforth.auth.issueJWT({ userName, issuer:
|
|
105
|
+
const value = this.adminforth.auth.issueJWT({ userName, issuer: issuerName, pk: userPk, userCanSkipSetup, rememberMeDays }, 'tempTotp', '2h');
|
|
103
106
|
response.setHeader('Set-Cookie', `adminforth_${brandNameSlug}_totpTemporaryJWT=${value}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; max-age=3600; `);
|
|
104
107
|
return {
|
|
105
108
|
body: {
|
|
@@ -109,7 +112,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
109
112
|
ok: true
|
|
110
113
|
};
|
|
111
114
|
}
|
|
112
|
-
const totpTemporaryJWT = this.adminforth.auth.issueJWT({ userName, newSecret, issuer:
|
|
115
|
+
const totpTemporaryJWT = this.adminforth.auth.issueJWT({ userName, newSecret, issuer: issuerName, pk: userPk, userCanSkipSetup, rememberMeDays }, 'tempTotp', '2h');
|
|
113
116
|
response.setHeader('Set-Cookie', `adminforth_${brandNameSlug}_totpTemporaryJWT=${totpTemporaryJWT}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=${new Date(Date.now() + '1h').toUTCString()} `);
|
|
114
117
|
return {
|
|
115
118
|
body: {
|
package/index.ts
CHANGED
|
@@ -93,6 +93,9 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
93
93
|
const userName = adminUser.dbUser[adminforth.config.auth.usernameField]
|
|
94
94
|
const brandName = adminforth.config.customization.brandName;
|
|
95
95
|
const brandNameSlug = adminforth.config.customization.brandNameSlug;
|
|
96
|
+
const issuerName = (this.options.customBrendPrefix && this.options.customBrendPrefix.trim())
|
|
97
|
+
? this.options.customBrendPrefix.trim()
|
|
98
|
+
: brandName;
|
|
96
99
|
const authResource = adminforth.config.resources.find((res)=>res.resourceId === adminforth.config.auth.usersResourceId )
|
|
97
100
|
const authPk = authResource.columns.find((col)=>col.primaryKey).name
|
|
98
101
|
const userPk = adminUser.dbUser[authPk]
|
|
@@ -108,10 +111,10 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
108
111
|
const userCanSkipSetup = this.options.usersFilterToAllowSkipSetup ? this.options.usersFilterToAllowSkipSetup(adminUser) : false;
|
|
109
112
|
|
|
110
113
|
if (!secret){
|
|
111
|
-
const tempSecret = twofactor.generateSecret({name:
|
|
114
|
+
const tempSecret = twofactor.generateSecret({name: issuerName,account: userName})
|
|
112
115
|
newSecret = tempSecret.secret
|
|
113
116
|
} else {
|
|
114
|
-
const value = this.adminforth.auth.issueJWT({userName, issuer:
|
|
117
|
+
const value = this.adminforth.auth.issueJWT({userName, issuer:issuerName, pk:userPk, userCanSkipSetup, rememberMeDays }, 'tempTotp', '2h');
|
|
115
118
|
response.setHeader('Set-Cookie', `adminforth_${brandNameSlug}_totpTemporaryJWT=${value}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; max-age=3600; `);
|
|
116
119
|
|
|
117
120
|
return {
|
|
@@ -122,7 +125,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
122
125
|
ok: true
|
|
123
126
|
}
|
|
124
127
|
}
|
|
125
|
-
const totpTemporaryJWT = this.adminforth.auth.issueJWT({userName, newSecret, issuer:
|
|
128
|
+
const totpTemporaryJWT = this.adminforth.auth.issueJWT({userName, newSecret, issuer:issuerName, pk:userPk, userCanSkipSetup, rememberMeDays }, 'tempTotp', '2h');
|
|
126
129
|
response.setHeader('Set-Cookie', `adminforth_${brandNameSlug}_totpTemporaryJWT=${totpTemporaryJWT}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=${new Date(Date.now() + '1h').toUTCString() } `);
|
|
127
130
|
|
|
128
131
|
return {
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -16,6 +16,8 @@ export type PluginOptions = {
|
|
|
16
16
|
*/
|
|
17
17
|
timeStepWindow?: number;
|
|
18
18
|
|
|
19
|
+
customBrendPrefix?: string;
|
|
20
|
+
|
|
19
21
|
/**
|
|
20
22
|
* Optional function to filter users to apply 2FA.
|
|
21
23
|
* Should return true if 2FA should be applied to the user and false if AdminForth should not challenge the user with 2FA.
|