@datrix/api 0.1.0 → 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 +1 -1
- package/dist/index.d.mts +20 -12
- package/dist/index.d.ts +20 -12
- package/dist/index.js +94 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +98 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DefaultPermission, IAuthManager, AuthUser, LoginResult, AuthContext, IUpload, BasePlugin, IApiPlugin, Datrix, QueryContext, PluginContext, SchemaDefinition,
|
|
1
|
+
import { DatrixEntry, DefaultPermission, AuthenticatedUser, IAuthManager, AuthUser, LoginResult, AuthContext, IUpload, BasePlugin, IApiPlugin, Datrix, QueryContext, PluginContext, SchemaDefinition, QueryObject, PermissionAction, ParsedQuery, FallbackInput, ParserError, PermissionValue, PermissionCheckResult, FieldPermissionCheckResult, RawQueryParams, ParserOptions, FallbackWhereClause, PopulateClause, DatrixRecord, DatrixError } from '@datrix/core';
|
|
2
2
|
export { PermissionAction } from '@datrix/core';
|
|
3
3
|
|
|
4
4
|
interface PasswordHash {
|
|
@@ -52,7 +52,7 @@ interface SessionConfig {
|
|
|
52
52
|
readonly checkPeriod?: number;
|
|
53
53
|
readonly prefix?: string;
|
|
54
54
|
}
|
|
55
|
-
interface AuthConfig<TRole extends string = string> {
|
|
55
|
+
interface AuthConfig<TRole extends string = string, TUser extends DatrixEntry = DatrixEntry> {
|
|
56
56
|
readonly roles: readonly TRole[];
|
|
57
57
|
readonly defaultRole: TRole;
|
|
58
58
|
readonly defaultPermission?: DefaultPermission<TRole>;
|
|
@@ -69,8 +69,14 @@ interface AuthConfig<TRole extends string = string> {
|
|
|
69
69
|
readonly register?: string;
|
|
70
70
|
readonly logout?: string;
|
|
71
71
|
readonly me?: string;
|
|
72
|
+
readonly forgotPassword?: string;
|
|
73
|
+
readonly resetPassword?: string;
|
|
72
74
|
readonly disableRegister?: boolean;
|
|
73
75
|
};
|
|
76
|
+
readonly passwordReset?: {
|
|
77
|
+
readonly tokenExpirySeconds?: number;
|
|
78
|
+
readonly onForgotPassword?: (user: Omit<AuthenticatedUser<TRole, TUser>, "password" | "passwordSalt">, token: string) => Promise<void>;
|
|
79
|
+
};
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
declare class JwtStrategy {
|
|
@@ -122,13 +128,13 @@ declare class MemorySessionStore implements SessionStore {
|
|
|
122
128
|
private getKey;
|
|
123
129
|
}
|
|
124
130
|
|
|
125
|
-
declare class AuthManager<TRole extends string = string> implements IAuthManager {
|
|
131
|
+
declare class AuthManager<TRole extends string = string, TUser extends DatrixEntry = DatrixEntry> implements IAuthManager {
|
|
126
132
|
private readonly passwordManager;
|
|
127
133
|
private readonly jwtStrategy;
|
|
128
134
|
private readonly sessionStrategy;
|
|
129
135
|
private readonly config;
|
|
130
|
-
get authConfig(): AuthConfig<TRole>;
|
|
131
|
-
constructor(config: AuthConfig<TRole>);
|
|
136
|
+
get authConfig(): AuthConfig<TRole, TUser>;
|
|
137
|
+
constructor(config: AuthConfig<TRole, TUser>);
|
|
132
138
|
hashPassword(password: string): Promise<PasswordHash>;
|
|
133
139
|
verifyPassword(password: string, hash: string, salt: string): Promise<boolean>;
|
|
134
140
|
login(user: AuthUser, options?: {
|
|
@@ -158,7 +164,7 @@ interface ApiConfig<TRole extends string = string> extends Record<string, unknow
|
|
|
158
164
|
declare class ApiPlugin<TRole extends string = string> extends BasePlugin<ApiConfig<TRole>> implements IApiPlugin<TRole> {
|
|
159
165
|
readonly name = "api";
|
|
160
166
|
readonly version = "1.0.0";
|
|
161
|
-
authManager?: AuthManager
|
|
167
|
+
authManager?: AuthManager<TRole>;
|
|
162
168
|
user: AuthUser | null;
|
|
163
169
|
private datrixInstance?;
|
|
164
170
|
get datrix(): Datrix;
|
|
@@ -186,7 +192,7 @@ declare class ApiPlugin<TRole extends string = string> extends BasePlugin<ApiCon
|
|
|
186
192
|
private handleAuthRequest;
|
|
187
193
|
isEnabled(): boolean;
|
|
188
194
|
isAuthEnabled(): boolean;
|
|
189
|
-
getAuthManager(): AuthManager | undefined;
|
|
195
|
+
getAuthManager(): AuthManager<TRole> | undefined;
|
|
190
196
|
}
|
|
191
197
|
|
|
192
198
|
declare function handleRequest(datrix: Datrix, request: Request): Promise<Response>;
|
|
@@ -242,18 +248,20 @@ declare function serializeQuery<T extends DatrixEntry = DatrixEntry>(query: Pars
|
|
|
242
248
|
|
|
243
249
|
declare function handleCrudRequest<TRole extends string = string>(request: Request, datrix: Datrix, api: IApiPlugin<TRole>, options?: ContextBuilderOptions): Promise<Response>;
|
|
244
250
|
|
|
245
|
-
interface AuthHandlerConfig {
|
|
251
|
+
interface AuthHandlerConfig<TRole extends string = string, TUser extends DatrixEntry = DatrixEntry> {
|
|
246
252
|
readonly datrix: Datrix;
|
|
247
|
-
readonly authManager: AuthManager
|
|
248
|
-
readonly authConfig: AuthConfig
|
|
253
|
+
readonly authManager: AuthManager<TRole, TUser>;
|
|
254
|
+
readonly authConfig: AuthConfig<TRole, TUser>;
|
|
249
255
|
}
|
|
250
|
-
declare function createAuthHandlers(config: AuthHandlerConfig): {
|
|
256
|
+
declare function createAuthHandlers<TRole extends string = string, TUser extends DatrixEntry = DatrixEntry>(config: AuthHandlerConfig<TRole, TUser>): {
|
|
251
257
|
register: (request: Request) => Promise<Response>;
|
|
252
258
|
login: (request: Request) => Promise<Response>;
|
|
253
259
|
logout: (request: Request) => Promise<Response>;
|
|
254
260
|
me: (request: Request) => Promise<Response>;
|
|
261
|
+
forgotPassword: (request: Request) => Promise<Response>;
|
|
262
|
+
resetPassword: (request: Request) => Promise<Response>;
|
|
255
263
|
};
|
|
256
|
-
declare function createUnifiedAuthHandler(config: AuthHandlerConfig, apiPrefix?: string): (request: Request) => Promise<Response>;
|
|
264
|
+
declare function createUnifiedAuthHandler<TRole extends string = string, TUser extends DatrixEntry = DatrixEntry>(config: AuthHandlerConfig<TRole, TUser>, apiPrefix?: string): (request: Request) => Promise<Response>;
|
|
257
265
|
|
|
258
266
|
declare class DatrixApiError extends DatrixError {
|
|
259
267
|
status: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DefaultPermission, IAuthManager, AuthUser, LoginResult, AuthContext, IUpload, BasePlugin, IApiPlugin, Datrix, QueryContext, PluginContext, SchemaDefinition,
|
|
1
|
+
import { DatrixEntry, DefaultPermission, AuthenticatedUser, IAuthManager, AuthUser, LoginResult, AuthContext, IUpload, BasePlugin, IApiPlugin, Datrix, QueryContext, PluginContext, SchemaDefinition, QueryObject, PermissionAction, ParsedQuery, FallbackInput, ParserError, PermissionValue, PermissionCheckResult, FieldPermissionCheckResult, RawQueryParams, ParserOptions, FallbackWhereClause, PopulateClause, DatrixRecord, DatrixError } from '@datrix/core';
|
|
2
2
|
export { PermissionAction } from '@datrix/core';
|
|
3
3
|
|
|
4
4
|
interface PasswordHash {
|
|
@@ -52,7 +52,7 @@ interface SessionConfig {
|
|
|
52
52
|
readonly checkPeriod?: number;
|
|
53
53
|
readonly prefix?: string;
|
|
54
54
|
}
|
|
55
|
-
interface AuthConfig<TRole extends string = string> {
|
|
55
|
+
interface AuthConfig<TRole extends string = string, TUser extends DatrixEntry = DatrixEntry> {
|
|
56
56
|
readonly roles: readonly TRole[];
|
|
57
57
|
readonly defaultRole: TRole;
|
|
58
58
|
readonly defaultPermission?: DefaultPermission<TRole>;
|
|
@@ -69,8 +69,14 @@ interface AuthConfig<TRole extends string = string> {
|
|
|
69
69
|
readonly register?: string;
|
|
70
70
|
readonly logout?: string;
|
|
71
71
|
readonly me?: string;
|
|
72
|
+
readonly forgotPassword?: string;
|
|
73
|
+
readonly resetPassword?: string;
|
|
72
74
|
readonly disableRegister?: boolean;
|
|
73
75
|
};
|
|
76
|
+
readonly passwordReset?: {
|
|
77
|
+
readonly tokenExpirySeconds?: number;
|
|
78
|
+
readonly onForgotPassword?: (user: Omit<AuthenticatedUser<TRole, TUser>, "password" | "passwordSalt">, token: string) => Promise<void>;
|
|
79
|
+
};
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
declare class JwtStrategy {
|
|
@@ -122,13 +128,13 @@ declare class MemorySessionStore implements SessionStore {
|
|
|
122
128
|
private getKey;
|
|
123
129
|
}
|
|
124
130
|
|
|
125
|
-
declare class AuthManager<TRole extends string = string> implements IAuthManager {
|
|
131
|
+
declare class AuthManager<TRole extends string = string, TUser extends DatrixEntry = DatrixEntry> implements IAuthManager {
|
|
126
132
|
private readonly passwordManager;
|
|
127
133
|
private readonly jwtStrategy;
|
|
128
134
|
private readonly sessionStrategy;
|
|
129
135
|
private readonly config;
|
|
130
|
-
get authConfig(): AuthConfig<TRole>;
|
|
131
|
-
constructor(config: AuthConfig<TRole>);
|
|
136
|
+
get authConfig(): AuthConfig<TRole, TUser>;
|
|
137
|
+
constructor(config: AuthConfig<TRole, TUser>);
|
|
132
138
|
hashPassword(password: string): Promise<PasswordHash>;
|
|
133
139
|
verifyPassword(password: string, hash: string, salt: string): Promise<boolean>;
|
|
134
140
|
login(user: AuthUser, options?: {
|
|
@@ -158,7 +164,7 @@ interface ApiConfig<TRole extends string = string> extends Record<string, unknow
|
|
|
158
164
|
declare class ApiPlugin<TRole extends string = string> extends BasePlugin<ApiConfig<TRole>> implements IApiPlugin<TRole> {
|
|
159
165
|
readonly name = "api";
|
|
160
166
|
readonly version = "1.0.0";
|
|
161
|
-
authManager?: AuthManager
|
|
167
|
+
authManager?: AuthManager<TRole>;
|
|
162
168
|
user: AuthUser | null;
|
|
163
169
|
private datrixInstance?;
|
|
164
170
|
get datrix(): Datrix;
|
|
@@ -186,7 +192,7 @@ declare class ApiPlugin<TRole extends string = string> extends BasePlugin<ApiCon
|
|
|
186
192
|
private handleAuthRequest;
|
|
187
193
|
isEnabled(): boolean;
|
|
188
194
|
isAuthEnabled(): boolean;
|
|
189
|
-
getAuthManager(): AuthManager | undefined;
|
|
195
|
+
getAuthManager(): AuthManager<TRole> | undefined;
|
|
190
196
|
}
|
|
191
197
|
|
|
192
198
|
declare function handleRequest(datrix: Datrix, request: Request): Promise<Response>;
|
|
@@ -242,18 +248,20 @@ declare function serializeQuery<T extends DatrixEntry = DatrixEntry>(query: Pars
|
|
|
242
248
|
|
|
243
249
|
declare function handleCrudRequest<TRole extends string = string>(request: Request, datrix: Datrix, api: IApiPlugin<TRole>, options?: ContextBuilderOptions): Promise<Response>;
|
|
244
250
|
|
|
245
|
-
interface AuthHandlerConfig {
|
|
251
|
+
interface AuthHandlerConfig<TRole extends string = string, TUser extends DatrixEntry = DatrixEntry> {
|
|
246
252
|
readonly datrix: Datrix;
|
|
247
|
-
readonly authManager: AuthManager
|
|
248
|
-
readonly authConfig: AuthConfig
|
|
253
|
+
readonly authManager: AuthManager<TRole, TUser>;
|
|
254
|
+
readonly authConfig: AuthConfig<TRole, TUser>;
|
|
249
255
|
}
|
|
250
|
-
declare function createAuthHandlers(config: AuthHandlerConfig): {
|
|
256
|
+
declare function createAuthHandlers<TRole extends string = string, TUser extends DatrixEntry = DatrixEntry>(config: AuthHandlerConfig<TRole, TUser>): {
|
|
251
257
|
register: (request: Request) => Promise<Response>;
|
|
252
258
|
login: (request: Request) => Promise<Response>;
|
|
253
259
|
logout: (request: Request) => Promise<Response>;
|
|
254
260
|
me: (request: Request) => Promise<Response>;
|
|
261
|
+
forgotPassword: (request: Request) => Promise<Response>;
|
|
262
|
+
resetPassword: (request: Request) => Promise<Response>;
|
|
255
263
|
};
|
|
256
|
-
declare function createUnifiedAuthHandler(config: AuthHandlerConfig, apiPrefix?: string): (request: Request) => Promise<Response>;
|
|
264
|
+
declare function createUnifiedAuthHandler<TRole extends string = string, TUser extends DatrixEntry = DatrixEntry>(config: AuthHandlerConfig<TRole, TUser>, apiPrefix?: string): (request: Request) => Promise<Response>;
|
|
257
265
|
|
|
258
266
|
declare class DatrixApiError extends DatrixError {
|
|
259
267
|
status: number;
|
package/dist/index.js
CHANGED
|
@@ -1263,7 +1263,91 @@ function createAuthHandlers(config) {
|
|
|
1263
1263
|
);
|
|
1264
1264
|
}
|
|
1265
1265
|
}
|
|
1266
|
-
|
|
1266
|
+
async function forgotPassword(request) {
|
|
1267
|
+
try {
|
|
1268
|
+
const onForgotPassword = authConfig.passwordReset?.onForgotPassword;
|
|
1269
|
+
if (!onForgotPassword) {
|
|
1270
|
+
throw handlerError.permissionDenied("Password reset is not configured");
|
|
1271
|
+
}
|
|
1272
|
+
const body = await request.json();
|
|
1273
|
+
const { email } = body;
|
|
1274
|
+
if (!email || typeof email !== "string") {
|
|
1275
|
+
throw handlerError.invalidBody("Email is required");
|
|
1276
|
+
}
|
|
1277
|
+
const authRecord = await datrix.raw.findOne(
|
|
1278
|
+
authSchemaName,
|
|
1279
|
+
{ email },
|
|
1280
|
+
{
|
|
1281
|
+
populate: true,
|
|
1282
|
+
select: ["email", "role", "resetToken", "resetTokenExpiry"]
|
|
1283
|
+
}
|
|
1284
|
+
);
|
|
1285
|
+
if (!authRecord) {
|
|
1286
|
+
return jsonResponse({ data: { success: true } });
|
|
1287
|
+
}
|
|
1288
|
+
const tokenBytes = new Uint8Array(32);
|
|
1289
|
+
crypto.getRandomValues(tokenBytes);
|
|
1290
|
+
const token = Array.from(tokenBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1291
|
+
const expirySeconds = authConfig.passwordReset?.tokenExpirySeconds ?? import_core7.DEFAULT_API_AUTH_CONFIG.passwordReset.tokenExpirySeconds;
|
|
1292
|
+
const expiry = new Date(Date.now() + expirySeconds * 1e3);
|
|
1293
|
+
await datrix.raw.update(authSchemaName, authRecord.id, {
|
|
1294
|
+
resetToken: token,
|
|
1295
|
+
resetTokenExpiry: expiry
|
|
1296
|
+
});
|
|
1297
|
+
await onForgotPassword(authRecord, token);
|
|
1298
|
+
return jsonResponse({ data: { success: true } });
|
|
1299
|
+
} catch (error) {
|
|
1300
|
+
if (error instanceof import_core8.DatrixError) {
|
|
1301
|
+
return datrixErrorResponse(error);
|
|
1302
|
+
}
|
|
1303
|
+
const message = error instanceof Error ? error.message : "Internal server error";
|
|
1304
|
+
return datrixErrorResponse(
|
|
1305
|
+
handlerError.internalError(
|
|
1306
|
+
message,
|
|
1307
|
+
error instanceof Error ? error : void 0
|
|
1308
|
+
)
|
|
1309
|
+
);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
async function resetPassword(request) {
|
|
1313
|
+
try {
|
|
1314
|
+
const body = await request.json();
|
|
1315
|
+
const { token, password } = body;
|
|
1316
|
+
if (!token || typeof token !== "string") {
|
|
1317
|
+
throw handlerError.invalidBody("Token is required");
|
|
1318
|
+
}
|
|
1319
|
+
if (!password || typeof password !== "string") {
|
|
1320
|
+
throw handlerError.invalidBody("Password is required");
|
|
1321
|
+
}
|
|
1322
|
+
const authRecord = await datrix.raw.findOne(
|
|
1323
|
+
authSchemaName,
|
|
1324
|
+
{ resetToken: token }
|
|
1325
|
+
);
|
|
1326
|
+
if (!authRecord || !authRecord.resetTokenExpiry || new Date(authRecord.resetTokenExpiry) < /* @__PURE__ */ new Date()) {
|
|
1327
|
+
throw handlerError.invalidBody("Invalid or expired reset token");
|
|
1328
|
+
}
|
|
1329
|
+
const { hash, salt } = await authManager.hashPassword(password);
|
|
1330
|
+
await datrix.raw.update(authSchemaName, authRecord.id, {
|
|
1331
|
+
password: hash,
|
|
1332
|
+
passwordSalt: salt,
|
|
1333
|
+
resetToken: null,
|
|
1334
|
+
resetTokenExpiry: null
|
|
1335
|
+
});
|
|
1336
|
+
return jsonResponse({ data: { success: true } });
|
|
1337
|
+
} catch (error) {
|
|
1338
|
+
if (error instanceof import_core8.DatrixError) {
|
|
1339
|
+
return datrixErrorResponse(error);
|
|
1340
|
+
}
|
|
1341
|
+
const message = error instanceof Error ? error.message : "Internal server error";
|
|
1342
|
+
return datrixErrorResponse(
|
|
1343
|
+
handlerError.internalError(
|
|
1344
|
+
message,
|
|
1345
|
+
error instanceof Error ? error : void 0
|
|
1346
|
+
)
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return { register, login, logout, me, forgotPassword, resetPassword };
|
|
1267
1351
|
}
|
|
1268
1352
|
function createUnifiedAuthHandler(config, apiPrefix = "/api") {
|
|
1269
1353
|
const handlers = createAuthHandlers(config);
|
|
@@ -1272,7 +1356,9 @@ function createUnifiedAuthHandler(config, apiPrefix = "/api") {
|
|
|
1272
1356
|
register: authConfig.endpoints?.register ?? import_core7.DEFAULT_API_AUTH_CONFIG.endpoints.register,
|
|
1273
1357
|
login: authConfig.endpoints?.login ?? import_core7.DEFAULT_API_AUTH_CONFIG.endpoints.login,
|
|
1274
1358
|
logout: authConfig.endpoints?.logout ?? import_core7.DEFAULT_API_AUTH_CONFIG.endpoints.logout,
|
|
1275
|
-
me: authConfig.endpoints?.me ?? import_core7.DEFAULT_API_AUTH_CONFIG.endpoints.me
|
|
1359
|
+
me: authConfig.endpoints?.me ?? import_core7.DEFAULT_API_AUTH_CONFIG.endpoints.me,
|
|
1360
|
+
forgotPassword: authConfig.endpoints?.forgotPassword ?? import_core7.DEFAULT_API_AUTH_CONFIG.endpoints.forgotPassword,
|
|
1361
|
+
resetPassword: authConfig.endpoints?.resetPassword ?? import_core7.DEFAULT_API_AUTH_CONFIG.endpoints.resetPassword
|
|
1276
1362
|
};
|
|
1277
1363
|
return async function authHandler(request) {
|
|
1278
1364
|
const url = new URL(request.url);
|
|
@@ -1290,6 +1376,12 @@ function createUnifiedAuthHandler(config, apiPrefix = "/api") {
|
|
|
1290
1376
|
if (path === endpoints.me && method === "GET") {
|
|
1291
1377
|
return handlers.me(request);
|
|
1292
1378
|
}
|
|
1379
|
+
if (path === endpoints.forgotPassword && method === "POST") {
|
|
1380
|
+
return handlers.forgotPassword(request);
|
|
1381
|
+
}
|
|
1382
|
+
if (path === endpoints.resetPassword && method === "POST") {
|
|
1383
|
+
return handlers.resetPassword(request);
|
|
1384
|
+
}
|
|
1293
1385
|
return datrixErrorResponse(
|
|
1294
1386
|
handlerError.recordNotFound("Auth Route", url.pathname)
|
|
1295
1387
|
);
|