@simple-auth-kit/cli 1.4.3 → 1.5.1
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/lib/copy.ts +1 -1
- package/package.json +1 -1
- package/registry/combos/express-drizzle/shared/src/{auth-core-error.middleware.ts → middleware/auth-core-error.middleware.ts} +17 -4
- package/registry/combos/express-drizzle/shared/src/{auth.middleware.ts → middleware/auth.middleware.ts} +23 -7
- package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/middleware}/response-envelope.middleware.ts +15 -3
- package/registry/combos/express-drizzle/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +32 -9
- package/registry/combos/express-drizzle/shared/src/repositories/password-reset.repository.ts +59 -0
- package/registry/combos/express-drizzle/shared/src/{session.repository.ts → repositories/session.repository.ts} +52 -14
- package/registry/combos/express-drizzle/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +20 -6
- package/registry/combos/express-drizzle/shared/src/request-context.ts +1 -1
- package/registry/combos/express-drizzle/shared/src/routers/auth.router.ts +395 -0
- package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +52 -19
- package/registry/combos/express-drizzle/variants/base/src/{authz.middleware.ts → middleware/authz.middleware.ts} +15 -7
- package/registry/combos/express-drizzle/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +28 -10
- package/registry/combos/express-drizzle/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +239 -48
- package/registry/combos/express-drizzle/variants/base/src/routers/admin.router.ts +497 -0
- package/registry/combos/express-drizzle/variants/base/src/{auth.service.ts → services/auth.service.ts} +480 -109
- package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +69 -23
- package/registry/combos/express-drizzle/variants/workspaces/src/{authz.middleware.ts → middleware/authz.middleware.ts} +32 -12
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +147 -28
- package/registry/combos/express-drizzle/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +30 -11
- package/registry/combos/express-drizzle/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +319 -70
- package/registry/combos/express-drizzle/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +127 -30
- package/registry/combos/express-drizzle/variants/workspaces/src/routers/admin.router.ts +534 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/routers/workspace.router.ts +194 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/{auth.service.ts → services/auth.service.ts} +544 -122
- package/registry/combos/express-prisma/shared/src/{auth-core-error.middleware.ts → middleware/auth-core-error.middleware.ts} +17 -4
- package/registry/combos/express-prisma/shared/src/{auth.middleware.ts → middleware/auth.middleware.ts} +19 -6
- package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/middleware}/response-envelope.middleware.ts +15 -3
- package/registry/combos/express-prisma/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +27 -9
- package/registry/combos/express-prisma/shared/src/repositories/password-reset.repository.ts +55 -0
- package/registry/combos/express-prisma/shared/src/{session.repository.ts → repositories/session.repository.ts} +30 -9
- package/registry/combos/express-prisma/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +11 -4
- package/registry/combos/express-prisma/shared/src/request-context.ts +1 -1
- package/registry/combos/express-prisma/shared/src/routers/auth.router.ts +398 -0
- package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +56 -21
- package/registry/combos/express-prisma/variants/base/src/{authz.middleware.ts → middleware/authz.middleware.ts} +15 -7
- package/registry/combos/express-prisma/variants/base/src/rbac.defaults.ts +66 -18
- package/registry/combos/express-prisma/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/express-prisma/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +199 -48
- package/registry/combos/express-prisma/variants/base/src/routers/admin.router.ts +508 -0
- package/registry/combos/express-prisma/variants/base/src/seed.ts +77 -19
- package/registry/combos/express-prisma/variants/base/src/{auth.service.ts → services/auth.service.ts} +454 -105
- package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +80 -25
- package/registry/combos/express-prisma/variants/workspaces/src/{authz.middleware.ts → middleware/authz.middleware.ts} +32 -12
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +147 -28
- package/registry/combos/express-prisma/variants/workspaces/src/rbac.defaults.ts +72 -20
- package/registry/combos/express-prisma/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/express-prisma/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +272 -64
- package/registry/combos/express-prisma/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +86 -23
- package/registry/combos/express-prisma/variants/workspaces/src/routers/admin.router.ts +545 -0
- package/registry/combos/express-prisma/variants/workspaces/src/routers/workspace.router.ts +194 -0
- package/registry/combos/express-prisma/variants/workspaces/src/seed.ts +108 -28
- package/registry/combos/express-prisma/variants/workspaces/src/{auth.service.ts → services/auth.service.ts} +518 -119
- package/registry/combos/nestjs-drizzle/shared/src/{auth.controller.ts → controllers/auth.controller.ts} +6 -6
- package/registry/combos/nestjs-drizzle/shared/src/{auth-core-error.filter.ts → filters/auth-core-error.filter.ts} +29 -5
- package/registry/combos/nestjs-drizzle/shared/src/{ability.guard.ts → guards/ability.guard.ts} +20 -7
- package/registry/combos/nestjs-drizzle/shared/src/{auth.guard.ts → guards/auth.guard.ts} +19 -7
- package/registry/combos/{nestjs-prisma/shared/src → nestjs-drizzle/shared/src/interceptors}/response.interceptor.ts +10 -2
- package/registry/combos/nestjs-drizzle/shared/src/repositories/oauth.repository.ts +63 -0
- package/registry/combos/nestjs-drizzle/shared/src/repositories/password-reset.repository.ts +59 -0
- package/registry/combos/nestjs-drizzle/shared/src/{session.repository.ts → repositories/session.repository.ts} +52 -14
- package/registry/combos/nestjs-drizzle/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +21 -7
- package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +1 -1
- package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +1097 -307
- package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +44 -20
- package/registry/combos/nestjs-drizzle/variants/base/src/{admin.controller.ts → controllers/admin.controller.ts} +228 -62
- package/registry/combos/nestjs-drizzle/variants/base/src/{authz.guard.ts → guards/authz.guard.ts} +12 -5
- package/registry/combos/nestjs-drizzle/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +27 -10
- package/registry/combos/nestjs-drizzle/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +249 -51
- package/registry/combos/nestjs-drizzle/variants/base/src/{auth.service.ts → services/auth.service.ts} +499 -116
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +48 -22
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{admin.controller.ts → controllers/admin.controller.ts} +269 -70
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{workspace.controller.ts → controllers/workspace.controller.ts} +93 -26
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{authz.guard.ts → guards/authz.guard.ts} +26 -8
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +29 -11
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +323 -71
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +131 -30
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/services/auth.service.ts +988 -0
- package/registry/combos/nestjs-prisma/shared/src/{auth.controller.ts → controllers/auth.controller.ts} +6 -6
- package/registry/combos/nestjs-prisma/shared/src/{auth-core-error.filter.ts → filters/auth-core-error.filter.ts} +29 -5
- package/registry/combos/nestjs-prisma/shared/src/{ability.guard.ts → guards/ability.guard.ts} +20 -7
- package/registry/combos/nestjs-prisma/shared/src/{auth.guard.ts → guards/auth.guard.ts} +19 -7
- package/registry/combos/{nestjs-drizzle/shared/src → nestjs-prisma/shared/src/interceptors}/response.interceptor.ts +10 -2
- package/registry/combos/nestjs-prisma/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +25 -8
- package/registry/combos/nestjs-prisma/shared/src/repositories/password-reset.repository.ts +55 -0
- package/registry/combos/nestjs-prisma/shared/src/{session.repository.ts → repositories/session.repository.ts} +30 -9
- package/registry/combos/nestjs-prisma/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +11 -4
- package/registry/combos/nestjs-prisma/shared/src/request-context.ts +1 -1
- package/registry/combos/nestjs-prisma/test/prove-cycle.ts +1097 -307
- package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +58 -27
- package/registry/combos/nestjs-prisma/variants/base/src/{admin.controller.ts → controllers/admin.controller.ts} +469 -110
- package/registry/combos/nestjs-prisma/variants/base/src/{audit-log.gateway.ts → gateways/audit-log.gateway.ts} +22 -6
- package/registry/combos/nestjs-prisma/variants/base/src/{authz.guard.ts → guards/authz.guard.ts} +12 -5
- package/registry/combos/nestjs-prisma/variants/base/src/rbac.defaults.ts +97 -26
- package/registry/combos/nestjs-prisma/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/nestjs-prisma/variants/base/src/{country.repository.ts → repositories/country.repository.ts} +98 -25
- package/registry/combos/nestjs-prisma/variants/base/src/{customer.repository.ts → repositories/customer.repository.ts} +106 -27
- package/registry/combos/nestjs-prisma/variants/base/src/{language.repository.ts → repositories/language.repository.ts} +93 -25
- package/registry/combos/nestjs-prisma/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +229 -55
- package/registry/combos/nestjs-prisma/variants/base/src/seed.ts +77 -19
- package/registry/combos/nestjs-prisma/variants/base/src/services/auth.service.ts +1064 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +54 -26
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{admin.controller.ts → controllers/admin.controller.ts} +544 -128
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{workspace.controller.ts → controllers/workspace.controller.ts} +93 -26
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{authz.guard.ts → guards/authz.guard.ts} +26 -8
- package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.defaults.ts +103 -28
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{country.repository.ts → repositories/country.repository.ts} +127 -26
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{customer.repository.ts → repositories/customer.repository.ts} +137 -28
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/language.repository.ts +243 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +314 -74
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +94 -24
- package/registry/combos/nestjs-prisma/variants/workspaces/src/seed.ts +108 -28
- package/registry/combos/nestjs-prisma/variants/workspaces/src/services/auth.service.ts +961 -0
- package/registry.json +4 -4
- package/simple-auth-kit.ts +42 -11
- package/registry/combos/express-drizzle/shared/src/auth.router.ts +0 -266
- package/registry/combos/express-drizzle/shared/src/password-reset.repository.ts +0 -35
- package/registry/combos/express-drizzle/variants/base/src/admin.router.ts +0 -307
- package/registry/combos/express-drizzle/variants/workspaces/src/admin.router.ts +0 -323
- package/registry/combos/express-drizzle/variants/workspaces/src/workspace.router.ts +0 -123
- package/registry/combos/express-prisma/shared/src/auth.router.ts +0 -269
- package/registry/combos/express-prisma/shared/src/password-reset.repository.ts +0 -29
- package/registry/combos/express-prisma/variants/base/src/admin.router.ts +0 -314
- package/registry/combos/express-prisma/variants/workspaces/src/admin.router.ts +0 -330
- package/registry/combos/express-prisma/variants/workspaces/src/workspace.router.ts +0 -123
- package/registry/combos/nestjs-drizzle/shared/src/oauth.repository.ts +0 -36
- package/registry/combos/nestjs-drizzle/shared/src/password-reset.repository.ts +0 -29
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.service.ts +0 -553
- package/registry/combos/nestjs-prisma/shared/src/password-reset.repository.ts +0 -29
- package/registry/combos/nestjs-prisma/variants/base/src/auth.service.ts +0 -618
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.service.ts +0 -509
- package/registry/combos/nestjs-prisma/variants/workspaces/src/language.repository.ts +0 -143
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { NextFunction, Request, Response } from "express";
|
|
2
2
|
import { AuthCoreError } from "@/lib/auth/core/types.js";
|
|
3
|
-
import { HttpError } from "
|
|
3
|
+
import { HttpError } from "../http-error.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Replaces the reference combo's `AuthCoreErrorFilter` (a Nest global `@Catch` exception
|
|
@@ -11,19 +11,32 @@ import { HttpError } from "./http-error.js";
|
|
|
11
11
|
* maps this combo's own `HttpError` to its intended status code — the plain-Express stand-in
|
|
12
12
|
* for the NestJS exception classes the reference throws.
|
|
13
13
|
*/
|
|
14
|
-
export function authCoreErrorMiddleware(
|
|
14
|
+
export function authCoreErrorMiddleware(
|
|
15
|
+
err: unknown,
|
|
16
|
+
_req: Request,
|
|
17
|
+
res: Response,
|
|
18
|
+
next: NextFunction,
|
|
19
|
+
): void {
|
|
15
20
|
if (res.headersSent) {
|
|
16
21
|
next(err);
|
|
17
22
|
return;
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
if (err instanceof AuthCoreError) {
|
|
21
|
-
res
|
|
26
|
+
res
|
|
27
|
+
.status(401)
|
|
28
|
+
.json({ statusCode: 401, code: err.code, message: err.message });
|
|
22
29
|
return;
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
if (err instanceof HttpError) {
|
|
26
|
-
res
|
|
33
|
+
res
|
|
34
|
+
.status(err.statusCode)
|
|
35
|
+
.json({
|
|
36
|
+
statusCode: err.statusCode,
|
|
37
|
+
code: err.name,
|
|
38
|
+
message: err.message,
|
|
39
|
+
});
|
|
27
40
|
return;
|
|
28
41
|
}
|
|
29
42
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { NextFunction, Request, Response } from "express";
|
|
2
2
|
import { verifyAccessToken } from "@/lib/auth/core/token-service.js";
|
|
3
|
-
import { KeyProviderService } from "
|
|
4
|
-
import "
|
|
5
|
-
import { SessionRepository } from "
|
|
3
|
+
import { KeyProviderService } from "../key-provider.js";
|
|
4
|
+
import "../request-context.js";
|
|
5
|
+
import { SessionRepository } from "../repositories/session.repository.js";
|
|
6
6
|
|
|
7
7
|
export interface AuthMiddlewareDeps {
|
|
8
8
|
keys: KeyProviderService;
|
|
@@ -19,16 +19,29 @@ export interface AuthMiddlewareDeps {
|
|
|
19
19
|
* is the idiomatic equivalent.
|
|
20
20
|
*/
|
|
21
21
|
export function createAuthMiddleware(deps: AuthMiddlewareDeps) {
|
|
22
|
-
return async function authMiddleware(
|
|
22
|
+
return async function authMiddleware(
|
|
23
|
+
req: Request,
|
|
24
|
+
res: Response,
|
|
25
|
+
next: NextFunction,
|
|
26
|
+
): Promise<void> {
|
|
23
27
|
const header = req.headers["authorization"];
|
|
24
28
|
if (!header?.startsWith("Bearer ")) {
|
|
25
|
-
res
|
|
29
|
+
res
|
|
30
|
+
.status(401)
|
|
31
|
+
.json({
|
|
32
|
+
statusCode: 401,
|
|
33
|
+
code: "UNAUTHORIZED",
|
|
34
|
+
message: "missing bearer token",
|
|
35
|
+
});
|
|
26
36
|
return;
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
try {
|
|
30
40
|
req.auth = await verifyAccessToken(
|
|
31
|
-
{
|
|
41
|
+
{
|
|
42
|
+
secret: deps.keys.secret,
|
|
43
|
+
isDenylisted: (jti) => deps.sessions.isDenylisted(jti),
|
|
44
|
+
},
|
|
32
45
|
header.slice("Bearer ".length),
|
|
33
46
|
);
|
|
34
47
|
} catch (err) {
|
|
@@ -36,10 +36,22 @@ export function responseEnvelope(): RequestHandler {
|
|
|
36
36
|
const originalJson = res.json.bind(res);
|
|
37
37
|
res.json = ((body: unknown) => {
|
|
38
38
|
if (res.statusCode >= 400) {
|
|
39
|
-
const shaped =
|
|
40
|
-
|
|
39
|
+
const shaped =
|
|
40
|
+
body && typeof body === "object"
|
|
41
|
+
? (body as Record<string, unknown>)
|
|
42
|
+
: { message: body };
|
|
43
|
+
return originalJson({
|
|
44
|
+
success: false,
|
|
45
|
+
statusCode: res.statusCode,
|
|
46
|
+
...shaped,
|
|
47
|
+
});
|
|
41
48
|
}
|
|
42
|
-
return originalJson({
|
|
49
|
+
return originalJson({
|
|
50
|
+
success: true,
|
|
51
|
+
statusCode: res.statusCode,
|
|
52
|
+
message: deriveMessage(req.method),
|
|
53
|
+
data: body,
|
|
54
|
+
});
|
|
43
55
|
}) as typeof res.json;
|
|
44
56
|
next();
|
|
45
57
|
};
|
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
import type { OAuthStoreDeps } from "@/lib/auth/core/oauth.js";
|
|
2
|
-
import { PrismaClient } from "
|
|
3
|
-
import { HttpError } from "
|
|
4
|
-
import { toId } from "
|
|
2
|
+
import { PrismaClient } from "../../generated/prisma/client.js";
|
|
3
|
+
import { HttpError } from "../http-error.js";
|
|
4
|
+
import { toId } from "../id.helper.js";
|
|
5
5
|
|
|
6
6
|
// Plain class, no DI container — constructed directly with a PrismaClient in
|
|
7
7
|
// create-auth-app.ts. Identical Prisma queries to the reference combo.
|
|
8
8
|
export class OAuthRepository implements OAuthStoreDeps {
|
|
9
9
|
constructor(private readonly prisma: PrismaClient) {}
|
|
10
10
|
|
|
11
|
-
async findAccountByProvider(
|
|
11
|
+
async findAccountByProvider(
|
|
12
|
+
provider: string,
|
|
13
|
+
providerAccountId: string,
|
|
14
|
+
): Promise<{ userId: string } | null> {
|
|
12
15
|
const row = await this.prisma.oAuthAccount.findUnique({
|
|
13
16
|
where: { provider_providerAccountId: { provider, providerAccountId } },
|
|
14
17
|
});
|
|
15
18
|
return row ? { userId: row.userId.toString() } : null;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
async linkAccount(input: {
|
|
19
|
-
|
|
21
|
+
async linkAccount(input: {
|
|
22
|
+
userId: string;
|
|
23
|
+
provider: string;
|
|
24
|
+
providerAccountId: string;
|
|
25
|
+
email?: string;
|
|
26
|
+
}): Promise<void> {
|
|
27
|
+
await this.prisma.oAuthAccount.create({
|
|
28
|
+
data: { ...input, userId: toId(input.userId) },
|
|
29
|
+
});
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
async findUserByVerifiedEmail(email: string): Promise<{ id: string } | null> {
|
|
@@ -25,9 +35,17 @@ export class OAuthRepository implements OAuthStoreDeps {
|
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
/** OAuth-only signup: no password is ever set, which is why `User.passwordHash` is nullable. */
|
|
28
|
-
async createUserFromOAuth(input: {
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
async createUserFromOAuth(input: {
|
|
39
|
+
email?: string;
|
|
40
|
+
}): Promise<{ id: string }> {
|
|
41
|
+
if (!input.email)
|
|
42
|
+
throw new HttpError(
|
|
43
|
+
400,
|
|
44
|
+
"OAuth provider did not return an email address",
|
|
45
|
+
);
|
|
46
|
+
const row = await this.prisma.user.create({
|
|
47
|
+
data: { email: input.email, passwordHash: null },
|
|
48
|
+
});
|
|
31
49
|
return { id: row.id.toString() };
|
|
32
50
|
}
|
|
33
51
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { PasswordResetStoreDeps } from "@/lib/auth/core/password-reset.js";
|
|
2
|
+
import { PrismaClient } from "../../generated/prisma/client.js";
|
|
3
|
+
import { toId } from "../id.helper.js";
|
|
4
|
+
|
|
5
|
+
// Plain class, no DI container — constructed directly with a PrismaClient in
|
|
6
|
+
// create-auth-app.ts. Identical Prisma queries to the reference combo.
|
|
7
|
+
export class PasswordResetRepository implements PasswordResetStoreDeps {
|
|
8
|
+
constructor(private readonly prisma: PrismaClient) {}
|
|
9
|
+
|
|
10
|
+
async saveResetToken(input: {
|
|
11
|
+
userId: string;
|
|
12
|
+
tokenHash: string;
|
|
13
|
+
expiresAt: string;
|
|
14
|
+
}): Promise<void> {
|
|
15
|
+
await this.prisma.passwordResetToken.create({
|
|
16
|
+
data: {
|
|
17
|
+
userId: toId(input.userId),
|
|
18
|
+
tokenHash: input.tokenHash,
|
|
19
|
+
expiresAt: new Date(input.expiresAt),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async findValidResetToken(
|
|
25
|
+
tokenHash: string,
|
|
26
|
+
): Promise<{
|
|
27
|
+
userId: string;
|
|
28
|
+
expiresAt: string;
|
|
29
|
+
consumedAt: string | null;
|
|
30
|
+
} | null> {
|
|
31
|
+
const row = await this.prisma.passwordResetToken.findUnique({
|
|
32
|
+
where: { tokenHash },
|
|
33
|
+
});
|
|
34
|
+
if (!row) return null;
|
|
35
|
+
return {
|
|
36
|
+
userId: row.userId.toString(),
|
|
37
|
+
expiresAt: row.expiresAt.toISOString(),
|
|
38
|
+
consumedAt: row.consumedAt?.toISOString() ?? null,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async consumeResetToken(tokenHash: string): Promise<void> {
|
|
43
|
+
await this.prisma.passwordResetToken.update({
|
|
44
|
+
where: { tokenHash },
|
|
45
|
+
data: { consumedAt: new Date() },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async setPasswordHash(userId: string, passwordHash: string): Promise<void> {
|
|
50
|
+
await this.prisma.user.update({
|
|
51
|
+
where: { id: toId(userId) },
|
|
52
|
+
data: { passwordHash },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import type { SessionStoreDeps } from "@/lib/auth/core/session-policy.js";
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import type {
|
|
4
|
+
AuditEvent,
|
|
5
|
+
Revoker,
|
|
6
|
+
SessionRecord,
|
|
7
|
+
} from "@/lib/auth/core/types.js";
|
|
8
|
+
import { PrismaClient } from "../../generated/prisma/client.js";
|
|
9
|
+
import type { AuthConfig } from "../auth.config.js";
|
|
6
10
|
import { AuditLogRepository } from "./audit-log.repository.js";
|
|
7
|
-
import { toId, toIdOrNull } from "
|
|
11
|
+
import { toId, toIdOrNull } from "../id.helper.js";
|
|
8
12
|
|
|
9
13
|
function toSessionRecord(row: {
|
|
10
14
|
id: bigint;
|
|
@@ -64,7 +68,9 @@ export class SessionRepository implements SessionStoreDeps {
|
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
async findSession(id: string): Promise<SessionRecord | null> {
|
|
67
|
-
const row = await this.prisma.session.findUnique({
|
|
71
|
+
const row = await this.prisma.session.findUnique({
|
|
72
|
+
where: { id: toId(id) },
|
|
73
|
+
});
|
|
68
74
|
return row ? toSessionRecord(row) : null;
|
|
69
75
|
}
|
|
70
76
|
|
|
@@ -92,7 +98,12 @@ export class SessionRepository implements SessionStoreDeps {
|
|
|
92
98
|
* `createdBy` is the user themselves. A session is only ever created by the person logging in,
|
|
93
99
|
* so recording it costs nothing and leaves the column meaningful rather than always null.
|
|
94
100
|
*/
|
|
95
|
-
async createSession(input: {
|
|
101
|
+
async createSession(input: {
|
|
102
|
+
userId: string;
|
|
103
|
+
userAgent?: string;
|
|
104
|
+
ip?: string;
|
|
105
|
+
provider?: string;
|
|
106
|
+
}): Promise<SessionRecord> {
|
|
96
107
|
const userIdBig = toId(input.userId);
|
|
97
108
|
const row = await this.prisma.session.create({
|
|
98
109
|
data: {
|
|
@@ -116,9 +127,17 @@ export class SessionRepository implements SessionStoreDeps {
|
|
|
116
127
|
});
|
|
117
128
|
}
|
|
118
129
|
|
|
119
|
-
async revokeAllByUserExcept(
|
|
130
|
+
async revokeAllByUserExcept(
|
|
131
|
+
userId: string,
|
|
132
|
+
keepSessionId: string,
|
|
133
|
+
revoker?: Revoker,
|
|
134
|
+
): Promise<void> {
|
|
120
135
|
await this.prisma.session.updateMany({
|
|
121
|
-
where: {
|
|
136
|
+
where: {
|
|
137
|
+
userId: toId(userId),
|
|
138
|
+
id: { not: toId(keepSessionId) },
|
|
139
|
+
revokedAt: null,
|
|
140
|
+
},
|
|
122
141
|
data: { ...revocationFields(revoker), sessionVersion: { increment: 1 } },
|
|
123
142
|
});
|
|
124
143
|
}
|
|
@@ -132,7 +151,9 @@ export class SessionRepository implements SessionStoreDeps {
|
|
|
132
151
|
}
|
|
133
152
|
|
|
134
153
|
async isDenylisted(jti: string): Promise<boolean> {
|
|
135
|
-
const row = await this.prisma.denylistedAccessToken.findUnique({
|
|
154
|
+
const row = await this.prisma.denylistedAccessToken.findUnique({
|
|
155
|
+
where: { jti },
|
|
156
|
+
});
|
|
136
157
|
if (!row) return false;
|
|
137
158
|
if (row.expiresAt.getTime() < Date.now()) return false; // expired; a cleanup job reaps these, see Phase 6
|
|
138
159
|
return true;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { verifyBackupCode } from "@/lib/auth/core/two-factor.js";
|
|
2
|
-
import { PrismaClient } from "
|
|
2
|
+
import { PrismaClient } from "../../generated/prisma/client.js";
|
|
3
3
|
|
|
4
4
|
// Plain class, no DI container — constructed directly with a PrismaClient in
|
|
5
5
|
// create-auth-app.ts. Identical Prisma queries to the reference combo.
|
|
@@ -12,15 +12,22 @@ export class TwoFactorRepository {
|
|
|
12
12
|
/** Replaces any prior set — re-confirming enrollment invalidates old backup codes. */
|
|
13
13
|
async saveBackupCodes(userId: bigint, hashes: string[]): Promise<void> {
|
|
14
14
|
await this.prisma.twoFactorBackupCode.deleteMany({ where: { userId } });
|
|
15
|
-
await this.prisma.twoFactorBackupCode.createMany({
|
|
15
|
+
await this.prisma.twoFactorBackupCode.createMany({
|
|
16
|
+
data: hashes.map((codeHash) => ({ userId, codeHash })),
|
|
17
|
+
});
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
async consumeBackupCode(userId: bigint, code: string): Promise<boolean> {
|
|
19
|
-
const unused = await this.prisma.twoFactorBackupCode.findMany({
|
|
21
|
+
const unused = await this.prisma.twoFactorBackupCode.findMany({
|
|
22
|
+
where: { userId, usedAt: null },
|
|
23
|
+
});
|
|
20
24
|
const match = unused.find((row) => verifyBackupCode(code, row.codeHash));
|
|
21
25
|
if (!match) return false;
|
|
22
26
|
|
|
23
|
-
await this.prisma.twoFactorBackupCode.update({
|
|
27
|
+
await this.prisma.twoFactorBackupCode.update({
|
|
28
|
+
where: { id: match.id },
|
|
29
|
+
data: { usedAt: new Date() },
|
|
30
|
+
});
|
|
24
31
|
return true;
|
|
25
32
|
}
|
|
26
33
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// `declare module "express"`.
|
|
21
21
|
import type { AccessTokenClaims } from "@/lib/auth/core/types.js";
|
|
22
22
|
import type { AppAbility } from "./ability.js";
|
|
23
|
-
import type { AuthzContext } from "./authz.middleware.js";
|
|
23
|
+
import type { AuthzContext } from "./middleware/authz.middleware.js";
|
|
24
24
|
|
|
25
25
|
declare global {
|
|
26
26
|
// Express's own declaration-merging style for augmenting its Request type requires a
|