@adminforth/two-factors-auth 1.0.12 → 1.0.15
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.
|
@@ -70,7 +70,6 @@
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
|
|
74
73
|
const router = useRouter();
|
|
75
74
|
const inProgress = ref(false);
|
|
76
75
|
|
|
@@ -143,10 +142,10 @@
|
|
|
143
142
|
}
|
|
144
143
|
})
|
|
145
144
|
if (resp.allowedLogin){
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
145
|
+
await user.finishLogin();
|
|
146
|
+
} else {
|
|
147
|
+
showErrorTost('Invalid code');
|
|
150
148
|
}
|
|
149
|
+
}
|
|
151
150
|
</script>
|
|
152
151
|
|
|
@@ -89,7 +89,6 @@ import { useUserStore } from '@/stores/user';
|
|
|
89
89
|
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
90
90
|
import { callAdminForthApi, loadFile } from '@/utils';
|
|
91
91
|
import { useRouter } from 'vue-router';
|
|
92
|
-
import { initFlowbite } from 'flowbite'
|
|
93
92
|
import { showErrorTost } from '@/composables/useFrontendApi';
|
|
94
93
|
|
|
95
94
|
|
|
@@ -180,9 +179,9 @@ async function sendCode () {
|
|
|
180
179
|
}
|
|
181
180
|
})
|
|
182
181
|
if (resp.allowedLogin){
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
182
|
+
await user.finishLogin()
|
|
183
|
+
} else {
|
|
184
|
+
showErrorTost('Invalid code');
|
|
187
185
|
}
|
|
186
|
+
}
|
|
188
187
|
</script>
|
|
@@ -70,7 +70,6 @@
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
|
|
74
73
|
const router = useRouter();
|
|
75
74
|
const inProgress = ref(false);
|
|
76
75
|
|
|
@@ -143,10 +142,10 @@
|
|
|
143
142
|
}
|
|
144
143
|
})
|
|
145
144
|
if (resp.allowedLogin){
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
145
|
+
await user.finishLogin();
|
|
146
|
+
} else {
|
|
147
|
+
showErrorTost('Invalid code');
|
|
150
148
|
}
|
|
149
|
+
}
|
|
151
150
|
</script>
|
|
152
151
|
|
|
@@ -89,7 +89,6 @@ import { useUserStore } from '@/stores/user';
|
|
|
89
89
|
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
90
90
|
import { callAdminForthApi, loadFile } from '@/utils';
|
|
91
91
|
import { useRouter } from 'vue-router';
|
|
92
|
-
import { initFlowbite } from 'flowbite'
|
|
93
92
|
import { showErrorTost } from '@/composables/useFrontendApi';
|
|
94
93
|
|
|
95
94
|
|
|
@@ -180,9 +179,9 @@ async function sendCode () {
|
|
|
180
179
|
}
|
|
181
180
|
})
|
|
182
181
|
if (resp.allowedLogin){
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
182
|
+
await user.finishLogin()
|
|
183
|
+
} else {
|
|
184
|
+
showErrorTost('Invalid code');
|
|
187
185
|
}
|
|
186
|
+
}
|
|
188
187
|
</script>
|
package/dist/index.js
CHANGED
|
@@ -40,11 +40,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
40
40
|
if (!this.authResource.columns.some((col) => col.name === this.options.twoFaSecretFieldName)) {
|
|
41
41
|
throw new Error(`Column ${this.options.twoFaSecretFieldName} not found in ${this.authResource.label}`);
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
if (!beforeLoginConfirmation.beforeLoginConfirmation) {
|
|
45
|
-
beforeLoginConfirmation.beforeLoginConfirmation = [];
|
|
46
|
-
}
|
|
47
|
-
beforeLoginConfirmation.beforeLoginConfirmation.push((_a) => __awaiter(this, [_a], void 0, function* ({ adminUser }) {
|
|
43
|
+
this.adminforth.config.auth.beforeLoginConfirmation.push((_a) => __awaiter(this, [_a], void 0, function* ({ adminUser, response }) {
|
|
48
44
|
if (adminUser.isRoot) {
|
|
49
45
|
return { body: { loginAllowed: true }, ok: true };
|
|
50
46
|
}
|
|
@@ -60,31 +56,22 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
60
56
|
newSecret = tempSecret.secret;
|
|
61
57
|
}
|
|
62
58
|
else {
|
|
59
|
+
const value = this.adminforth.auth.issueJWT({ userName, issuer: brandName, pk: userPk }, 'tempTotp');
|
|
60
|
+
response.setHeader('Set-Cookie', `adminforth_totpTemporaryJWT=${value}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; max-age=3600; `);
|
|
63
61
|
return {
|
|
64
62
|
body: {
|
|
65
63
|
loginAllowed: false,
|
|
66
64
|
redirectTo: '/confirm2fa',
|
|
67
|
-
setCookie: [{
|
|
68
|
-
name: 'totpTemporaryJWT',
|
|
69
|
-
value: this.adminforth.auth.issueJWT({ userName, issuer: brandName, pk: userPk }, 'tempTotp'),
|
|
70
|
-
expiry: '1h',
|
|
71
|
-
httpOnly: true
|
|
72
|
-
},]
|
|
73
65
|
},
|
|
74
66
|
ok: true
|
|
75
67
|
};
|
|
76
68
|
}
|
|
77
69
|
const totpTemporaryJWT = this.adminforth.auth.issueJWT({ userName, newSecret, issuer: brandName, pk: userPk }, 'tempTotp');
|
|
70
|
+
response.setHeader('Set-Cookie', `adminforth_totpTemporaryJWT=${totpTemporaryJWT}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=${new Date(Date.now() + '1h').toUTCString()} `);
|
|
78
71
|
return {
|
|
79
72
|
body: {
|
|
80
73
|
loginAllowed: false,
|
|
81
74
|
redirectTo: secret ? '/confirm2fa' : '/setup2fa',
|
|
82
|
-
setCookie: [{
|
|
83
|
-
name: 'totpTemporaryJWT',
|
|
84
|
-
value: totpTemporaryJWT,
|
|
85
|
-
expiry: '1h',
|
|
86
|
-
httpOnly: true
|
|
87
|
-
},]
|
|
88
75
|
},
|
|
89
76
|
ok: true
|
|
90
77
|
};
|
|
@@ -96,8 +83,9 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
96
83
|
path: `/plugin/twofa/initSetup`,
|
|
97
84
|
noAuth: true,
|
|
98
85
|
handler: (server) => __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
var _a;
|
|
99
87
|
const toReturn = { totpJWT: null, status: 'ok', };
|
|
100
|
-
const totpTemporaryJWT = server.cookies
|
|
88
|
+
const totpTemporaryJWT = (_a = server.cookies.find((cookie) => cookie.key === 'adminforth_totpTemporaryJWT')) === null || _a === void 0 ? void 0 : _a.value;
|
|
101
89
|
if (totpTemporaryJWT) {
|
|
102
90
|
toReturn.totpJWT = totpTemporaryJWT;
|
|
103
91
|
}
|
|
@@ -108,8 +96,9 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
108
96
|
method: 'POST',
|
|
109
97
|
path: `/plugin/twofa/confirmSetup`,
|
|
110
98
|
noAuth: true,
|
|
111
|
-
handler: (
|
|
112
|
-
|
|
99
|
+
handler: (_b) => __awaiter(this, [_b], void 0, function* ({ body, adminUser, response, cookies }) {
|
|
100
|
+
var _c;
|
|
101
|
+
const totpTemporaryJWT = (_c = cookies.find((cookie) => cookie.key === 'adminforth_totpTemporaryJWT')) === null || _c === void 0 ? void 0 : _c.value;
|
|
113
102
|
const decoded = yield this.adminforth.auth.verify(totpTemporaryJWT, 'tempTotp');
|
|
114
103
|
if (!decoded) {
|
|
115
104
|
return { status: 'error', message: 'Invalid token' };
|
|
@@ -120,8 +109,8 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
120
109
|
this.connectors = this.adminforth.connectors;
|
|
121
110
|
const connector = this.connectors[this.authResource.dataSource];
|
|
122
111
|
yield connector.updateRecord({ resource: this.authResource, recordId: decoded.pk, newValues: { [this.options.twoFaSecretFieldName]: decoded.newSecret } });
|
|
123
|
-
this.
|
|
124
|
-
this.
|
|
112
|
+
this.adminforth.auth.removeCustomCookie({ response, name: 'totpTemporaryJWT' });
|
|
113
|
+
this.adminforth.auth.setAuthCookie({ response, username: decoded.userName, pk: decoded.pk });
|
|
125
114
|
return { status: 'ok', allowedLogin: true };
|
|
126
115
|
}
|
|
127
116
|
else {
|
|
@@ -135,8 +124,8 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
135
124
|
const user = yield connector.getRecordByPrimaryKey(this.authResource, decoded.pk);
|
|
136
125
|
const verified = twofactor.verifyToken(user[this.options.twoFaSecretFieldName], body.code);
|
|
137
126
|
if (verified) {
|
|
138
|
-
this.
|
|
139
|
-
this.
|
|
127
|
+
this.adminforth.auth.removeCustomCookie({ response, name: 'totpTemporaryJWT' });
|
|
128
|
+
this.adminforth.auth.setAuthCookie({ response, username: decoded.userName, pk: decoded.pk });
|
|
140
129
|
return { status: 'ok', allowedLogin: true };
|
|
141
130
|
}
|
|
142
131
|
else {
|
package/index.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { IAdminForth, IHttpServer, IAdminForthAuth } from "adminforth
|
|
3
|
-
import { AdminForthPlugin } from "adminforth";
|
|
1
|
+
import { AdminForthPlugin } from "adminforth";
|
|
2
|
+
import type { AdminForthResource, AdminUser, IAdminForth, IHttpServer, IAdminForthAuth, BeforeLoginConfirmationFunction, IAdminForthHttpResponse } from "adminforth";
|
|
4
3
|
import twofactor from 'node-2fa';
|
|
5
4
|
import { PluginOptions } from "./types.js"
|
|
6
5
|
|
|
7
|
-
|
|
8
6
|
export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
9
7
|
options: PluginOptions;
|
|
10
8
|
adminforth: IAdminForth;
|
|
@@ -44,54 +42,46 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
44
42
|
if(!this.authResource.columns.some((col)=>col.name === this.options.twoFaSecretFieldName)){
|
|
45
43
|
throw new Error(`Column ${this.options.twoFaSecretFieldName} not found in ${this.authResource.label}`)
|
|
46
44
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
45
|
+
(this.adminforth.config.auth.beforeLoginConfirmation as BeforeLoginConfirmationFunction[]).push(
|
|
46
|
+
async({ adminUser, response }: { adminUser: AdminUser, response: IAdminForthHttpResponse} )=> {
|
|
47
|
+
if(adminUser.isRoot){
|
|
48
|
+
return { body: {loginAllowed: true}, ok: true}
|
|
49
|
+
}
|
|
50
|
+
const secret = adminUser.dbUser[this.options.twoFaSecretFieldName]
|
|
51
|
+
const userName = adminUser.dbUser[adminforth.config.auth.usernameField]
|
|
52
|
+
const brandName = adminforth.config.customization.brandName
|
|
53
|
+
const authResource = adminforth.config.resources.find((res)=>res.resourceId === adminforth.config.auth.resourceId )
|
|
54
|
+
const authPk = authResource.columns.find((col)=>col.primaryKey).name
|
|
55
|
+
const userPk = adminUser.dbUser[authPk]
|
|
56
|
+
let newSecret = null
|
|
57
|
+
if (!secret){
|
|
58
|
+
const tempSecret = twofactor.generateSecret({name: brandName,account: userName})
|
|
59
|
+
newSecret = tempSecret.secret
|
|
60
|
+
} else {
|
|
61
|
+
const value = this.adminforth.auth.issueJWT({userName, issuer:brandName, pk:userPk },'tempTotp');
|
|
62
|
+
response.setHeader('Set-Cookie', `adminforth_totpTemporaryJWT=${value}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; max-age=3600; `);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
body:{
|
|
66
|
+
loginAllowed: false,
|
|
67
|
+
redirectTo: '/confirm2fa',
|
|
68
|
+
|
|
69
|
+
},
|
|
70
|
+
ok: true
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const totpTemporaryJWT = this.adminforth.auth.issueJWT({userName, newSecret, issuer:brandName, pk:userPk },'tempTotp', )
|
|
75
|
+
response.setHeader('Set-Cookie', `adminforth_totpTemporaryJWT=${totpTemporaryJWT}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=${new Date(Date.now() + '1h').toUTCString() } `);
|
|
76
|
+
|
|
77
|
+
return {
|
|
67
78
|
body:{
|
|
68
79
|
loginAllowed: false,
|
|
69
|
-
redirectTo: '/confirm2fa',
|
|
70
|
-
setCookie: [{
|
|
71
|
-
name: 'totpTemporaryJWT',
|
|
72
|
-
value: this.adminforth.auth.issueJWT({userName, issuer:brandName, pk:userPk },'tempTotp', ),
|
|
73
|
-
expiry: '1h',
|
|
74
|
-
httpOnly: true
|
|
75
|
-
},]
|
|
80
|
+
redirectTo: secret ? '/confirm2fa' : '/setup2fa',
|
|
76
81
|
},
|
|
77
82
|
ok: true
|
|
78
|
-
|
|
79
83
|
}
|
|
80
|
-
}
|
|
81
|
-
const totpTemporaryJWT = this.adminforth.auth.issueJWT({userName, newSecret, issuer:brandName, pk:userPk },'tempTotp', )
|
|
82
|
-
return {
|
|
83
|
-
body:{
|
|
84
|
-
loginAllowed: false,
|
|
85
|
-
redirectTo: secret ? '/confirm2fa' : '/setup2fa',
|
|
86
|
-
setCookie: [{
|
|
87
|
-
name: 'totpTemporaryJWT',
|
|
88
|
-
value: totpTemporaryJWT,
|
|
89
|
-
expiry: '1h',
|
|
90
|
-
httpOnly: true
|
|
91
|
-
},]},
|
|
92
|
-
ok: true
|
|
93
|
-
}
|
|
94
|
-
})
|
|
84
|
+
})
|
|
95
85
|
}
|
|
96
86
|
|
|
97
87
|
setupEndpoints(server: IHttpServer): void {
|
|
@@ -101,7 +91,8 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
101
91
|
noAuth: true,
|
|
102
92
|
handler: async (server) => {
|
|
103
93
|
const toReturn = {totpJWT:null,status:'ok',}
|
|
104
|
-
|
|
94
|
+
|
|
95
|
+
const totpTemporaryJWT = server.cookies.find((cookie)=>cookie.key === 'adminforth_totpTemporaryJWT')?.value;
|
|
105
96
|
if (totpTemporaryJWT){
|
|
106
97
|
toReturn.totpJWT = totpTemporaryJWT
|
|
107
98
|
}
|
|
@@ -113,7 +104,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
113
104
|
path: `/plugin/twofa/confirmSetup`,
|
|
114
105
|
noAuth: true,
|
|
115
106
|
handler: async ({ body, adminUser, response, cookies }) => {
|
|
116
|
-
const totpTemporaryJWT = cookies
|
|
107
|
+
const totpTemporaryJWT = cookies.find((cookie)=>cookie.key === 'adminforth_totpTemporaryJWT')?.value;
|
|
117
108
|
const decoded = await this.adminforth.auth.verify(totpTemporaryJWT, 'tempTotp');
|
|
118
109
|
if ( !decoded ) {
|
|
119
110
|
return {status:'error',message:'Invalid token'}
|
|
@@ -124,9 +115,9 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
124
115
|
this.connectors = this.adminforth.connectors
|
|
125
116
|
const connector = this.connectors[this.authResource.dataSource];
|
|
126
117
|
await connector.updateRecord({resource:this.authResource, recordId:decoded.pk, newValues:{[this.options.twoFaSecretFieldName]: decoded.newSecret}})
|
|
127
|
-
this.
|
|
128
|
-
this.
|
|
129
|
-
return { status: 'ok',allowedLogin: true }
|
|
118
|
+
this.adminforth.auth.removeCustomCookie({response, name:'totpTemporaryJWT'})
|
|
119
|
+
this.adminforth.auth.setAuthCookie({response, username:decoded.userName, pk:decoded.pk})
|
|
120
|
+
return { status: 'ok', allowedLogin: true }
|
|
130
121
|
} else {
|
|
131
122
|
return {error: 'Wrong or expired OTP code'}
|
|
132
123
|
}
|
|
@@ -137,9 +128,9 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
137
128
|
const user = await connector.getRecordByPrimaryKey(this.authResource, decoded.pk)
|
|
138
129
|
const verified = twofactor.verifyToken(user[this.options.twoFaSecretFieldName], body.code);
|
|
139
130
|
if (verified) {
|
|
140
|
-
this.
|
|
141
|
-
this.
|
|
142
|
-
return { status: 'ok',allowedLogin: true }
|
|
131
|
+
this.adminforth.auth.removeCustomCookie({response, name:'totpTemporaryJWT'})
|
|
132
|
+
this.adminforth.auth.setAuthCookie({response, username:decoded.userName, pk:decoded.pk})
|
|
133
|
+
return { status: 'ok', allowedLogin: true }
|
|
143
134
|
} else {
|
|
144
135
|
return {error: 'Wrong or expired OTP code'}
|
|
145
136
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adminforth/two-factors-auth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "AdminForth plugin for two factors authentication",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"rollout": "tsc && cp -
|
|
9
|
+
"rollout": "tsc && cp -rf custom dist/ && npm version patch && npm publish --access public"
|
|
10
10
|
},
|
|
11
11
|
"author": "devforth",
|
|
12
12
|
"license": "ISC",
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"adminforth": "^1.1.95",
|
|
15
14
|
"node-2fa": "^2.0.3"
|
|
16
15
|
}
|
|
17
16
|
}
|