@scryan7371/sdr-security 0.1.1 → 0.1.2
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/README.md +175 -13
- package/dist/api/contracts.d.ts +12 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/migrations/1739520000000-create-password-reset-tokens.d.ts +9 -0
- package/dist/api/migrations/1739520000000-create-password-reset-tokens.js +42 -0
- package/dist/api/migrations/index.d.ts +2 -1
- package/dist/api/migrations/index.js +4 -1
- package/dist/api/migrations/migrations.test.d.ts +1 -0
- package/dist/api/migrations/migrations.test.js +134 -0
- package/dist/api/notification-workflows.d.ts +35 -0
- package/dist/api/notification-workflows.js +23 -0
- package/dist/api/notification-workflows.test.d.ts +1 -0
- package/dist/api/notification-workflows.test.js +66 -0
- package/dist/api/validation.test.d.ts +1 -0
- package/dist/api/validation.test.js +20 -0
- package/dist/app/client.d.ts +17 -2
- package/dist/app/client.js +38 -11
- package/dist/app/client.test.d.ts +1 -0
- package/dist/app/client.test.js +132 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +10 -0
- package/dist/integration/database.integration.test.d.ts +1 -0
- package/dist/integration/database.integration.test.js +158 -0
- package/dist/nest/contracts.d.ts +24 -0
- package/dist/nest/contracts.js +2 -0
- package/dist/nest/dto/auth.dto.d.ts +27 -0
- package/dist/nest/dto/auth.dto.js +99 -0
- package/dist/nest/dto/workflows.dto.d.ts +16 -0
- package/dist/nest/dto/workflows.dto.js +58 -0
- package/dist/nest/entities/app-user.entity.d.ts +11 -0
- package/dist/nest/entities/app-user.entity.js +64 -0
- package/dist/nest/entities/password-reset-token.entity.d.ts +8 -0
- package/dist/nest/entities/password-reset-token.entity.js +49 -0
- package/dist/nest/entities/refresh-token.entity.d.ts +8 -0
- package/dist/nest/entities/refresh-token.entity.js +49 -0
- package/dist/nest/entities/security-role.entity.d.ts +6 -0
- package/dist/nest/entities/security-role.entity.js +39 -0
- package/dist/nest/entities/security-user-role.entity.d.ts +5 -0
- package/dist/nest/entities/security-user-role.entity.js +34 -0
- package/dist/nest/index.d.ts +18 -0
- package/dist/nest/index.js +34 -0
- package/dist/nest/index.test.d.ts +1 -0
- package/dist/nest/index.test.js +14 -0
- package/dist/nest/security-admin.guard.d.ts +4 -0
- package/dist/nest/security-admin.guard.js +25 -0
- package/dist/nest/security-admin.guard.test.d.ts +1 -0
- package/dist/nest/security-admin.guard.test.js +24 -0
- package/dist/nest/security-auth.constants.d.ts +1 -0
- package/dist/nest/security-auth.constants.js +4 -0
- package/dist/nest/security-auth.controller.d.ts +53 -0
- package/dist/nest/security-auth.controller.js +179 -0
- package/dist/nest/security-auth.controller.test.d.ts +1 -0
- package/dist/nest/security-auth.controller.test.js +91 -0
- package/dist/nest/security-auth.module.d.ts +9 -0
- package/dist/nest/security-auth.module.js +68 -0
- package/dist/nest/security-auth.options.d.ts +8 -0
- package/dist/nest/security-auth.options.js +2 -0
- package/dist/nest/security-auth.service.d.ts +59 -0
- package/dist/nest/security-auth.service.js +269 -0
- package/dist/nest/security-auth.service.test.d.ts +1 -0
- package/dist/nest/security-auth.service.test.js +245 -0
- package/dist/nest/security-jwt.guard.d.ts +7 -0
- package/dist/nest/security-jwt.guard.js +46 -0
- package/dist/nest/security-jwt.guard.test.d.ts +1 -0
- package/dist/nest/security-jwt.guard.test.js +51 -0
- package/dist/nest/security-modules.test.d.ts +1 -0
- package/dist/nest/security-modules.test.js +61 -0
- package/dist/nest/security-workflows.controller.d.ts +72 -0
- package/dist/nest/security-workflows.controller.js +187 -0
- package/dist/nest/security-workflows.controller.test.d.ts +1 -0
- package/dist/nest/security-workflows.controller.test.js +87 -0
- package/dist/nest/security-workflows.module.d.ts +9 -0
- package/dist/nest/security-workflows.module.js +59 -0
- package/dist/nest/security-workflows.service.d.ts +67 -0
- package/dist/nest/security-workflows.service.js +200 -0
- package/dist/nest/security-workflows.service.test.d.ts +1 -0
- package/dist/nest/security-workflows.service.test.js +173 -0
- package/dist/nest/swagger.d.ts +2 -0
- package/dist/nest/swagger.js +16 -0
- package/dist/nest/swagger.test.d.ts +1 -0
- package/dist/nest/swagger.test.js +21 -0
- package/dist/nest/tokens.d.ts +1 -0
- package/dist/nest/tokens.js +4 -0
- package/package.json +45 -4
- package/src/api/contracts.ts +11 -0
- package/src/api/index.ts +1 -0
- package/src/api/migrations/1739520000000-create-password-reset-tokens.ts +57 -0
- package/src/api/migrations/index.ts +3 -0
- package/src/api/migrations/migrations.test.ts +208 -0
- package/src/api/notification-workflows.test.ts +81 -0
- package/src/api/notification-workflows.ts +45 -0
- package/src/api/validation.test.ts +21 -0
- package/src/app/client.test.ts +159 -0
- package/src/app/client.ts +73 -12
- package/src/index.test.ts +9 -0
- package/src/integration/database.integration.test.ts +205 -0
- package/src/nest/contracts.ts +25 -0
- package/src/nest/dto/auth.dto.ts +54 -0
- package/src/nest/dto/workflows.dto.ts +29 -0
- package/src/nest/entities/app-user.entity.ts +31 -0
- package/src/nest/entities/password-reset-token.entity.ts +27 -0
- package/src/nest/entities/refresh-token.entity.ts +22 -0
- package/src/nest/entities/security-role.entity.ts +16 -0
- package/src/nest/entities/security-user-role.entity.ts +13 -0
- package/src/nest/index.test.ts +20 -0
- package/src/nest/index.ts +18 -0
- package/src/nest/security-admin.guard.test.ts +31 -0
- package/src/nest/security-admin.guard.ts +21 -0
- package/src/nest/security-auth.constants.ts +1 -0
- package/src/nest/security-auth.controller.test.ts +132 -0
- package/src/nest/security-auth.controller.ts +152 -0
- package/src/nest/security-auth.module.ts +63 -0
- package/src/nest/security-auth.options.ts +8 -0
- package/src/nest/security-auth.service.test.ts +337 -0
- package/src/nest/security-auth.service.ts +319 -0
- package/src/nest/security-jwt.guard.test.ts +65 -0
- package/src/nest/security-jwt.guard.ts +47 -0
- package/src/nest/security-modules.test.ts +79 -0
- package/src/nest/security-workflows.controller.test.ts +119 -0
- package/src/nest/security-workflows.controller.ts +149 -0
- package/src/nest/security-workflows.module.ts +54 -0
- package/src/nest/security-workflows.service.test.ts +232 -0
- package/src/nest/security-workflows.service.ts +215 -0
- package/src/nest/swagger.test.ts +27 -0
- package/src/nest/swagger.ts +18 -0
- package/src/nest/tokens.ts +1 -0
package/README.md
CHANGED
|
@@ -15,13 +15,112 @@ Use shared helpers/types in your API controllers/services where useful:
|
|
|
15
15
|
- `isValidEmail`
|
|
16
16
|
- `isStrongPassword`
|
|
17
17
|
- `AuthResponse`, `RegisterResponse`, `SafeUser`
|
|
18
|
+
- `notifyAdminsOnEmailVerified`
|
|
19
|
+
- `notifyUserOnAdminApproval`
|
|
20
|
+
|
|
21
|
+
## Nest Integration
|
|
22
|
+
|
|
23
|
+
Import the Nest surface from `@scryan7371/sdr-security/nest`.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { Module } from "@nestjs/common";
|
|
27
|
+
import {
|
|
28
|
+
SecurityWorkflowsModule,
|
|
29
|
+
SECURITY_WORKFLOW_NOTIFIER,
|
|
30
|
+
} from "@scryan7371/sdr-security/nest";
|
|
31
|
+
import { EmailService } from "./notifications/email.service";
|
|
32
|
+
|
|
33
|
+
@Module({
|
|
34
|
+
imports: [
|
|
35
|
+
SecurityWorkflowsModule.forRoot({
|
|
36
|
+
notifierProvider: {
|
|
37
|
+
provide: SECURITY_WORKFLOW_NOTIFIER,
|
|
38
|
+
useFactory: (emailService: EmailService) => ({
|
|
39
|
+
sendAdminsUserEmailVerified: ({ adminEmails, user }) =>
|
|
40
|
+
emailService.sendEmailVerifiedNotificationToAdmins(
|
|
41
|
+
adminEmails,
|
|
42
|
+
user,
|
|
43
|
+
),
|
|
44
|
+
sendUserAccountApproved: ({ email, firstName }) =>
|
|
45
|
+
emailService.sendAccountApproved(email, firstName),
|
|
46
|
+
}),
|
|
47
|
+
inject: [EmailService],
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
})
|
|
52
|
+
export class AppModule {}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Optional Swagger setup in consuming app:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { setupSecuritySwagger } from "@scryan7371/sdr-security/nest";
|
|
59
|
+
|
|
60
|
+
setupSecuritySwagger(app); // default path: /docs/security
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Routes exposed by the shared controller:
|
|
64
|
+
|
|
65
|
+
- `POST /security/auth/register`
|
|
66
|
+
- `POST /security/auth/login`
|
|
67
|
+
- `POST /security/auth/forgot-password`
|
|
68
|
+
- `POST /security/auth/reset-password`
|
|
69
|
+
- `GET /security/auth/verify-email?token=...`
|
|
70
|
+
- `POST /security/auth/change-password` (JWT required)
|
|
71
|
+
- `POST /security/auth/logout` (JWT required)
|
|
72
|
+
- `POST /security/auth/refresh`
|
|
73
|
+
- `GET /security/auth/me/roles` (JWT required)
|
|
74
|
+
- `POST /security/workflows/users/:id/email-verified`
|
|
75
|
+
- marks `email_verified_at` and notifies admins.
|
|
76
|
+
- `PATCH /security/workflows/users/:id/admin-approval` with `{ approved: boolean }`
|
|
77
|
+
- updates `admin_approved_at` and notifies user when approved (admin JWT required).
|
|
78
|
+
- `PATCH /security/workflows/users/:id/active` with `{ active: boolean }` (admin JWT required)
|
|
79
|
+
- `GET /security/workflows/roles` (admin JWT required)
|
|
80
|
+
- `POST /security/workflows/roles` (admin JWT required)
|
|
81
|
+
- `DELETE /security/workflows/roles/:role` (admin JWT required)
|
|
82
|
+
- `GET /security/workflows/users/:id/roles` (admin JWT required)
|
|
83
|
+
- `PUT /security/workflows/users/:id/roles` (admin JWT required)
|
|
84
|
+
- `POST /security/workflows/users/:id/roles` with `{ role: string }` (admin JWT required)
|
|
85
|
+
- `DELETE /security/workflows/users/:id/roles/:role` (admin JWT required)
|
|
86
|
+
|
|
87
|
+
### Shared notification workflows
|
|
88
|
+
|
|
89
|
+
Use these helpers to standardize notification behavior across apps while still
|
|
90
|
+
keeping app-specific email sending in your own services.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { api as sdrSecurity } from "@scryan7371/sdr-security";
|
|
94
|
+
|
|
95
|
+
await sdrSecurity.notifyAdminsOnEmailVerified({
|
|
96
|
+
user: {
|
|
97
|
+
id: user.id,
|
|
98
|
+
email: user.email,
|
|
99
|
+
firstName: user.firstName,
|
|
100
|
+
lastName: user.lastName,
|
|
101
|
+
},
|
|
102
|
+
listAdminEmails: () => usersService.listAdminEmails(),
|
|
103
|
+
notifyAdmins: ({ adminEmails, user }) =>
|
|
104
|
+
emailService.sendEmailVerifiedNotificationToAdmins(adminEmails, user),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
await sdrSecurity.notifyUserOnAdminApproval({
|
|
108
|
+
approved: body.approved,
|
|
109
|
+
user: {
|
|
110
|
+
email: user.email,
|
|
111
|
+
firstName: user.firstName,
|
|
112
|
+
},
|
|
113
|
+
notifyUser: ({ email, firstName }) =>
|
|
114
|
+
emailService.sendAccountApproved(email, firstName),
|
|
115
|
+
});
|
|
116
|
+
```
|
|
18
117
|
|
|
19
118
|
## App Integration
|
|
20
119
|
|
|
21
120
|
Create one client per app session and reuse it across screens:
|
|
22
121
|
|
|
23
122
|
```ts
|
|
24
|
-
import { app as sdrSecurity } from
|
|
123
|
+
import { app as sdrSecurity } from "@scryan7371/sdr-security";
|
|
25
124
|
|
|
26
125
|
const securityClient = sdrSecurity.createSecurityClient({
|
|
27
126
|
baseUrl,
|
|
@@ -42,31 +141,94 @@ Methods:
|
|
|
42
141
|
- `requestPhoneVerification`
|
|
43
142
|
- `verifyPhone`
|
|
44
143
|
|
|
45
|
-
##
|
|
144
|
+
## Publish (npmjs)
|
|
46
145
|
|
|
47
|
-
1.
|
|
146
|
+
1. Configure project-local npm auth (`.npmrc`):
|
|
147
|
+
|
|
148
|
+
```ini
|
|
149
|
+
registry=https://registry.npmjs.org/
|
|
150
|
+
@scryan7371:registry=https://registry.npmjs.org/
|
|
151
|
+
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
2. Set token, bump version, and publish:
|
|
48
155
|
|
|
49
156
|
```bash
|
|
50
|
-
export
|
|
157
|
+
export NPM_TOKEN=xxxx
|
|
158
|
+
npm version patch
|
|
159
|
+
npm publish --access public --registry=https://registry.npmjs.org --userconfig .npmrc
|
|
51
160
|
```
|
|
52
161
|
|
|
53
|
-
|
|
162
|
+
3. Push commit and tags:
|
|
54
163
|
|
|
55
164
|
```bash
|
|
56
|
-
|
|
165
|
+
git push
|
|
166
|
+
git push --tags
|
|
57
167
|
```
|
|
58
168
|
|
|
59
|
-
##
|
|
169
|
+
## CI Publish (GitHub Actions)
|
|
60
170
|
|
|
61
|
-
|
|
171
|
+
Tag pushes like `sdr-security-v*` trigger `.github/workflows/publish.yml`.
|
|
62
172
|
|
|
63
|
-
|
|
64
|
-
@scryan7371:registry=https://npm.pkg.github.com
|
|
65
|
-
//npm.pkg.github.com/:_authToken=${GITHUB_PACKAGES_TOKEN}
|
|
66
|
-
```
|
|
173
|
+
Required repo secret:
|
|
67
174
|
|
|
68
|
-
|
|
175
|
+
- `NPM_TOKEN` (npm granular token with read/write + bypass 2FA for automation).
|
|
176
|
+
|
|
177
|
+
## Install
|
|
178
|
+
|
|
179
|
+
Install a pinned version:
|
|
69
180
|
|
|
70
181
|
```bash
|
|
71
182
|
npm install @scryan7371/sdr-security@0.1.0
|
|
72
183
|
```
|
|
184
|
+
|
|
185
|
+
## Database Integration Test
|
|
186
|
+
|
|
187
|
+
A sample Postgres integration test is included at:
|
|
188
|
+
|
|
189
|
+
- `src/integration/database.integration.test.ts`
|
|
190
|
+
|
|
191
|
+
Run it with:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npm run test:db
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Configuration resolution order:
|
|
198
|
+
|
|
199
|
+
1. `.env.test` (if present)
|
|
200
|
+
2. `.env.dev` (if present)
|
|
201
|
+
3. existing process env
|
|
202
|
+
|
|
203
|
+
Supported env vars:
|
|
204
|
+
|
|
205
|
+
- `SECURITY_TEST_DATABASE_URL` (preferred)
|
|
206
|
+
- or `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`
|
|
207
|
+
- optional fallback: `DATABASE_URL`
|
|
208
|
+
- optional debug:
|
|
209
|
+
- `SECURITY_TEST_KEEP_SCHEMA=true` (do not drop schema after test run)
|
|
210
|
+
- `SECURITY_TEST_SCHEMA=your_schema_name` (use fixed schema name)
|
|
211
|
+
|
|
212
|
+
See `.env.test.example` for a template.
|
|
213
|
+
|
|
214
|
+
## Release Script
|
|
215
|
+
|
|
216
|
+
You can automate version bump + tag + push with:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npm run release:patch
|
|
220
|
+
npm run release:minor
|
|
221
|
+
npm run release:major
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
What it does:
|
|
225
|
+
|
|
226
|
+
1. Verifies clean git working tree
|
|
227
|
+
2. Runs `npm test`
|
|
228
|
+
3. Runs `npm run build`
|
|
229
|
+
4. Bumps `package.json` + `package-lock.json`
|
|
230
|
+
5. Commits as `chore(release): vX.Y.Z`
|
|
231
|
+
6. Tags as `sdr-security-vX.Y.Z`
|
|
232
|
+
7. Pushes commit and tag
|
|
233
|
+
|
|
234
|
+
This tag format triggers `.github/workflows/publish.yml`.
|
package/dist/api/contracts.d.ts
CHANGED
|
@@ -44,3 +44,15 @@ export type DebugCodeResponse = {
|
|
|
44
44
|
success: true;
|
|
45
45
|
debugCode?: string;
|
|
46
46
|
};
|
|
47
|
+
export type GenericSuccessResponse = {
|
|
48
|
+
success: true;
|
|
49
|
+
};
|
|
50
|
+
export type UserActiveResponse = {
|
|
51
|
+
success: true;
|
|
52
|
+
userId: string;
|
|
53
|
+
active: boolean;
|
|
54
|
+
};
|
|
55
|
+
export type AdminNotificationResponse = {
|
|
56
|
+
success: true;
|
|
57
|
+
notified: boolean;
|
|
58
|
+
};
|
package/dist/api/index.d.ts
CHANGED
package/dist/api/index.js
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CreatePasswordResetTokens1739520000000 = void 0;
|
|
4
|
+
class CreatePasswordResetTokens1739520000000 {
|
|
5
|
+
name = "CreatePasswordResetTokens1739520000000";
|
|
6
|
+
async up(queryRunner) {
|
|
7
|
+
const userTableRef = getUserTableReference();
|
|
8
|
+
await queryRunner.query(`
|
|
9
|
+
CREATE TABLE IF NOT EXISTS "security_password_reset_token" (
|
|
10
|
+
"id" uuid PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
|
|
11
|
+
"user_id" varchar NOT NULL,
|
|
12
|
+
"token" varchar NOT NULL,
|
|
13
|
+
"expires_at" timestamptz NOT NULL,
|
|
14
|
+
"used_at" timestamptz,
|
|
15
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
16
|
+
CONSTRAINT "FK_security_password_reset_token_user_id" FOREIGN KEY ("user_id") REFERENCES ${userTableRef} ("id") ON DELETE CASCADE
|
|
17
|
+
)
|
|
18
|
+
`);
|
|
19
|
+
await queryRunner.query(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_security_password_reset_token_token" ON "security_password_reset_token" ("token")`);
|
|
20
|
+
await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_security_password_reset_token_user_id" ON "security_password_reset_token" ("user_id")`);
|
|
21
|
+
}
|
|
22
|
+
async down(queryRunner) {
|
|
23
|
+
await queryRunner.query(`DROP INDEX IF EXISTS "IDX_security_password_reset_token_user_id"`);
|
|
24
|
+
await queryRunner.query(`DROP INDEX IF EXISTS "IDX_security_password_reset_token_token"`);
|
|
25
|
+
await queryRunner.query(`DROP TABLE IF EXISTS "security_password_reset_token"`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.CreatePasswordResetTokens1739520000000 = CreatePasswordResetTokens1739520000000;
|
|
29
|
+
const getUserTableReference = () => {
|
|
30
|
+
const table = getSafeIdentifier(process.env.USER_TABLE, "app_user");
|
|
31
|
+
const schema = process.env.USER_TABLE_SCHEMA
|
|
32
|
+
? getSafeIdentifier(process.env.USER_TABLE_SCHEMA, "public")
|
|
33
|
+
: "public";
|
|
34
|
+
return `"${schema}"."${table}"`;
|
|
35
|
+
};
|
|
36
|
+
const getSafeIdentifier = (value, fallback) => {
|
|
37
|
+
const resolved = value?.trim() || fallback;
|
|
38
|
+
if (!resolved || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(resolved)) {
|
|
39
|
+
throw new Error(`Invalid SQL identifier: ${resolved}`);
|
|
40
|
+
}
|
|
41
|
+
return resolved;
|
|
42
|
+
};
|
|
@@ -2,5 +2,6 @@ import { AddRefreshTokens1700000000001 } from "./1700000000001-add-refresh-token
|
|
|
2
2
|
import { AddGoogleSubjectToUser1739490000000 } from "./1739490000000-add-google-subject-to-user";
|
|
3
3
|
import { CreateSecurityIdentity1739500000000 } from "./1739500000000-create-security-identity";
|
|
4
4
|
import { CreateSecurityRoles1739510000000 } from "./1739510000000-create-security-roles";
|
|
5
|
+
import { CreatePasswordResetTokens1739520000000 } from "./1739520000000-create-password-reset-tokens";
|
|
5
6
|
export declare const securityMigrations: (typeof AddRefreshTokens1700000000001)[];
|
|
6
|
-
export { AddRefreshTokens1700000000001, AddGoogleSubjectToUser1739490000000, CreateSecurityIdentity1739500000000, CreateSecurityRoles1739510000000, };
|
|
7
|
+
export { AddRefreshTokens1700000000001, AddGoogleSubjectToUser1739490000000, CreateSecurityIdentity1739500000000, CreateSecurityRoles1739510000000, CreatePasswordResetTokens1739520000000, };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CreateSecurityRoles1739510000000 = exports.CreateSecurityIdentity1739500000000 = exports.AddGoogleSubjectToUser1739490000000 = exports.AddRefreshTokens1700000000001 = exports.securityMigrations = void 0;
|
|
3
|
+
exports.CreatePasswordResetTokens1739520000000 = exports.CreateSecurityRoles1739510000000 = exports.CreateSecurityIdentity1739500000000 = exports.AddGoogleSubjectToUser1739490000000 = exports.AddRefreshTokens1700000000001 = exports.securityMigrations = void 0;
|
|
4
4
|
const _1700000000001_add_refresh_tokens_1 = require("./1700000000001-add-refresh-tokens");
|
|
5
5
|
Object.defineProperty(exports, "AddRefreshTokens1700000000001", { enumerable: true, get: function () { return _1700000000001_add_refresh_tokens_1.AddRefreshTokens1700000000001; } });
|
|
6
6
|
const _1739490000000_add_google_subject_to_user_1 = require("./1739490000000-add-google-subject-to-user");
|
|
@@ -9,9 +9,12 @@ const _1739500000000_create_security_identity_1 = require("./1739500000000-creat
|
|
|
9
9
|
Object.defineProperty(exports, "CreateSecurityIdentity1739500000000", { enumerable: true, get: function () { return _1739500000000_create_security_identity_1.CreateSecurityIdentity1739500000000; } });
|
|
10
10
|
const _1739510000000_create_security_roles_1 = require("./1739510000000-create-security-roles");
|
|
11
11
|
Object.defineProperty(exports, "CreateSecurityRoles1739510000000", { enumerable: true, get: function () { return _1739510000000_create_security_roles_1.CreateSecurityRoles1739510000000; } });
|
|
12
|
+
const _1739520000000_create_password_reset_tokens_1 = require("./1739520000000-create-password-reset-tokens");
|
|
13
|
+
Object.defineProperty(exports, "CreatePasswordResetTokens1739520000000", { enumerable: true, get: function () { return _1739520000000_create_password_reset_tokens_1.CreatePasswordResetTokens1739520000000; } });
|
|
12
14
|
exports.securityMigrations = [
|
|
13
15
|
_1700000000001_add_refresh_tokens_1.AddRefreshTokens1700000000001,
|
|
14
16
|
_1739490000000_add_google_subject_to_user_1.AddGoogleSubjectToUser1739490000000,
|
|
15
17
|
_1739500000000_create_security_identity_1.CreateSecurityIdentity1739500000000,
|
|
16
18
|
_1739510000000_create_security_roles_1.CreateSecurityRoles1739510000000,
|
|
19
|
+
_1739520000000_create_password_reset_tokens_1.CreatePasswordResetTokens1739520000000,
|
|
17
20
|
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const _1700000000001_add_refresh_tokens_1 = require("./1700000000001-add-refresh-tokens");
|
|
5
|
+
const _1739490000000_add_google_subject_to_user_1 = require("./1739490000000-add-google-subject-to-user");
|
|
6
|
+
const _1739500000000_create_security_identity_1 = require("./1739500000000-create-security-identity");
|
|
7
|
+
const _1739510000000_create_security_roles_1 = require("./1739510000000-create-security-roles");
|
|
8
|
+
const _1739520000000_create_password_reset_tokens_1 = require("./1739520000000-create-password-reset-tokens");
|
|
9
|
+
const index_1 = require("./index");
|
|
10
|
+
const originalEnv = { ...process.env };
|
|
11
|
+
(0, vitest_1.afterEach)(() => {
|
|
12
|
+
process.env = { ...originalEnv };
|
|
13
|
+
vitest_1.vi.restoreAllMocks();
|
|
14
|
+
});
|
|
15
|
+
(0, vitest_1.describe)("security migrations", () => {
|
|
16
|
+
(0, vitest_1.it)("exports migration list", () => {
|
|
17
|
+
(0, vitest_1.expect)(index_1.securityMigrations.length).toBe(5);
|
|
18
|
+
(0, vitest_1.expect)(index_1.securityMigrations[0]).toBe(_1700000000001_add_refresh_tokens_1.AddRefreshTokens1700000000001);
|
|
19
|
+
});
|
|
20
|
+
(0, vitest_1.it)("runs add refresh tokens migration up/down", async () => {
|
|
21
|
+
const query = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
22
|
+
const migration = new _1700000000001_add_refresh_tokens_1.AddRefreshTokens1700000000001();
|
|
23
|
+
await migration.up({ query });
|
|
24
|
+
await migration.down({ query });
|
|
25
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('CREATE TABLE "refresh_token"'));
|
|
26
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('DROP TABLE "refresh_token"'));
|
|
27
|
+
});
|
|
28
|
+
(0, vitest_1.it)("uses schema/table env in refresh token migration", async () => {
|
|
29
|
+
process.env.USER_TABLE = "users";
|
|
30
|
+
process.env.USER_TABLE_SCHEMA = "security";
|
|
31
|
+
const query = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
32
|
+
await new _1700000000001_add_refresh_tokens_1.AddRefreshTokens1700000000001().up({ query });
|
|
33
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('REFERENCES "security"."users" ("id")'));
|
|
34
|
+
});
|
|
35
|
+
(0, vitest_1.it)("throws for invalid identifiers in refresh token migration", async () => {
|
|
36
|
+
process.env.USER_TABLE = "bad-name;drop";
|
|
37
|
+
const query = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
38
|
+
await (0, vitest_1.expect)(new _1700000000001_add_refresh_tokens_1.AddRefreshTokens1700000000001().up({ query })).rejects.toThrow("Invalid SQL identifier");
|
|
39
|
+
});
|
|
40
|
+
(0, vitest_1.it)("keeps legacy google subject migration as no-op", async () => {
|
|
41
|
+
const migration = new _1739490000000_add_google_subject_to_user_1.AddGoogleSubjectToUser1739490000000();
|
|
42
|
+
await (0, vitest_1.expect)(migration.up()).resolves.toBeUndefined();
|
|
43
|
+
await (0, vitest_1.expect)(migration.down()).resolves.toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
(0, vitest_1.it)("runs security identity migration path with google_subject present", async () => {
|
|
46
|
+
const query = vitest_1.vi
|
|
47
|
+
.fn()
|
|
48
|
+
.mockResolvedValueOnce(undefined)
|
|
49
|
+
.mockResolvedValueOnce(undefined)
|
|
50
|
+
.mockResolvedValueOnce(undefined)
|
|
51
|
+
.mockResolvedValueOnce(undefined)
|
|
52
|
+
.mockResolvedValueOnce([{ "?column?": 1 }])
|
|
53
|
+
.mockResolvedValueOnce(undefined)
|
|
54
|
+
.mockResolvedValueOnce(undefined)
|
|
55
|
+
.mockResolvedValueOnce(undefined);
|
|
56
|
+
const migration = new _1739500000000_create_security_identity_1.CreateSecurityIdentity1739500000000();
|
|
57
|
+
await migration.up({ query });
|
|
58
|
+
await migration.down({ query });
|
|
59
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('CREATE TABLE IF NOT EXISTS "security_identity"'));
|
|
60
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('DROP TABLE IF EXISTS "security_identity"'));
|
|
61
|
+
});
|
|
62
|
+
(0, vitest_1.it)("skips google_subject migration block when column absent", async () => {
|
|
63
|
+
const query = vitest_1.vi
|
|
64
|
+
.fn()
|
|
65
|
+
.mockResolvedValueOnce(undefined)
|
|
66
|
+
.mockResolvedValueOnce(undefined)
|
|
67
|
+
.mockResolvedValueOnce(undefined)
|
|
68
|
+
.mockResolvedValueOnce(undefined)
|
|
69
|
+
.mockResolvedValueOnce([]);
|
|
70
|
+
await new _1739500000000_create_security_identity_1.CreateSecurityIdentity1739500000000().up({ query });
|
|
71
|
+
(0, vitest_1.expect)(query).not.toHaveBeenCalledWith(vitest_1.expect.stringContaining('DROP COLUMN IF EXISTS "google_subject"'));
|
|
72
|
+
});
|
|
73
|
+
(0, vitest_1.it)("throws for invalid identifiers in identity migration", async () => {
|
|
74
|
+
process.env.USER_TABLE_SCHEMA = "bad-schema!";
|
|
75
|
+
const query = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
76
|
+
await (0, vitest_1.expect)(new _1739500000000_create_security_identity_1.CreateSecurityIdentity1739500000000().up({ query })).rejects.toThrow("Invalid SQL identifier");
|
|
77
|
+
});
|
|
78
|
+
(0, vitest_1.it)("runs security roles migration path with legacy role column", async () => {
|
|
79
|
+
const query = vitest_1.vi
|
|
80
|
+
.fn()
|
|
81
|
+
.mockResolvedValueOnce(undefined)
|
|
82
|
+
.mockResolvedValueOnce(undefined)
|
|
83
|
+
.mockResolvedValueOnce(undefined)
|
|
84
|
+
.mockResolvedValueOnce(undefined)
|
|
85
|
+
.mockResolvedValueOnce(undefined)
|
|
86
|
+
.mockResolvedValueOnce(undefined)
|
|
87
|
+
.mockResolvedValueOnce([{ "?column?": 1 }])
|
|
88
|
+
.mockResolvedValueOnce(undefined)
|
|
89
|
+
.mockResolvedValueOnce(undefined)
|
|
90
|
+
.mockResolvedValueOnce(undefined);
|
|
91
|
+
const migration = new _1739510000000_create_security_roles_1.CreateSecurityRoles1739510000000();
|
|
92
|
+
await migration.up({ query });
|
|
93
|
+
await migration.down({ query });
|
|
94
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('CREATE TABLE IF NOT EXISTS "security_role"'));
|
|
95
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('CREATE TABLE IF NOT EXISTS "security_user_role"'));
|
|
96
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('DROP TABLE IF EXISTS "security_user_role"'));
|
|
97
|
+
});
|
|
98
|
+
(0, vitest_1.it)("skips legacy role backfill when role column absent", async () => {
|
|
99
|
+
const query = vitest_1.vi
|
|
100
|
+
.fn()
|
|
101
|
+
.mockResolvedValueOnce(undefined)
|
|
102
|
+
.mockResolvedValueOnce(undefined)
|
|
103
|
+
.mockResolvedValueOnce(undefined)
|
|
104
|
+
.mockResolvedValueOnce(undefined)
|
|
105
|
+
.mockResolvedValueOnce(undefined)
|
|
106
|
+
.mockResolvedValueOnce(undefined)
|
|
107
|
+
.mockResolvedValueOnce([]);
|
|
108
|
+
await new _1739510000000_create_security_roles_1.CreateSecurityRoles1739510000000().up({ query });
|
|
109
|
+
(0, vitest_1.expect)(query).not.toHaveBeenCalledWith(vitest_1.expect.stringContaining('DROP COLUMN IF EXISTS "role"'));
|
|
110
|
+
});
|
|
111
|
+
(0, vitest_1.it)("throws for invalid identifiers in roles migration", async () => {
|
|
112
|
+
process.env.USER_TABLE = "bad-name*";
|
|
113
|
+
const query = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
114
|
+
await (0, vitest_1.expect)(new _1739510000000_create_security_roles_1.CreateSecurityRoles1739510000000().up({ query })).rejects.toThrow("Invalid SQL identifier");
|
|
115
|
+
});
|
|
116
|
+
(0, vitest_1.it)("runs password reset token migration up/down", async () => {
|
|
117
|
+
const query = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
118
|
+
const migration = new _1739520000000_create_password_reset_tokens_1.CreatePasswordResetTokens1739520000000();
|
|
119
|
+
await migration.up({ query });
|
|
120
|
+
await migration.down({ query });
|
|
121
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('CREATE TABLE IF NOT EXISTS "security_password_reset_token"'));
|
|
122
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('DROP TABLE IF EXISTS "security_password_reset_token"'));
|
|
123
|
+
});
|
|
124
|
+
(0, vitest_1.it)("uses default public schema in password reset migration", async () => {
|
|
125
|
+
const query = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
126
|
+
await new _1739520000000_create_password_reset_tokens_1.CreatePasswordResetTokens1739520000000().up({ query });
|
|
127
|
+
(0, vitest_1.expect)(query).toHaveBeenCalledWith(vitest_1.expect.stringContaining('REFERENCES "public"."app_user" ("id")'));
|
|
128
|
+
});
|
|
129
|
+
(0, vitest_1.it)("throws for invalid identifiers in password reset migration", async () => {
|
|
130
|
+
process.env.USER_TABLE_SCHEMA = "bad.schema";
|
|
131
|
+
const query = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
132
|
+
await (0, vitest_1.expect)(new _1739520000000_create_password_reset_tokens_1.CreatePasswordResetTokens1739520000000().up({ query })).rejects.toThrow("Invalid SQL identifier");
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type VerificationNotificationUser = {
|
|
2
|
+
id: string;
|
|
3
|
+
email: string;
|
|
4
|
+
firstName?: string | null;
|
|
5
|
+
lastName?: string | null;
|
|
6
|
+
};
|
|
7
|
+
export declare const notifyAdminsOnEmailVerified: (params: {
|
|
8
|
+
user: VerificationNotificationUser;
|
|
9
|
+
listAdminEmails: () => Promise<string[]>;
|
|
10
|
+
notifyAdmins: (payload: {
|
|
11
|
+
adminEmails: string[];
|
|
12
|
+
user: VerificationNotificationUser;
|
|
13
|
+
}) => Promise<void>;
|
|
14
|
+
}) => Promise<{
|
|
15
|
+
notified: false;
|
|
16
|
+
adminEmails: string[];
|
|
17
|
+
} | {
|
|
18
|
+
notified: true;
|
|
19
|
+
adminEmails: string[];
|
|
20
|
+
}>;
|
|
21
|
+
export declare const notifyUserOnAdminApproval: (params: {
|
|
22
|
+
approved: boolean;
|
|
23
|
+
user: {
|
|
24
|
+
email: string;
|
|
25
|
+
firstName?: string | null;
|
|
26
|
+
};
|
|
27
|
+
notifyUser: (payload: {
|
|
28
|
+
email: string;
|
|
29
|
+
firstName?: string | null;
|
|
30
|
+
}) => Promise<void>;
|
|
31
|
+
}) => Promise<{
|
|
32
|
+
notified: false;
|
|
33
|
+
} | {
|
|
34
|
+
notified: true;
|
|
35
|
+
}>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.notifyUserOnAdminApproval = exports.notifyAdminsOnEmailVerified = void 0;
|
|
4
|
+
const notifyAdminsOnEmailVerified = async (params) => {
|
|
5
|
+
const adminEmails = await params.listAdminEmails();
|
|
6
|
+
if (adminEmails.length === 0) {
|
|
7
|
+
return { notified: false, adminEmails };
|
|
8
|
+
}
|
|
9
|
+
await params.notifyAdmins({ adminEmails, user: params.user });
|
|
10
|
+
return { notified: true, adminEmails };
|
|
11
|
+
};
|
|
12
|
+
exports.notifyAdminsOnEmailVerified = notifyAdminsOnEmailVerified;
|
|
13
|
+
const notifyUserOnAdminApproval = async (params) => {
|
|
14
|
+
if (!params.approved) {
|
|
15
|
+
return { notified: false };
|
|
16
|
+
}
|
|
17
|
+
await params.notifyUser({
|
|
18
|
+
email: params.user.email,
|
|
19
|
+
firstName: params.user.firstName,
|
|
20
|
+
});
|
|
21
|
+
return { notified: true };
|
|
22
|
+
};
|
|
23
|
+
exports.notifyUserOnAdminApproval = notifyUserOnAdminApproval;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const notification_workflows_1 = require("./notification-workflows");
|
|
5
|
+
(0, vitest_1.describe)("notification-workflows", () => {
|
|
6
|
+
(0, vitest_1.it)("notifies admins when admin recipients exist", async () => {
|
|
7
|
+
const listAdminEmails = vitest_1.vi
|
|
8
|
+
.fn()
|
|
9
|
+
.mockResolvedValue(["admin@example.com"]);
|
|
10
|
+
const notifyAdmins = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
11
|
+
const result = await (0, notification_workflows_1.notifyAdminsOnEmailVerified)({
|
|
12
|
+
user: {
|
|
13
|
+
id: "user-1",
|
|
14
|
+
email: "user@example.com",
|
|
15
|
+
firstName: "User",
|
|
16
|
+
lastName: "One",
|
|
17
|
+
},
|
|
18
|
+
listAdminEmails,
|
|
19
|
+
notifyAdmins,
|
|
20
|
+
});
|
|
21
|
+
(0, vitest_1.expect)(result).toEqual({
|
|
22
|
+
notified: true,
|
|
23
|
+
adminEmails: ["admin@example.com"],
|
|
24
|
+
});
|
|
25
|
+
(0, vitest_1.expect)(notifyAdmins).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
|
|
26
|
+
adminEmails: ["admin@example.com"],
|
|
27
|
+
user: vitest_1.expect.objectContaining({ id: "user-1" }),
|
|
28
|
+
}));
|
|
29
|
+
});
|
|
30
|
+
(0, vitest_1.it)("skips admin notification when there are no admin recipients", async () => {
|
|
31
|
+
const notifyAdmins = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
32
|
+
const result = await (0, notification_workflows_1.notifyAdminsOnEmailVerified)({
|
|
33
|
+
user: {
|
|
34
|
+
id: "user-1",
|
|
35
|
+
email: "user@example.com",
|
|
36
|
+
},
|
|
37
|
+
listAdminEmails: vitest_1.vi.fn().mockResolvedValue([]),
|
|
38
|
+
notifyAdmins,
|
|
39
|
+
});
|
|
40
|
+
(0, vitest_1.expect)(result).toEqual({ notified: false, adminEmails: [] });
|
|
41
|
+
(0, vitest_1.expect)(notifyAdmins).not.toHaveBeenCalled();
|
|
42
|
+
});
|
|
43
|
+
(0, vitest_1.it)("notifies user when account is approved", async () => {
|
|
44
|
+
const notifyUser = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
45
|
+
const result = await (0, notification_workflows_1.notifyUserOnAdminApproval)({
|
|
46
|
+
approved: true,
|
|
47
|
+
user: { email: "user@example.com", firstName: "User" },
|
|
48
|
+
notifyUser,
|
|
49
|
+
});
|
|
50
|
+
(0, vitest_1.expect)(result).toEqual({ notified: true });
|
|
51
|
+
(0, vitest_1.expect)(notifyUser).toHaveBeenCalledWith({
|
|
52
|
+
email: "user@example.com",
|
|
53
|
+
firstName: "User",
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
(0, vitest_1.it)("skips user notification when approval is false", async () => {
|
|
57
|
+
const notifyUser = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
58
|
+
const result = await (0, notification_workflows_1.notifyUserOnAdminApproval)({
|
|
59
|
+
approved: false,
|
|
60
|
+
user: { email: "user@example.com" },
|
|
61
|
+
notifyUser,
|
|
62
|
+
});
|
|
63
|
+
(0, vitest_1.expect)(result).toEqual({ notified: false });
|
|
64
|
+
(0, vitest_1.expect)(notifyUser).not.toHaveBeenCalled();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const validation_1 = require("./validation");
|
|
5
|
+
(0, vitest_1.describe)("validation", () => {
|
|
6
|
+
(0, vitest_1.it)("sanitizes email", () => {
|
|
7
|
+
(0, vitest_1.expect)((0, validation_1.sanitizeEmail)(" USER@Example.COM ")).toBe("user@example.com");
|
|
8
|
+
});
|
|
9
|
+
(0, vitest_1.it)("validates email format", () => {
|
|
10
|
+
(0, vitest_1.expect)((0, validation_1.isValidEmail)("bad-email")).toBe(false);
|
|
11
|
+
(0, vitest_1.expect)((0, validation_1.isValidEmail)("u@e\\.c")).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
(0, vitest_1.it)("checks password strength", () => {
|
|
14
|
+
(0, vitest_1.expect)((0, validation_1.isStrongPassword)("Abc123")).toBe(false);
|
|
15
|
+
(0, vitest_1.expect)((0, validation_1.isStrongPassword)("lowercase123")).toBe(false);
|
|
16
|
+
(0, vitest_1.expect)((0, validation_1.isStrongPassword)("UPPERCASE123")).toBe(false);
|
|
17
|
+
(0, vitest_1.expect)((0, validation_1.isStrongPassword)("NoDigitsHere")).toBe(false);
|
|
18
|
+
(0, vitest_1.expect)((0, validation_1.isStrongPassword)("Abc\\d")).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
});
|
package/dist/app/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthResponse, DebugCodeResponse,
|
|
1
|
+
import type { AdminNotificationResponse, AuthResponse, DebugCodeResponse, GenericSuccessResponse, RoleCatalogResponse, RegisterResponse, UserActiveResponse, UserRolesResponse } from "../api/contracts";
|
|
2
2
|
type FetchLike = typeof fetch;
|
|
3
3
|
export type SecurityClientOptions = {
|
|
4
4
|
baseUrl: string;
|
|
@@ -32,10 +32,18 @@ export declare const createSecurityClient: (options: SecurityClientOptions) => {
|
|
|
32
32
|
}) => Promise<{
|
|
33
33
|
success: true;
|
|
34
34
|
}>;
|
|
35
|
-
|
|
35
|
+
changePassword: (payload: {
|
|
36
|
+
currentPassword: string;
|
|
37
|
+
newPassword: string;
|
|
38
|
+
}) => Promise<GenericSuccessResponse>;
|
|
36
39
|
verifyEmail: (token: string) => Promise<{
|
|
37
40
|
success: true;
|
|
38
41
|
}>;
|
|
42
|
+
forgotPassword: (email: string) => Promise<GenericSuccessResponse>;
|
|
43
|
+
resetPassword: (payload: {
|
|
44
|
+
token: string;
|
|
45
|
+
newPassword: string;
|
|
46
|
+
}) => Promise<GenericSuccessResponse>;
|
|
39
47
|
requestPhoneVerification: () => Promise<DebugCodeResponse>;
|
|
40
48
|
verifyPhone: (code: string) => Promise<{
|
|
41
49
|
success: true;
|
|
@@ -48,5 +56,12 @@ export declare const createSecurityClient: (options: SecurityClientOptions) => {
|
|
|
48
56
|
}) => Promise<RoleCatalogResponse>;
|
|
49
57
|
getUserRoles: (userId: string) => Promise<UserRolesResponse>;
|
|
50
58
|
setUserRoles: (userId: string, roles: string[]) => Promise<UserRolesResponse>;
|
|
59
|
+
approveUser: (userId: string, approved: boolean) => Promise<AdminNotificationResponse>;
|
|
60
|
+
setUserActive: (userId: string, active: boolean) => Promise<UserActiveResponse>;
|
|
61
|
+
removeRole: (role: string) => Promise<{
|
|
62
|
+
success: boolean;
|
|
63
|
+
}>;
|
|
64
|
+
assignRoleToUser: (userId: string, role: string) => Promise<UserRolesResponse>;
|
|
65
|
+
removeRoleFromUser: (userId: string, role: string) => Promise<UserRolesResponse>;
|
|
51
66
|
};
|
|
52
67
|
export {};
|