@technomoron/api-server-base 1.1.13 → 2.0.0-beta.10

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.
Files changed (116) hide show
  1. package/README.txt +25 -2
  2. package/dist/cjs/api-server-base.cjs +448 -111
  3. package/dist/cjs/api-server-base.d.ts +91 -34
  4. package/dist/cjs/auth-api/auth-module.d.ts +105 -0
  5. package/dist/cjs/auth-api/auth-module.js +1180 -0
  6. package/dist/cjs/auth-api/compat-auth-storage.d.ts +57 -0
  7. package/dist/cjs/auth-api/compat-auth-storage.js +128 -0
  8. package/dist/cjs/auth-api/mem-auth-store.d.ts +68 -0
  9. package/dist/cjs/auth-api/mem-auth-store.js +141 -0
  10. package/dist/cjs/{auth-module.d.ts → auth-api/module.d.ts} +7 -7
  11. package/dist/cjs/{auth-module.cjs → auth-api/module.js} +1 -1
  12. package/dist/cjs/auth-api/sql-auth-store.d.ts +77 -0
  13. package/dist/cjs/auth-api/sql-auth-store.js +172 -0
  14. package/dist/cjs/auth-api/storage.d.ts +38 -0
  15. package/dist/cjs/{auth-storage.cjs → auth-api/storage.js} +17 -7
  16. package/dist/cjs/auth-api/types.d.ts +34 -0
  17. package/dist/cjs/auth-api/types.js +2 -0
  18. package/dist/cjs/index.cjs +41 -7
  19. package/dist/cjs/index.d.ts +29 -5
  20. package/dist/cjs/oauth/base.d.ts +10 -0
  21. package/dist/cjs/oauth/base.js +6 -0
  22. package/dist/cjs/oauth/memory.d.ts +16 -0
  23. package/dist/cjs/oauth/memory.js +99 -0
  24. package/dist/cjs/oauth/models.d.ts +45 -0
  25. package/dist/cjs/oauth/models.js +58 -0
  26. package/dist/cjs/oauth/sequelize.d.ts +68 -0
  27. package/dist/cjs/oauth/sequelize.js +210 -0
  28. package/dist/cjs/oauth/types.d.ts +50 -0
  29. package/dist/cjs/oauth/types.js +3 -0
  30. package/dist/cjs/passkey/base.d.ts +16 -0
  31. package/dist/cjs/passkey/base.js +6 -0
  32. package/dist/cjs/passkey/memory.d.ts +27 -0
  33. package/dist/cjs/passkey/memory.js +86 -0
  34. package/dist/cjs/passkey/models.d.ts +25 -0
  35. package/dist/cjs/passkey/models.js +115 -0
  36. package/dist/cjs/passkey/sequelize.d.ts +55 -0
  37. package/dist/cjs/passkey/sequelize.js +220 -0
  38. package/dist/cjs/passkey/service.d.ts +20 -0
  39. package/dist/cjs/passkey/service.js +356 -0
  40. package/dist/cjs/passkey/types.d.ts +78 -0
  41. package/dist/cjs/passkey/types.js +2 -0
  42. package/dist/cjs/token/base.d.ts +38 -0
  43. package/dist/cjs/token/base.js +114 -0
  44. package/dist/cjs/token/memory.d.ts +19 -0
  45. package/dist/cjs/token/memory.js +149 -0
  46. package/dist/cjs/token/sequelize.d.ts +58 -0
  47. package/dist/cjs/token/sequelize.js +404 -0
  48. package/dist/cjs/token/types.d.ts +27 -0
  49. package/dist/cjs/token/types.js +2 -0
  50. package/dist/cjs/user/base.d.ts +26 -0
  51. package/dist/cjs/user/base.js +45 -0
  52. package/dist/cjs/user/memory.d.ts +35 -0
  53. package/dist/cjs/user/memory.js +173 -0
  54. package/dist/cjs/user/sequelize.d.ts +41 -0
  55. package/dist/cjs/user/sequelize.js +182 -0
  56. package/dist/cjs/user/types.d.ts +11 -0
  57. package/dist/cjs/user/types.js +2 -0
  58. package/dist/esm/api-server-base.d.ts +91 -34
  59. package/dist/esm/api-server-base.js +447 -110
  60. package/dist/esm/auth-api/auth-module.d.ts +105 -0
  61. package/dist/esm/auth-api/auth-module.js +1178 -0
  62. package/dist/esm/auth-api/compat-auth-storage.d.ts +57 -0
  63. package/dist/esm/auth-api/compat-auth-storage.js +124 -0
  64. package/dist/esm/auth-api/mem-auth-store.d.ts +68 -0
  65. package/dist/esm/auth-api/mem-auth-store.js +137 -0
  66. package/dist/esm/{auth-module.d.ts → auth-api/module.d.ts} +7 -7
  67. package/dist/esm/{auth-module.js → auth-api/module.js} +1 -1
  68. package/dist/esm/auth-api/sql-auth-store.d.ts +77 -0
  69. package/dist/esm/auth-api/sql-auth-store.js +168 -0
  70. package/dist/esm/auth-api/storage.d.ts +38 -0
  71. package/dist/esm/{auth-storage.js → auth-api/storage.js} +15 -5
  72. package/dist/esm/auth-api/types.d.ts +34 -0
  73. package/dist/esm/auth-api/types.js +1 -0
  74. package/dist/esm/index.d.ts +29 -5
  75. package/dist/esm/index.js +19 -2
  76. package/dist/esm/oauth/base.d.ts +10 -0
  77. package/dist/esm/oauth/base.js +2 -0
  78. package/dist/esm/oauth/memory.d.ts +16 -0
  79. package/dist/esm/oauth/memory.js +92 -0
  80. package/dist/esm/oauth/models.d.ts +45 -0
  81. package/dist/esm/oauth/models.js +51 -0
  82. package/dist/esm/oauth/sequelize.d.ts +68 -0
  83. package/dist/esm/oauth/sequelize.js +199 -0
  84. package/dist/esm/oauth/types.d.ts +50 -0
  85. package/dist/esm/oauth/types.js +2 -0
  86. package/dist/esm/passkey/base.d.ts +16 -0
  87. package/dist/esm/passkey/base.js +2 -0
  88. package/dist/esm/passkey/memory.d.ts +27 -0
  89. package/dist/esm/passkey/memory.js +82 -0
  90. package/dist/esm/passkey/models.d.ts +25 -0
  91. package/dist/esm/passkey/models.js +108 -0
  92. package/dist/esm/passkey/sequelize.d.ts +55 -0
  93. package/dist/esm/passkey/sequelize.js +216 -0
  94. package/dist/esm/passkey/service.d.ts +20 -0
  95. package/dist/esm/passkey/service.js +319 -0
  96. package/dist/esm/passkey/types.d.ts +78 -0
  97. package/dist/esm/passkey/types.js +1 -0
  98. package/dist/esm/token/base.d.ts +38 -0
  99. package/dist/esm/token/base.js +107 -0
  100. package/dist/esm/token/memory.d.ts +19 -0
  101. package/dist/esm/token/memory.js +145 -0
  102. package/dist/esm/token/sequelize.d.ts +58 -0
  103. package/dist/esm/token/sequelize.js +400 -0
  104. package/dist/esm/token/types.d.ts +27 -0
  105. package/dist/esm/token/types.js +1 -0
  106. package/dist/esm/user/base.d.ts +26 -0
  107. package/dist/esm/user/base.js +38 -0
  108. package/dist/esm/user/memory.d.ts +35 -0
  109. package/dist/esm/user/memory.js +169 -0
  110. package/dist/esm/user/sequelize.d.ts +41 -0
  111. package/dist/esm/user/sequelize.js +176 -0
  112. package/dist/esm/user/types.d.ts +11 -0
  113. package/dist/esm/user/types.js +1 -0
  114. package/package.json +13 -3
  115. package/dist/cjs/auth-storage.d.ts +0 -133
  116. package/dist/esm/auth-storage.d.ts +0 -133
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryUserStore = void 0;
4
+ const base_js_1 = require("./base.js");
5
+ function cloneUser(user) {
6
+ return { ...user };
7
+ }
8
+ function normalizeUserId(identifier) {
9
+ if (typeof identifier === 'number' && Number.isFinite(identifier)) {
10
+ return identifier;
11
+ }
12
+ if (typeof identifier === 'string' && /^\d+$/.test(identifier)) {
13
+ return Number(identifier);
14
+ }
15
+ throw new Error(`Unable to normalise user identifier: ${identifier}`);
16
+ }
17
+ class MemoryUserStore extends base_js_1.UserStore {
18
+ constructor(options = {}) {
19
+ super({
20
+ toPublic: options.toPublic,
21
+ bcryptRounds: options.bcryptRounds,
22
+ bcryptPepper: options.bcryptPepper
23
+ });
24
+ this.usersById = new Map();
25
+ this.loginToId = new Map();
26
+ this.emailToId = new Map();
27
+ this.nextUserId = Number.isFinite(options.startingUserId) ? Number(options.startingUserId) : 1;
28
+ this.userIdFactory =
29
+ options.userIdFactory ??
30
+ (() => {
31
+ const id = this.nextUserId;
32
+ this.nextUserId += 1;
33
+ return id;
34
+ });
35
+ }
36
+ async findUser(identifier) {
37
+ if (typeof identifier === 'number') {
38
+ const user = this.usersById.get(identifier);
39
+ return user ? cloneUser(user) : null;
40
+ }
41
+ if (typeof identifier === 'string') {
42
+ const numeric = /^\d+$/.test(identifier) ? Number(identifier) : null;
43
+ if (numeric !== null) {
44
+ const user = this.usersById.get(numeric);
45
+ if (user) {
46
+ return cloneUser(user);
47
+ }
48
+ }
49
+ const loginId = this.loginToId.get(identifier);
50
+ if (loginId !== undefined) {
51
+ return cloneUser(this.usersById.get(loginId));
52
+ }
53
+ const emailId = this.emailToId.get(identifier);
54
+ if (emailId !== undefined) {
55
+ return cloneUser(this.usersById.get(emailId));
56
+ }
57
+ }
58
+ return null;
59
+ }
60
+ async findById(id) {
61
+ try {
62
+ const numeric = normalizeUserId(id);
63
+ const user = this.usersById.get(numeric);
64
+ return user ? cloneUser(user) : null;
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ async findByLoginOrEmail(loginOrEmail) {
71
+ return this.findUser(loginOrEmail);
72
+ }
73
+ async createUser(input) {
74
+ const normalizedInput = this.normalizeUserInput(input);
75
+ const providedId = input.user_id;
76
+ const userId = typeof providedId === 'number' && Number.isFinite(providedId) ? providedId : this.userIdFactory();
77
+ if (this.usersById.has(userId)) {
78
+ throw new Error(`User ${userId} already exists`);
79
+ }
80
+ if (this.loginToId.has(normalizedInput.login)) {
81
+ throw new Error(`User with login ${normalizedInput.login} already exists`);
82
+ }
83
+ if (this.emailToId.has(normalizedInput.email)) {
84
+ throw new Error(`User with email ${normalizedInput.email} already exists`);
85
+ }
86
+ const passwordHash = normalizedInput.password ? await this.hashPassword(normalizedInput.password) : '';
87
+ const record = {
88
+ ...normalizedInput,
89
+ user_id: userId,
90
+ password: passwordHash
91
+ };
92
+ this.persistUser(record);
93
+ if (typeof providedId === 'number' && Number.isFinite(providedId)) {
94
+ this.nextUserId = Math.max(this.nextUserId, providedId + 1);
95
+ }
96
+ return cloneUser(record);
97
+ }
98
+ async upsertUser(input) {
99
+ const normalizedInput = this.normalizeUserInput(input);
100
+ const providedId = input.user_id;
101
+ if (providedId !== undefined) {
102
+ const existing = this.usersById.get(providedId);
103
+ if (!existing) {
104
+ throw new Error(`User ${providedId} not found`);
105
+ }
106
+ const updates = {
107
+ ...existing,
108
+ ...normalizedInput
109
+ };
110
+ if (updates.login !== existing.login) {
111
+ const loginOwner = this.loginToId.get(updates.login);
112
+ if (loginOwner !== undefined && loginOwner !== existing.user_id) {
113
+ throw new Error(`User with login ${updates.login} already exists`);
114
+ }
115
+ }
116
+ if (updates.email !== existing.email) {
117
+ const emailOwner = this.emailToId.get(updates.email);
118
+ if (emailOwner !== undefined && emailOwner !== existing.user_id) {
119
+ throw new Error(`User with email ${updates.email} already exists`);
120
+ }
121
+ }
122
+ if (normalizedInput.password) {
123
+ updates.password = await this.hashPassword(normalizedInput.password);
124
+ }
125
+ this.persistUser(updates);
126
+ return cloneUser(updates);
127
+ }
128
+ return this.createUser(input);
129
+ }
130
+ async updateUser(id, patch) {
131
+ const user = await this.findById(id);
132
+ if (!user) {
133
+ throw new Error(`User ${String(id)} not found`);
134
+ }
135
+ const updates = { ...user, ...patch };
136
+ if (patch.password) {
137
+ updates.password = await this.hashPassword(patch.password);
138
+ }
139
+ this.persistUser(updates);
140
+ return cloneUser(updates);
141
+ }
142
+ async setPasswordHash(id, hash) {
143
+ const user = await this.findById(id);
144
+ if (!user) {
145
+ throw new Error(`User ${String(id)} not found`);
146
+ }
147
+ const updates = { ...user, password: hash };
148
+ this.persistUser(updates);
149
+ }
150
+ getPasswordHash(user) {
151
+ return user.password;
152
+ }
153
+ getUserId(user) {
154
+ return user.user_id;
155
+ }
156
+ persistUser(user) {
157
+ const snapshot = { ...user };
158
+ this.usersById.set(user.user_id, snapshot);
159
+ for (const [login, id] of [...this.loginToId.entries()]) {
160
+ if (id === user.user_id && login !== user.login) {
161
+ this.loginToId.delete(login);
162
+ }
163
+ }
164
+ for (const [email, id] of [...this.emailToId.entries()]) {
165
+ if (id === user.user_id && email !== user.email) {
166
+ this.emailToId.delete(email);
167
+ }
168
+ }
169
+ this.loginToId.set(user.login, user.user_id);
170
+ this.emailToId.set(user.email, user.user_id);
171
+ }
172
+ }
173
+ exports.MemoryUserStore = MemoryUserStore;
@@ -0,0 +1,41 @@
1
+ import { CreationOptional, Model, type InferAttributes, type InferCreationAttributes, type ModelStatic, type Sequelize } from 'sequelize';
2
+ import { UserStore } from './base.js';
3
+ import type { CreateUserInput, PublicUserMapper, UpdateUserInput } from './types.js';
4
+ import type { AuthIdentifier } from '../auth-api/types.js';
5
+ export declare class AuthUserModel extends Model<InferAttributes<AuthUserModel>, InferCreationAttributes<AuthUserModel>> implements InferAttributes<AuthUserModel> {
6
+ user_id: CreationOptional<number>;
7
+ login: string;
8
+ email: string;
9
+ password: string;
10
+ }
11
+ export type AuthUserAttributes = InferAttributes<AuthUserModel>;
12
+ export type AuthUserCreationAttributes = InferCreationAttributes<AuthUserModel>;
13
+ export declare function initAuthUserModel(sequelize: Sequelize): typeof AuthUserModel;
14
+ export type GenericUserModel = Model<Record<string, unknown>, Record<string, unknown>>;
15
+ export type GenericUserModelStatic = ModelStatic<GenericUserModel>;
16
+ export interface SequelizeUserStoreOptions<UserAttributes extends AuthUserAttributes = AuthUserAttributes, PublicUserShape extends Omit<UserAttributes, 'password'> = Omit<UserAttributes, 'password'>> {
17
+ bcryptRounds?: number;
18
+ bcryptPepper?: string;
19
+ sequelize: Sequelize;
20
+ userModel?: GenericUserModelStatic;
21
+ userModelFactory?: (sequelize: Sequelize) => GenericUserModelStatic;
22
+ recordMapper?: (model: GenericUserModel) => UserAttributes;
23
+ toPublic?: PublicUserMapper<UserAttributes, PublicUserShape>;
24
+ }
25
+ export declare class SequelizeUserStore<UserAttributes extends AuthUserAttributes = AuthUserAttributes, PublicUserShape extends Omit<UserAttributes, 'password'> = Omit<UserAttributes, 'password'>> extends UserStore<UserAttributes, PublicUserShape> {
26
+ readonly Users: GenericUserModelStatic;
27
+ private readonly recordMapper;
28
+ constructor(options: SequelizeUserStoreOptions<UserAttributes, PublicUserShape>);
29
+ findUser(identifier: AuthIdentifier | string): Promise<UserAttributes | null>;
30
+ findById(id: AuthIdentifier): Promise<UserAttributes | null>;
31
+ findByLoginOrEmail(loginOrEmail: string): Promise<UserAttributes | null>;
32
+ createUser(input: CreateUserInput): Promise<UserAttributes>;
33
+ upsertUser(input: CreateUserInput): Promise<UserAttributes>;
34
+ updateUser(id: AuthIdentifier, patch: UpdateUserInput): Promise<UserAttributes>;
35
+ setPasswordHash(id: AuthIdentifier, hash: string): Promise<void>;
36
+ getPasswordHash(user: UserAttributes): string | null;
37
+ getUserId(user: UserAttributes): AuthIdentifier;
38
+ protected toUserRecord(model: GenericUserModel): UserAttributes;
39
+ private static mapModelToUser;
40
+ private normalizeUserId;
41
+ }
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SequelizeUserStore = exports.AuthUserModel = void 0;
4
+ exports.initAuthUserModel = initAuthUserModel;
5
+ const sequelize_1 = require("sequelize");
6
+ const base_js_1 = require("./base.js");
7
+ const DIALECTS_SUPPORTING_UNSIGNED = new Set(['mysql', 'mariadb']);
8
+ function integerIdType(sequelize) {
9
+ return DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect()) ? sequelize_1.DataTypes.INTEGER.UNSIGNED : sequelize_1.DataTypes.INTEGER;
10
+ }
11
+ function userTableOptions(sequelize) {
12
+ const opts = {
13
+ sequelize,
14
+ tableName: 'users',
15
+ timestamps: false
16
+ };
17
+ if (DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect())) {
18
+ opts.charset = 'utf8mb4';
19
+ opts.collate = 'utf8mb4_unicode_ci';
20
+ }
21
+ return opts;
22
+ }
23
+ class AuthUserModel extends sequelize_1.Model {
24
+ }
25
+ exports.AuthUserModel = AuthUserModel;
26
+ function initAuthUserModel(sequelize) {
27
+ const idType = integerIdType(sequelize);
28
+ AuthUserModel.init({
29
+ user_id: {
30
+ type: idType,
31
+ autoIncrement: true,
32
+ allowNull: false,
33
+ primaryKey: true
34
+ },
35
+ login: {
36
+ type: sequelize_1.DataTypes.STRING(64),
37
+ allowNull: false,
38
+ unique: true
39
+ },
40
+ email: {
41
+ type: sequelize_1.DataTypes.STRING(100),
42
+ allowNull: false,
43
+ unique: true
44
+ },
45
+ password: {
46
+ type: sequelize_1.DataTypes.STRING(255),
47
+ allowNull: false
48
+ }
49
+ }, {
50
+ ...userTableOptions(sequelize)
51
+ });
52
+ return AuthUserModel;
53
+ }
54
+ class SequelizeUserStore extends base_js_1.UserStore {
55
+ constructor(options) {
56
+ super({
57
+ toPublic: options.toPublic,
58
+ bcryptRounds: options.bcryptRounds,
59
+ bcryptPepper: options.bcryptPepper
60
+ });
61
+ if (!options?.sequelize) {
62
+ throw new Error('SequelizeUserStore requires a configured Sequelize instance');
63
+ }
64
+ this.Users = options.userModel
65
+ ? options.userModel
66
+ : (options.userModelFactory ?? initAuthUserModel)(options.sequelize);
67
+ this.recordMapper =
68
+ options.recordMapper ??
69
+ ((model) => SequelizeUserStore.mapModelToUser(model));
70
+ }
71
+ async findUser(identifier) {
72
+ const where = [];
73
+ try {
74
+ where.push({ user_id: this.normalizeUserId(identifier) });
75
+ }
76
+ catch {
77
+ // ignore
78
+ }
79
+ if (typeof identifier === 'string') {
80
+ where.push({ login: identifier });
81
+ where.push({ email: identifier });
82
+ }
83
+ if (where.length === 0) {
84
+ return null;
85
+ }
86
+ const model = await this.Users.findOne({ where: { [sequelize_1.Op.or]: where } });
87
+ return model ? this.toUserRecord(model) : null;
88
+ }
89
+ async findById(id) {
90
+ try {
91
+ const model = await this.Users.findByPk(this.normalizeUserId(id));
92
+ return model ? this.toUserRecord(model) : null;
93
+ }
94
+ catch {
95
+ return null;
96
+ }
97
+ }
98
+ async findByLoginOrEmail(loginOrEmail) {
99
+ const model = await this.Users.findOne({
100
+ where: { [sequelize_1.Op.or]: [{ login: loginOrEmail }, { email: loginOrEmail }] }
101
+ });
102
+ return model ? this.toUserRecord(model) : null;
103
+ }
104
+ async createUser(input) {
105
+ const normalized = this.normalizeUserInput(input);
106
+ const { password, ...rest } = normalized;
107
+ const defaults = {
108
+ ...rest,
109
+ password: password ? await this.hashPassword(password) : ''
110
+ };
111
+ const providedId = input.user_id;
112
+ if (providedId !== undefined && providedId !== null && Number.isFinite(providedId)) {
113
+ defaults.user_id = Number(providedId);
114
+ }
115
+ const [model] = await this.Users.findOrCreate({
116
+ where: { login: rest.login },
117
+ defaults: defaults
118
+ });
119
+ return this.toUserRecord(model);
120
+ }
121
+ async upsertUser(input) {
122
+ const normalized = this.normalizeUserInput(input);
123
+ const providedId = input.user_id;
124
+ if (providedId !== undefined) {
125
+ const model = await this.Users.findByPk(Number(providedId));
126
+ if (!model) {
127
+ throw new Error(`User ${String(providedId)} not found`);
128
+ }
129
+ const next = { ...normalized };
130
+ if (normalized.password) {
131
+ next.password = await this.hashPassword(normalized.password);
132
+ }
133
+ await model.set(next);
134
+ await model.save();
135
+ return this.toUserRecord(model);
136
+ }
137
+ return this.createUser(input);
138
+ }
139
+ async updateUser(id, patch) {
140
+ const model = await this.Users.findByPk(this.normalizeUserId(id));
141
+ if (!model) {
142
+ throw new Error(`User ${String(id)} not found`);
143
+ }
144
+ const updates = { ...patch };
145
+ if (patch.password) {
146
+ updates.password = await this.hashPassword(patch.password);
147
+ }
148
+ await model.set(updates);
149
+ await model.save();
150
+ return this.toUserRecord(model);
151
+ }
152
+ async setPasswordHash(id, hash) {
153
+ await this.Users.update({ password: hash }, { where: { user_id: this.normalizeUserId(id) } });
154
+ }
155
+ getPasswordHash(user) {
156
+ return user.password;
157
+ }
158
+ getUserId(user) {
159
+ return user.user_id;
160
+ }
161
+ toUserRecord(model) {
162
+ return this.recordMapper(model);
163
+ }
164
+ static mapModelToUser(model) {
165
+ return {
166
+ user_id: model.get('user_id'),
167
+ login: model.get('login'),
168
+ email: model.get('email'),
169
+ password: model.get('password')
170
+ };
171
+ }
172
+ normalizeUserId(identifier) {
173
+ if (typeof identifier === 'number' && Number.isFinite(identifier)) {
174
+ return identifier;
175
+ }
176
+ if (typeof identifier === 'string' && /^\d+$/.test(identifier)) {
177
+ return Number(identifier);
178
+ }
179
+ throw new Error(`Unable to normalise user identifier: ${identifier}`);
180
+ }
181
+ }
182
+ exports.SequelizeUserStore = SequelizeUserStore;
@@ -0,0 +1,11 @@
1
+ export interface BcryptHasherOptions {
2
+ rounds?: number;
3
+ pepper?: string;
4
+ }
5
+ export type CreateUserInput = Record<string, unknown> & {
6
+ login: string;
7
+ email: string;
8
+ password?: string;
9
+ };
10
+ export type UpdateUserInput = Partial<CreateUserInput>;
11
+ export type PublicUserMapper<User, PublicUser> = (user: User) => PublicUser;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -4,49 +4,40 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import { Application, Request, Response } from 'express';
8
- import jwt, { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
7
+ import { Application, Request, Response, type ErrorRequestHandler, type RequestHandler } from 'express';
9
8
  import { ApiModule } from './api-module.js';
10
- import type { ApiAuthClass, ApiKey } from './api-module.js';
11
- import type { AuthProviderModule } from './auth-module.js';
12
- import type { AuthStorage, AuthIdentifier, AuthTokenData, AuthTokenMetadata } from './auth-storage.js';
9
+ import { TokenStore, type JwtDecodeResult, type JwtSignResult, type JwtVerifyResult } from './token/base.js';
10
+ import type { ApiAuthClass, ApiAuthType, ApiKey } from './api-module.js';
11
+ import type { AuthProviderModule } from './auth-api/module.js';
12
+ import type { AuthAdapter, AuthIdentifier } from './auth-api/types.js';
13
+ import type { OAuthStore } from './oauth/base.js';
14
+ import type { AuthCodeData, AuthCodeRequest, OAuthClient } from './oauth/types.js';
15
+ import type { PasskeyService } from './passkey/service.js';
16
+ import type { PasskeyChallenge, PasskeyChallengeParams, StoredPasskeyCredential, PasskeyVerificationParams, PasskeyVerificationResult } from './passkey/types.js';
17
+ import type { Token } from './token/types.js';
18
+ import type { UserStore } from './user/base.js';
19
+ import type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
13
20
  export type { Application, Request, Response, NextFunction, Router } from 'express';
14
21
  export type { Multer } from 'multer';
15
22
  export type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
16
- export interface RequestWithStuff extends Request {
23
+ export interface ExtendedReq extends Request {
17
24
  file?: Express.Multer.File;
18
25
  files?: Express.Multer.File[] | {
19
26
  [fieldname: string]: Express.Multer.File[];
20
27
  };
21
28
  }
22
- interface JwtSignResult {
23
- success: boolean;
24
- token?: string;
25
- error?: string;
26
- }
27
- interface JwtVerifyResult<T> {
28
- success: boolean;
29
- data?: T;
30
- expired?: boolean;
31
- error?: string;
32
- }
33
- interface JwtDecodeResult<T> {
34
- success: boolean;
35
- data?: T;
36
- error?: string;
37
- }
38
- export interface ApiTokenData extends JwtPayload, AuthTokenMetadata {
29
+ export interface ApiTokenData extends JwtPayload, Partial<Token> {
39
30
  uid: unknown;
40
31
  iat?: number;
41
32
  exp?: number;
42
33
  }
43
34
  export interface ApiRequest {
44
35
  server: any;
45
- req: RequestWithStuff;
36
+ req: ExtendedReq;
46
37
  res: Response;
47
38
  tokenData?: ApiTokenData | null;
48
39
  token?: string;
49
- authToken?: AuthTokenData | null;
40
+ authToken?: Token | null;
50
41
  apiKey?: ApiKey | null;
51
42
  clientInfo?: ClientInfo;
52
43
  realUid?: AuthIdentifier | null;
@@ -56,6 +47,12 @@ export interface ApiRequest {
56
47
  getRealUid: () => AuthIdentifier | null;
57
48
  isImpersonating: () => boolean;
58
49
  }
50
+ export interface ExpressApiRequest extends ExtendedReq {
51
+ apiReq?: ApiRequest;
52
+ }
53
+ export interface ExpressApiLocals {
54
+ apiReq?: ApiRequest;
55
+ }
59
56
  export interface ClientAgentProfile {
60
57
  ua: string;
61
58
  browser: string;
@@ -66,6 +63,16 @@ export interface ClientInfo extends ClientAgentProfile {
66
63
  ip: string | null;
67
64
  ipchain: string[];
68
65
  }
66
+ export interface ApiServerAuthStores {
67
+ userStore: UserStore<any, any>;
68
+ tokenStore: TokenStore;
69
+ passkeyService?: PasskeyService;
70
+ oauthStore?: OAuthStore;
71
+ canImpersonate?: (params: {
72
+ realUserId: AuthIdentifier;
73
+ effectiveUserId: AuthIdentifier;
74
+ }) => boolean | Promise<boolean>;
75
+ }
69
76
  export { ApiModule } from './api-module.js';
70
77
  export type { ApiHandler, ApiAuthType, ApiAuthClass, ApiRoute, ApiKey } from './api-module.js';
71
78
  export interface ApiErrorParams {
@@ -100,8 +107,11 @@ export interface ApiServerConf {
100
107
  devMode: boolean;
101
108
  hydrateGetBody: boolean;
102
109
  validateTokens: boolean;
110
+ refreshMaybe: boolean;
103
111
  apiVersion: string;
104
112
  minClientVersion: string;
113
+ tokenStore?: TokenStore;
114
+ authStores?: ApiServerAuthStores;
105
115
  }
106
116
  export declare class ApiServer {
107
117
  app: Application;
@@ -112,33 +122,69 @@ export declare class ApiServer {
112
122
  private storageAdapter;
113
123
  private moduleAdapter;
114
124
  private apiNotFoundHandler;
125
+ private tokenStoreAdapter;
126
+ private userStoreAdapter;
127
+ private passkeyServiceAdapter;
128
+ private oauthStoreAdapter;
129
+ private canImpersonateAdapter;
130
+ private readonly jwtHelper;
115
131
  constructor(config?: Partial<ApiServerConf>);
116
- authStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
132
+ authStorage<UserRow, SafeUser>(storage: AuthAdapter<UserRow, SafeUser>): this;
117
133
  /**
118
134
  * @deprecated Use {@link ApiServer.authStorage} instead.
119
135
  */
120
- useAuthStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
136
+ useAuthStorage<UserRow, SafeUser>(storage: AuthAdapter<UserRow, SafeUser>): this;
121
137
  authModule<UserRow>(module: AuthProviderModule<UserRow>): this;
122
138
  /**
123
139
  * @deprecated Use {@link ApiServer.authModule} instead.
124
140
  */
125
141
  useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
126
- getAuthStorage(): AuthStorage<any, any>;
142
+ getAuthStorage(): AuthAdapter<any, any>;
127
143
  getAuthModule(): AuthProviderModule<any>;
144
+ setTokenStore(store: TokenStore): this;
145
+ getTokenStore(): TokenStore | null;
146
+ private ensureUserStore;
147
+ private ensureTokenStore;
148
+ private ensurePasskeyService;
149
+ listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
150
+ deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
151
+ private ensureOAuthStore;
152
+ getUser(identifier: AuthIdentifier): Promise<any | null>;
153
+ getUserPasswordHash(user: any): string;
154
+ getUserId(user: any): AuthIdentifier;
155
+ filterUser(user: any): any;
156
+ verifyPassword(password: string, hash: string): Promise<boolean>;
157
+ storeToken(data: Token): Promise<void>;
158
+ getToken(query: Partial<Token> & {
159
+ userId?: AuthIdentifier;
160
+ ruid?: AuthIdentifier;
161
+ }, opts?: {
162
+ includeExpired?: boolean;
163
+ }): Promise<Token | null>;
164
+ deleteToken(query: Partial<Token> & {
165
+ userId?: AuthIdentifier;
166
+ ruid?: AuthIdentifier;
167
+ }): Promise<number>;
168
+ createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
169
+ verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
170
+ getClient(clientId: string): Promise<OAuthClient | null>;
171
+ verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
172
+ createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
173
+ consumeAuthCode(code: string, clientId: string): Promise<AuthCodeData | null>;
174
+ canImpersonate(params: {
175
+ realUserId: AuthIdentifier;
176
+ effectiveUserId: AuthIdentifier;
177
+ }): Promise<boolean>;
128
178
  jwtSign(payload: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
129
179
  jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
130
- jwtDecode<T>(token: string, options?: jwt.DecodeOptions): JwtDecodeResult<T>;
180
+ jwtDecode<T>(token: string, options?: import('jsonwebtoken').DecodeOptions): JwtDecodeResult<T>;
131
181
  getApiKey<T = ApiKey>(token: string): Promise<T | null>;
132
182
  authenticateUser(params: {
133
183
  login: string;
134
184
  password: string;
135
185
  }): Promise<boolean>;
136
- updateToken(updates: {
137
- accessToken: string;
186
+ updateToken(updates: Partial<Token> & {
138
187
  refreshToken: string;
139
- expires?: Date;
140
- clientId?: string;
141
- scope?: string[];
142
188
  }): Promise<boolean>;
143
189
  guessExceptionText(error: any, defMsg?: string): string;
144
190
  protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
@@ -150,6 +196,9 @@ export declare class ApiServer {
150
196
  private describeMissingEndpoint;
151
197
  start(): this;
152
198
  private verifyJWT;
199
+ private jwtCookieOptions;
200
+ private setAccessCookie;
201
+ private tryRefreshAccessToken;
153
202
  private authenticate;
154
203
  private tryAuthenticateApiKey;
155
204
  private requiresAuthToken;
@@ -158,6 +207,14 @@ export declare class ApiServer {
158
207
  private normalizeAuthIdentifier;
159
208
  private extractTokenUserId;
160
209
  private resolveRealUserId;
210
+ useExpress(path: string, ...handlers: Array<RequestHandler | ErrorRequestHandler>): this;
211
+ useExpress(...handlers: Array<RequestHandler | ErrorRequestHandler>): this;
212
+ private createApiRequest;
213
+ expressAuth(auth: {
214
+ type: ApiAuthType;
215
+ req: ApiAuthClass;
216
+ }): RequestHandler;
217
+ expressErrorHandler(): ErrorRequestHandler;
161
218
  private handle_request;
162
219
  api<T extends ApiModule<any>>(module: T): this;
163
220
  dumpRequest(apiReq: ApiRequest): void;