@riao/authn-password 1.1.0 → 2.0.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/CHANGELOG.md +1 -1
- package/LICENSE.md +20 -20
- package/README.md +148 -148
- package/authentication-password.d.ts +9 -4
- package/authentication-password.js +76 -5
- package/authentication-password.js.map +1 -1
- package/migrations/001-create-passwords-table.js +11 -0
- package/migrations/001-create-passwords-table.js.map +1 -1
- package/migrations/002-add-password-authentication-protection.d.ts +5 -0
- package/migrations/002-add-password-authentication-protection.js +41 -0
- package/migrations/002-add-password-authentication-protection.js.map +1 -0
- package/package.json +4 -3
- package/password.d.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
# authn-password - Changelog
|
|
1
|
+
# authn-password - Changelog
|
package/LICENSE.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
Copyright (c) 2022 authn-password and others
|
|
2
|
-
|
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
-
a copy of this software and associated documentation files (the
|
|
5
|
-
"Software"), to deal in the Software without restriction, including
|
|
6
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
-
the following conditions:
|
|
10
|
-
|
|
11
|
-
The above copyright notice and this permission notice shall be
|
|
12
|
-
included in all copies or substantial portions of the Software.
|
|
13
|
-
|
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1
|
+
Copyright (c) 2022 authn-password and others
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
# @riao/authn-password
|
|
2
|
-
|
|
3
|
-
A TypeScript library that provides password-based authentication for the RIAO Identity and Access Management (IAM) framework. This driver implements secure password hashing and verification using bcrypt.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Secure Password Hashing**: Uses bcrypt for secure password hashing with configurable salt rounds
|
|
8
|
-
- **Principal Management**: Create and manage user principals with associated passwords
|
|
9
|
-
- **Authentication**: Authenticate users with login credentials and passwords
|
|
10
|
-
- **Flexible Hash Implementation**: Supports custom hash implementations via the `Hash` interface
|
|
11
|
-
- **Database Agnostic**: Works with any database supported by the RIAO DBAL (Database Abstraction Layer)
|
|
12
|
-
- **TypeScript Support**: Full TypeScript support with type safety
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
npm install @riao/authn-password @riao/iam @riao/dbal
|
|
18
|
-
npm install --save-dev @riao/cli
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Database Migrations
|
|
22
|
-
|
|
23
|
-
This driver requires the IAM password table to be created in your database. You can use the provided migrations:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npx riao migration:create import-iam-tables
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Then, add the `AuthenticationPasswordMigrations` to your migration runner:
|
|
30
|
-
|
|
31
|
-
`database/main/migrations/1234567890-import-iam-tables.ts`
|
|
32
|
-
```typescript
|
|
33
|
-
import { AuthenticationPasswordMigrations } from 'authn-password';
|
|
34
|
-
|
|
35
|
-
export default AuthenticationPasswordMigrations;
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Quick Start
|
|
39
|
-
|
|
40
|
-
### Basic Usage
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
import { PasswordAuthentication } from '@riao/authn-password';
|
|
44
|
-
import { Principal } from '@riao/iam/auth';
|
|
45
|
-
import { Database } from '@riao/dbal';
|
|
46
|
-
|
|
47
|
-
// Define your user interface
|
|
48
|
-
interface User extends Principal {
|
|
49
|
-
login: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Create an authentication service class
|
|
53
|
-
class UserAuthentication extends PasswordAuthentication<User> {}
|
|
54
|
-
|
|
55
|
-
// Initialize with your database
|
|
56
|
-
const db = new YourDatabase();
|
|
57
|
-
await db.init();
|
|
58
|
-
await db.connect();
|
|
59
|
-
|
|
60
|
-
const authService = new UserAuthentication({ db });
|
|
61
|
-
|
|
62
|
-
// Create a new user with a password
|
|
63
|
-
const userId = await authService.createPrincipal({
|
|
64
|
-
login: 'john.doe',
|
|
65
|
-
type: 'user',
|
|
66
|
-
name: 'John Doe',
|
|
67
|
-
password: 'securePassword123',
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// Change a password
|
|
71
|
-
await authService.changePassword(userId, 'newPassword123');
|
|
72
|
-
|
|
73
|
-
// Authenticate a user
|
|
74
|
-
const user = await authService.authenticate({
|
|
75
|
-
login: 'john.doe',
|
|
76
|
-
password: 'newPassword123',
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
if (user) {
|
|
80
|
-
console.log(`Welcome ${user.name}!`);
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
console.log('Authentication failed');
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## API Reference
|
|
88
|
-
|
|
89
|
-
### PasswordAuthentication<TPrincipal>
|
|
90
|
-
|
|
91
|
-
Abstract class that extends `Authentication<TPrincipal>` and adds password-based authentication.
|
|
92
|
-
|
|
93
|
-
#### Constructor Options
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
interface PasswordAuthenticationOptions extends AuthOptions {
|
|
97
|
-
hash?: Hash; // Optional custom hash implementation (defaults to bcrypt)
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
#### Methods
|
|
102
|
-
|
|
103
|
-
- **createPrincipal(principal)**: Creates a new principal with a hashed password
|
|
104
|
-
- Parameters: Principal data with `password` field
|
|
105
|
-
- Returns: Principal ID
|
|
106
|
-
|
|
107
|
-
- **authenticate(credentials)**: Authenticates a user with login and password
|
|
108
|
-
- Parameters: Object with `login` and `password` fields
|
|
109
|
-
- Returns: Principal object if successful, null if authentication fails
|
|
110
|
-
|
|
111
|
-
- **passwordsRepo**: Query repository for accessing password records directly
|
|
112
|
-
|
|
113
|
-
### Password Record Schema
|
|
114
|
-
|
|
115
|
-
Passwords are stored in the `iam_passwords` table:
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
interface Password {
|
|
119
|
-
id?: string;
|
|
120
|
-
principal_id: string; // Foreign key to principal
|
|
121
|
-
password_hash: string; // Bcrypt hashed password
|
|
122
|
-
deactivate_timestamp?: string;
|
|
123
|
-
create_timestamp?: string;
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Configuration
|
|
128
|
-
|
|
129
|
-
### Custom Hash Implementation
|
|
130
|
-
|
|
131
|
-
You can provide a custom hash implementation:
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
import { Hash } from '@riao/
|
|
135
|
-
|
|
136
|
-
class CustomHash extends Hash {
|
|
137
|
-
// Implement your custom hashing logic
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const authService = new UserAuthentication({
|
|
141
|
-
db,
|
|
142
|
-
hash: new CustomHash(),
|
|
143
|
-
});
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## Contributing & Development
|
|
147
|
-
|
|
148
|
-
See [contributing.md](docs/contributing/contributing.md) for information on how to develop or contribute to this project!
|
|
1
|
+
# @riao/authn-password
|
|
2
|
+
|
|
3
|
+
A TypeScript library that provides password-based authentication for the RIAO Identity and Access Management (IAM) framework. This driver implements secure password hashing and verification using bcrypt.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Secure Password Hashing**: Uses bcrypt for secure password hashing with configurable salt rounds
|
|
8
|
+
- **Principal Management**: Create and manage user principals with associated passwords
|
|
9
|
+
- **Authentication**: Authenticate users with login credentials and passwords
|
|
10
|
+
- **Flexible Hash Implementation**: Supports custom hash implementations via the `Hash` interface
|
|
11
|
+
- **Database Agnostic**: Works with any database supported by the RIAO DBAL (Database Abstraction Layer)
|
|
12
|
+
- **TypeScript Support**: Full TypeScript support with type safety
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @riao/authn-password @riao/iam @riao/dbal
|
|
18
|
+
npm install --save-dev @riao/cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Database Migrations
|
|
22
|
+
|
|
23
|
+
This driver requires the IAM password table to be created in your database. You can use the provided migrations:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx riao migration:create import-iam-tables
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then, add the `AuthenticationPasswordMigrations` to your migration runner:
|
|
30
|
+
|
|
31
|
+
`database/main/migrations/1234567890-import-iam-tables.ts`
|
|
32
|
+
```typescript
|
|
33
|
+
import { AuthenticationPasswordMigrations } from 'authn-password';
|
|
34
|
+
|
|
35
|
+
export default AuthenticationPasswordMigrations;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### Basic Usage
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { PasswordAuthentication } from '@riao/authn-password';
|
|
44
|
+
import { Principal } from '@riao/iam/auth';
|
|
45
|
+
import { Database } from '@riao/dbal';
|
|
46
|
+
|
|
47
|
+
// Define your user interface
|
|
48
|
+
interface User extends Principal {
|
|
49
|
+
login: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Create an authentication service class
|
|
53
|
+
class UserAuthentication extends PasswordAuthentication<User> {}
|
|
54
|
+
|
|
55
|
+
// Initialize with your database
|
|
56
|
+
const db = new YourDatabase();
|
|
57
|
+
await db.init();
|
|
58
|
+
await db.connect();
|
|
59
|
+
|
|
60
|
+
const authService = new UserAuthentication({ db });
|
|
61
|
+
|
|
62
|
+
// Create a new user with a password
|
|
63
|
+
const userId = await authService.createPrincipal({
|
|
64
|
+
login: 'john.doe',
|
|
65
|
+
type: 'user',
|
|
66
|
+
name: 'John Doe',
|
|
67
|
+
password: 'securePassword123',
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Change a password
|
|
71
|
+
await authService.changePassword(userId, 'newPassword123');
|
|
72
|
+
|
|
73
|
+
// Authenticate a user
|
|
74
|
+
const user = await authService.authenticate({
|
|
75
|
+
login: 'john.doe',
|
|
76
|
+
password: 'newPassword123',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (user) {
|
|
80
|
+
console.log(`Welcome ${user.name}!`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.log('Authentication failed');
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## API Reference
|
|
88
|
+
|
|
89
|
+
### PasswordAuthentication<TPrincipal>
|
|
90
|
+
|
|
91
|
+
Abstract class that extends `Authentication<TPrincipal>` and adds password-based authentication.
|
|
92
|
+
|
|
93
|
+
#### Constructor Options
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
interface PasswordAuthenticationOptions extends AuthOptions {
|
|
97
|
+
hash?: Hash; // Optional custom hash implementation (defaults to bcrypt)
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### Methods
|
|
102
|
+
|
|
103
|
+
- **createPrincipal(principal)**: Creates a new principal with a hashed password
|
|
104
|
+
- Parameters: Principal data with `password` field
|
|
105
|
+
- Returns: Principal ID
|
|
106
|
+
|
|
107
|
+
- **authenticate(credentials)**: Authenticates a user with login and password
|
|
108
|
+
- Parameters: Object with `login` and `password` fields
|
|
109
|
+
- Returns: Principal object if successful, null if authentication fails
|
|
110
|
+
|
|
111
|
+
- **passwordsRepo**: Query repository for accessing password records directly
|
|
112
|
+
|
|
113
|
+
### Password Record Schema
|
|
114
|
+
|
|
115
|
+
Passwords are stored in the `iam_passwords` table:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
interface Password {
|
|
119
|
+
id?: string;
|
|
120
|
+
principal_id: string; // Foreign key to principal
|
|
121
|
+
password_hash: string; // Bcrypt hashed password
|
|
122
|
+
deactivate_timestamp?: string;
|
|
123
|
+
create_timestamp?: string;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Configuration
|
|
128
|
+
|
|
129
|
+
### Custom Hash Implementation
|
|
130
|
+
|
|
131
|
+
You can provide a custom hash implementation:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { Hash } from '@riao/crypto';
|
|
135
|
+
|
|
136
|
+
class CustomHash extends Hash {
|
|
137
|
+
// Implement your custom hashing logic
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const authService = new UserAuthentication({
|
|
141
|
+
db,
|
|
142
|
+
hash: new CustomHash(),
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Contributing & Development
|
|
147
|
+
|
|
148
|
+
See [contributing.md](docs/contributing/contributing.md) for information on how to develop or contribute to this project!
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { DatabaseRecordId, QueryRepository } from '@riao/dbal';
|
|
2
|
-
import { Hash } from '@riao/
|
|
2
|
+
import { Hash } from '@riao/crypto';
|
|
3
3
|
import { Principal } from '@riao/iam/auth';
|
|
4
|
-
import { Authentication } from '@riao/iam/authentication';
|
|
5
|
-
import { AuthOptions } from '@riao/iam/auth/auth';
|
|
4
|
+
import { Authentication, AuthenticationOptions } from '@riao/iam/authentication';
|
|
6
5
|
import { Password } from './password';
|
|
7
|
-
export interface PasswordAuthenticationOptions extends
|
|
6
|
+
export interface PasswordAuthenticationOptions extends AuthenticationOptions {
|
|
8
7
|
hash?: Hash;
|
|
8
|
+
maxFailedAttempts?: number;
|
|
9
|
+
lockoutDurationMs?: number;
|
|
9
10
|
}
|
|
10
11
|
export declare abstract class PasswordAuthentication<TPrincipal extends Principal> extends Authentication<TPrincipal> {
|
|
11
12
|
passwordsRepo: QueryRepository<Password>;
|
|
12
13
|
protected hash: Hash;
|
|
14
|
+
protected readonly maxFailedAttempts: number;
|
|
15
|
+
protected readonly lockoutDurationMs: number;
|
|
13
16
|
constructor(options: PasswordAuthenticationOptions);
|
|
14
17
|
createPrincipal(principal: Omit<TPrincipal, 'id' | 'create_timestamp'> & {
|
|
15
18
|
password: string;
|
|
@@ -18,6 +21,8 @@ export declare abstract class PasswordAuthentication<TPrincipal extends Principa
|
|
|
18
21
|
authenticate(credentials: Partial<TPrincipal & {
|
|
19
22
|
password: string;
|
|
20
23
|
}>): Promise<TPrincipal | null>;
|
|
24
|
+
protected recordPasswordFailure(password: Password): Promise<void>;
|
|
25
|
+
protected resetPasswordFailures(passwordId: DatabaseRecordId): Promise<void>;
|
|
21
26
|
protected revokePasswords(principalId: DatabaseRecordId): Promise<void>;
|
|
22
27
|
protected insertPassword(principalId: DatabaseRecordId, hash: string): Promise<void>;
|
|
23
28
|
}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PasswordAuthentication = void 0;
|
|
4
|
-
const
|
|
4
|
+
const crypto_1 = require("@riao/crypto");
|
|
5
5
|
const authentication_1 = require("@riao/iam/authentication");
|
|
6
6
|
class PasswordAuthentication extends authentication_1.Authentication {
|
|
7
7
|
passwordsRepo;
|
|
8
8
|
hash;
|
|
9
|
+
maxFailedAttempts;
|
|
10
|
+
lockoutDurationMs;
|
|
9
11
|
constructor(options) {
|
|
10
12
|
super(options);
|
|
11
|
-
this.hash = options.hash ?? new
|
|
13
|
+
this.hash = options.hash ?? new crypto_1.Hash();
|
|
14
|
+
this.maxFailedAttempts = Math.max(1, options.maxFailedAttempts ?? 5);
|
|
15
|
+
this.lockoutDurationMs = options.lockoutDurationMs ?? 15 * 60 * 1000;
|
|
12
16
|
this.passwordsRepo = options.db.getQueryRepository({
|
|
13
17
|
table: 'iam_passwords',
|
|
14
18
|
identifiedBy: 'id',
|
|
@@ -22,13 +26,19 @@ class PasswordAuthentication extends authentication_1.Authentication {
|
|
|
22
26
|
return principalId;
|
|
23
27
|
}
|
|
24
28
|
async changePassword(principalId, newPassword) {
|
|
25
|
-
// eslint-disable-next-line no-console
|
|
26
|
-
console.log('Changing password for principal ID:', principalId, newPassword);
|
|
27
29
|
const hash = await this.hash.make(newPassword);
|
|
28
30
|
await this.revokePasswords(principalId);
|
|
29
31
|
await this.insertPassword(principalId, hash);
|
|
30
32
|
}
|
|
31
33
|
async authenticate(credentials) {
|
|
34
|
+
const attempt = {
|
|
35
|
+
scheme: 'password',
|
|
36
|
+
subject: String(credentials.login ?? ''),
|
|
37
|
+
};
|
|
38
|
+
const protection = await this.beforeAuthenticationAttempt(attempt);
|
|
39
|
+
if (!protection.allowed) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
32
42
|
const principal = await this.findActivePrincipal({
|
|
33
43
|
where: {
|
|
34
44
|
login: credentials.login,
|
|
@@ -44,8 +54,67 @@ class PasswordAuthentication extends authentication_1.Authentication {
|
|
|
44
54
|
if (!passwordRecord) {
|
|
45
55
|
return null;
|
|
46
56
|
}
|
|
57
|
+
if (passwordRecord.locked_until &&
|
|
58
|
+
passwordRecord.locked_until <= new Date()) {
|
|
59
|
+
await this.passwordsRepo.update({
|
|
60
|
+
set: {
|
|
61
|
+
failed_authentication_count: 0,
|
|
62
|
+
locked_until: null,
|
|
63
|
+
},
|
|
64
|
+
where: { id: passwordRecord.id },
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else if (passwordRecord.locked_until &&
|
|
68
|
+
passwordRecord.locked_until > new Date()) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
47
71
|
const isValid = await this.hash.check(credentials.password, passwordRecord?.password_hash);
|
|
48
|
-
|
|
72
|
+
if (!isValid) {
|
|
73
|
+
await this.recordAuthenticationFailure(attempt);
|
|
74
|
+
await this.recordPasswordFailure(passwordRecord);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
await this.recordAuthenticationSuccess(attempt);
|
|
78
|
+
await this.resetPasswordFailures(passwordRecord.id);
|
|
79
|
+
return principal;
|
|
80
|
+
}
|
|
81
|
+
async recordPasswordFailure(password) {
|
|
82
|
+
if (password.locked_until &&
|
|
83
|
+
password.locked_until <= new Date()) {
|
|
84
|
+
await this.passwordsRepo.update({
|
|
85
|
+
set: {
|
|
86
|
+
failed_authentication_count: 0,
|
|
87
|
+
locked_until: null,
|
|
88
|
+
},
|
|
89
|
+
where: { id: password.id },
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
await this.passwordsRepo.increment({
|
|
93
|
+
column: 'failed_authentication_count',
|
|
94
|
+
where: { id: password.id },
|
|
95
|
+
});
|
|
96
|
+
const updatedPassword = await this.passwordsRepo.findOne({
|
|
97
|
+
where: { id: password.id },
|
|
98
|
+
});
|
|
99
|
+
if (updatedPassword &&
|
|
100
|
+
(updatedPassword.failed_authentication_count ?? 0) >=
|
|
101
|
+
this.maxFailedAttempts) {
|
|
102
|
+
await this.passwordsRepo.update({
|
|
103
|
+
set: {
|
|
104
|
+
locked_until: new Date(Date.now() + this.lockoutDurationMs),
|
|
105
|
+
},
|
|
106
|
+
where: { id: password.id },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async resetPasswordFailures(passwordId) {
|
|
111
|
+
await this.passwordsRepo.update({
|
|
112
|
+
set: {
|
|
113
|
+
failed_authentication_count: 0,
|
|
114
|
+
locked_until: null,
|
|
115
|
+
},
|
|
116
|
+
where: { id: passwordId },
|
|
117
|
+
});
|
|
49
118
|
}
|
|
50
119
|
async revokePasswords(principalId) {
|
|
51
120
|
await this.passwordsRepo.update({
|
|
@@ -61,6 +130,8 @@ class PasswordAuthentication extends authentication_1.Authentication {
|
|
|
61
130
|
record: {
|
|
62
131
|
principal_id: principalId,
|
|
63
132
|
password_hash: hash,
|
|
133
|
+
failed_authentication_count: 0,
|
|
134
|
+
locked_until: null,
|
|
64
135
|
},
|
|
65
136
|
});
|
|
66
137
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authentication-password.js","sourceRoot":"","sources":["../../src/authentication-password.ts"],"names":[],"mappings":";;;AACA,
|
|
1
|
+
{"version":3,"file":"authentication-password.js","sourceRoot":"","sources":["../../src/authentication-password.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,6 +25,17 @@ class CreatePasswordsTable extends dbal_1.Migration {
|
|
|
25
25
|
type: dbal_1.ColumnType.TIMESTAMP,
|
|
26
26
|
required: false,
|
|
27
27
|
},
|
|
28
|
+
{
|
|
29
|
+
name: 'failed_authentication_count',
|
|
30
|
+
type: dbal_1.ColumnType.INT,
|
|
31
|
+
required: true,
|
|
32
|
+
default: 0,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'locked_until',
|
|
36
|
+
type: dbal_1.ColumnType.TIMESTAMP,
|
|
37
|
+
required: false,
|
|
38
|
+
},
|
|
28
39
|
],
|
|
29
40
|
});
|
|
30
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"001-create-passwords-table.js","sourceRoot":"","sources":["../../../src/migrations/001-create-passwords-table.ts"],"names":[],"mappings":";;;AAAA,wDAIgC;AAChC,qCAAmD;AAEnD,MAAa,oBAAqB,SAAQ,gBAAS;IACzC,KAAK,CAAC,EAAE;QAChB,MAAM,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC;YAC1B,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE;gBACR,2BAAa;gBACb;oBACC,IAAI,EAAE,cAAc;oBACpB,IAAI,EAAE,iBAAU,CAAC,IAAI;oBACrB,EAAE,EAAE;wBACH,eAAe,EAAE,gBAAgB;wBACjC,gBAAgB,EAAE,IAAI;wBACtB,QAAQ,EAAE,SAAS;qBACnB;iBACD;gBACD,EAAE,GAAG,4BAAc,EAAE,IAAI,EAAE,eAAe,EAAE;gBAC5C,mCAAqB;gBACrB;oBACC,IAAI,EAAE,sBAAsB;oBAC5B,IAAI,EAAE,iBAAU,CAAC,SAAS;oBAC1B,QAAQ,EAAE,KAAK;iBACf;aACD;SACD,CAAC,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QAClB,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;YACxB,MAAM,EAAE,eAAe;SACvB,CAAC,CAAC;IACJ,CAAC;CACD;
|
|
1
|
+
{"version":3,"file":"001-create-passwords-table.js","sourceRoot":"","sources":["../../../src/migrations/001-create-passwords-table.ts"],"names":[],"mappings":";;;AAAA,wDAIgC;AAChC,qCAAmD;AAEnD,MAAa,oBAAqB,SAAQ,gBAAS;IACzC,KAAK,CAAC,EAAE;QAChB,MAAM,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC;YAC1B,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE;gBACR,2BAAa;gBACb;oBACC,IAAI,EAAE,cAAc;oBACpB,IAAI,EAAE,iBAAU,CAAC,IAAI;oBACrB,EAAE,EAAE;wBACH,eAAe,EAAE,gBAAgB;wBACjC,gBAAgB,EAAE,IAAI;wBACtB,QAAQ,EAAE,SAAS;qBACnB;iBACD;gBACD,EAAE,GAAG,4BAAc,EAAE,IAAI,EAAE,eAAe,EAAE;gBAC5C,mCAAqB;gBACrB;oBACC,IAAI,EAAE,sBAAsB;oBAC5B,IAAI,EAAE,iBAAU,CAAC,SAAS;oBAC1B,QAAQ,EAAE,KAAK;iBACf;gBACD;oBACC,IAAI,EAAE,6BAA6B;oBACnC,IAAI,EAAE,iBAAU,CAAC,GAAG;oBACpB,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE,CAAC;iBACV;gBACD;oBACC,IAAI,EAAE,cAAc;oBACpB,IAAI,EAAE,iBAAU,CAAC,SAAS;oBAC1B,QAAQ,EAAE,KAAK;iBACf;aACD;SACD,CAAC,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QAClB,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;YACxB,MAAM,EAAE,eAAe;SACvB,CAAC,CAAC;IACJ,CAAC;CACD;AA1CD,oDA0CC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AddPasswordAuthenticationProtection = void 0;
|
|
4
|
+
const dbal_1 = require("@riao/dbal");
|
|
5
|
+
class AddPasswordAuthenticationProtection extends dbal_1.Migration {
|
|
6
|
+
async up() {
|
|
7
|
+
await this.ddl.addColumns({
|
|
8
|
+
table: 'iam_passwords',
|
|
9
|
+
columns: [
|
|
10
|
+
{
|
|
11
|
+
name: 'failed_authentication_count',
|
|
12
|
+
type: dbal_1.ColumnType.INT,
|
|
13
|
+
required: true,
|
|
14
|
+
default: 0,
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
});
|
|
18
|
+
await this.ddl.addColumns({
|
|
19
|
+
table: 'iam_passwords',
|
|
20
|
+
columns: [
|
|
21
|
+
{
|
|
22
|
+
name: 'locked_until',
|
|
23
|
+
type: dbal_1.ColumnType.TIMESTAMP,
|
|
24
|
+
required: false,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async down() {
|
|
30
|
+
await this.ddl.dropColumn({
|
|
31
|
+
table: 'iam_passwords',
|
|
32
|
+
column: 'locked_until',
|
|
33
|
+
});
|
|
34
|
+
await this.ddl.dropColumn({
|
|
35
|
+
table: 'iam_passwords',
|
|
36
|
+
column: 'failed_authentication_count',
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.AddPasswordAuthenticationProtection = AddPasswordAuthenticationProtection;
|
|
41
|
+
//# sourceMappingURL=002-add-password-authentication-protection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"002-add-password-authentication-protection.js","sourceRoot":"","sources":["../../../src/migrations/002-add-password-authentication-protection.ts"],"names":[],"mappings":";;;AAAA,qCAAmD;AAEnD,MAAa,mCAAoC,SAAQ,gBAAS;IACxD,KAAK,CAAC,EAAE;QAChB,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;YACzB,KAAK,EAAE,eAAe;YACtB,OAAO,EAAE;gBACR;oBACC,IAAI,EAAE,6BAA6B;oBACnC,IAAI,EAAE,iBAAU,CAAC,GAAG;oBACpB,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE,CAAC;iBACV;aACD;SACD,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;YACzB,KAAK,EAAE,eAAe;YACtB,OAAO,EAAE;gBACR;oBACC,IAAI,EAAE,cAAc;oBACpB,IAAI,EAAE,iBAAU,CAAC,SAAS;oBAC1B,QAAQ,EAAE,KAAK;iBACf;aACD;SACD,CAAC,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QAClB,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;YACzB,KAAK,EAAE,eAAe;YACtB,MAAM,EAAE,cAAc;SACtB,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;YACzB,KAAK,EAAE,eAAe;YACtB,MAAM,EAAE,6BAA6B;SACrC,CAAC,CAAC;IACJ,CAAC;CACD;AAnCD,kFAmCC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riao/authn-password",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Password driver for Riao",
|
|
5
5
|
"typescript-template": {
|
|
6
6
|
"base": "1.0.1",
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@riao/
|
|
13
|
-
"@riao/
|
|
12
|
+
"@riao/crypto": "^1.1.1",
|
|
13
|
+
"@riao/dbal": "^3.12.0",
|
|
14
|
+
"@riao/iam": "^4.0.0",
|
|
14
15
|
"bcrypt": "^6.0.0"
|
|
15
16
|
},
|
|
16
17
|
"repository": {
|