Skip to content

Commit faaeb44

Browse files
committed
docs: enhance Two-Factor Authentication plugin documentation with detailed usage instructions and new features
1 parent 21d2e56 commit faaeb44

File tree

1 file changed

+73
-26
lines changed

1 file changed

+73
-26
lines changed

adminforth/documentation/docs/tutorial/07-Plugins/02-TwoFactorsAuth.md

Lines changed: 73 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,23 @@
22

33
The Two-Factor Authentication Plugin provides an additional layer of security to the application by requiring users to provide a second form of authentication in addition to their password. This plugin supports authenticator apps.
44

5+
Plugin supports next 2FA methods:
6+
- TOTP (Time-based One-Time Password) via authenticator apps (Google Authenticator, Authy, Microsoft Authenticator, etc)
7+
- Upgrade to Passkeys (WebAuthn) alongside TOTP (with TOTP as alternative method)
8+
9+
Also it supports both:
10+
11+
- Multi-Factor Authentication (MFA): asking for 2FA on every login (or single WebAuthn passkey login)
12+
- Step-Up MFA: asking for 2FA again on critical operations (custom actions, secure save etc)
13+
14+
515
## Installation
616

717
``` bash
818
npm i @adminforth/two-factors-auth --save
919
```
1020

11-
Plugin is already installed into adminforth, to import:
21+
To import:
1222

1323
```ts title="/adminuser.ts"
1424
import TwoFactorsAuthPlugin from '@adminforth/two-factors-auth';
@@ -78,8 +88,7 @@ Thats it! Two-Factor Authentication is now enabled:
7888
7989
## Disabling Two-Factor Authentication locally
8090
81-
If it is not convenient to enter the code every time you log in during local development, you can disable Two-Factor Authentication
82-
for the dev environment using `usersFilterToApply` option.
91+
If it is not convenient to enter the code every time during local development, you can disable Two-Factor Authentication for the dev environment using `usersFilterToApply` option:
8392
8493
```ts title='./index.ts'
8594

@@ -104,6 +113,8 @@ for the dev environment using `usersFilterToApply` option.
104113
],
105114
```
106115
116+
> Note: you can enable it temporarey while testing two-factor authentication flow locally and then disable.
117+
107118
## Select which users should use Two-Factor Authentication
108119
109120
By default plugin enforces Two-Factor Authentication for all users.
@@ -117,6 +128,10 @@ If you wish to enforce 2FA only for specific users, you can again use `usersFilt
117128
},
118129
```
119130
131+
Other users (for whom this function returns false) will not be even suggested to setup 2FA and it will be not requested for them, nor on login, nor on step-up MFA requests (such requests will be automatically passed for them without any popups)
132+
133+
### Control 2FA per-user via DB field
134+
120135
You can even add a boolean column to the user table to store whether the user should use 2FA or not:
121136
122137
In `schema.prisma`:
@@ -129,7 +144,7 @@ model adminuser {
129144
role String
130145
password_hash String
131146
secret2fa String?
132-
//diff-add
147+
//diff-add
133148
use2fa Boolean? @default(false)
134149
}
135150
```
@@ -184,30 +199,35 @@ Then in `adminuser.ts`:
184199
185200
## Allow Specific Users to Skip Two-Factor Authentication Setup
186201
187-
By default, all users are required to setup Two-Factor Authentication if it is enabled.
202+
By default, all users are required to setup Two-Factor Authentication if it is enabled (enforced).
188203
189-
If you want to allow specific users to **skip** the 2FA setup, you can use the `usersFilterToAllowSkipSetup` option:
204+
If you want to allow specific users to **skip** the 2FA setup (but still suggest it as optional possibility), you can use the `usersFilterToAllowSkipSetup` option:
190205
191206
```ts title='./adminuser.ts'
192207
...
193208
plugins: [
194-
new TwoFactorsAuthPlugin ({
195-
twoFaSecretFieldName: 'secret2fa',
196-
...
197-
//diff-add
198-
usersFilterToAllowSkipSetup: (adminUser: AdminUser) => {
199-
//diff-add
200-
// allow skip setup 2FA for users which email is 'adminforth' or 'adminguest'
201-
//diff-add
202-
return (['adminforth', 'adminguest'].includes(adminUser.dbUser.email));
203-
//diff-add
204-
},
205-
}),
209+
new TwoFactorsAuthPlugin ({
210+
twoFaSecretFieldName: 'secret2fa',
211+
...
212+
//diff-add
213+
usersFilterToAllowSkipSetup: (adminUser: AdminUser) => {
214+
//diff-add
215+
// allow skip setup 2FA for users which email is 'adminforth' or 'adminguest'
216+
//diff-add
217+
return (['adminforth', 'adminguest'].includes(adminUser.dbUser.email));
218+
//diff-add
219+
},
220+
}),
206221
],
207222
...
208223
```
209224
210-
## Request 2FA on custom Actions
225+
So such users will have suggestion to setup 2FA, but will be able to skip it with "Skip for now" button.
226+
227+
228+
## Step-Up MFA (Two-Factor re-authentication on critical operations)
229+
230+
### Request 2FA on custom Actions
211231
212232
You might want to to allow to call some custom critical/money related actions with additional 2FA approval. This eliminates risks caused by user cookies theft by some virous/doorway software after login.
213233
@@ -225,15 +245,18 @@ To do it, first, create frontend custom component which wraps and intercepts cli
225245
const props = defineProps<{ disabled?: boolean; meta?: Record<string, any> }>();
226246

227247
async function onClick() {
228-
if (props.disabled) return;
248+
if (props.disabled) {
249+
return;
250+
}
229251

230-
const verificationResult = await window.adminforthTwoFaModal.get2FaConfirmationResult(); // this will ask user to enter code
252+
const verificationResult = await window.adminforthTwoFaModal.get2FaConfirmationResult(); // this will ask user to enter code
253+
231254
emit('callAction', { verificationResult }); // then we pass this verification result to action (from fronted to backend)
232255
}
233256
</script>
234257
```
235258
236-
Now we need to use verification result which we got from user on frontend, inside of backend action handler and verify that it is valid (and not expired):
259+
Now we need to use verification result which we got from user on frontend, inside of backend action handler and verify that it is valid (and not expired):
237260
238261
```ts title='/adminuser.ts'
239262
options: {
@@ -303,7 +326,7 @@ options: {
303326
}
304327
```
305328
306-
## Request 2FA for create/edit (secure save gating)
329+
### Request 2FA for create/edit (secure save gating)
307330
308331
To protect create and edit operations, collect the result of the 2FA modal on the frontend and send it along with the save payload. The server must verify it before writing changes.
309332
@@ -371,7 +394,7 @@ This approach ensures 2FA cannot be bypassed by calling the API directly:
371394
- The client collects verification via the modal and forwards it under `meta.confirmationResult`.
372395
- The server validates it in `beforeSave` with access to `extra.cookies` and the `adminUser`.
373396
374-
## Request 2FA from custom components
397+
### Request 2FA from custom components
375398
376399
Imagine you have some button which does some API call
377400
@@ -451,7 +474,7 @@ async function callAdminAPI() {
451474

452475
```
453476
454-
And oin API call we need to verify it:
477+
And on API call we need to verify it:
455478
456479
457480
```ts
@@ -506,6 +529,30 @@ app.post(`${ADMIN_BASE_URL}/myCriticalAction`,
506529
);
507530
```
508531
532+
### Step-Up Authentication Grace Period
533+
534+
> 💡** Note ** this feature is now in development and might be not yet available.
535+
536+
By default, step-up authentication is required every time the user performs a critical operation.
537+
538+
While it might be nessesary for high-security applications, it can be inconvenient for users who perform multiple critical actions in a short period. To fix the issue (by lowering security a bit), you can enable grace period between step-up authentication requests:
539+
540+
541+
```ts title='./adminuser.ts'
542+
new TwoFactorsAuthPlugin ({
543+
twoFaSecretFieldName: 'secret2fa',
544+
...
545+
//diff-add
546+
stepUpMfaGracePeriodSeconds: 300, // 5 minutes grace period
547+
...
548+
}),
549+
```
550+
551+
This configuration still remembers user browser fingerprint and IP address, and if at least one of them changes, it will ask for 2FA again, ignoring grace period.
552+
553+
554+
Any popups asking for 2FA would be automatically resolved during grace period without user interaction if both browser fingerprint and IP address are the same as during last successful 2FA and time since last 2FA is less than grace period.
555+
509556
510557
## Custom label prefix in authenticator app
511558
@@ -522,7 +569,7 @@ If you want to have custom label prefix for some reason:
522569
],
523570
```
524571
525-
## Passkeys setup
572+
## Passkeys setup (WebAuthn) alongside TOTP
526573
527574
If you want to use both passkeys and TOTP simultaneously, you can set them up as follows:
528575

0 commit comments

Comments
 (0)