@shipfox/api-auth 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +116 -0
- package/LICENSE +21 -0
- package/README.md +336 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +95 -0
- package/dist/config.js.map +1 -0
- package/dist/core/auth.d.ts +108 -0
- package/dist/core/auth.d.ts.map +1 -0
- package/dist/core/auth.js +476 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/entities/email-verification.d.ts +9 -0
- package/dist/core/entities/email-verification.d.ts.map +1 -0
- package/dist/core/entities/email-verification.js +3 -0
- package/dist/core/entities/email-verification.js.map +1 -0
- package/dist/core/entities/password-reset.d.ts +9 -0
- package/dist/core/entities/password-reset.d.ts.map +1 -0
- package/dist/core/entities/password-reset.js +3 -0
- package/dist/core/entities/password-reset.js.map +1 -0
- package/dist/core/entities/refresh-token.d.ts +12 -0
- package/dist/core/entities/refresh-token.d.ts.map +1 -0
- package/dist/core/entities/refresh-token.js +3 -0
- package/dist/core/entities/refresh-token.js.map +1 -0
- package/dist/core/entities/user.d.ts +12 -0
- package/dist/core/entities/user.d.ts.map +1 -0
- package/dist/core/entities/user.js +3 -0
- package/dist/core/entities/user.js.map +1 -0
- package/dist/core/errors.d.ts +54 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +106 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/job-lease-token.d.ts +12 -0
- package/dist/core/job-lease-token.d.ts.map +1 -0
- package/dist/core/job-lease-token.js +58 -0
- package/dist/core/job-lease-token.js.map +1 -0
- package/dist/core/jwt.d.ts +37 -0
- package/dist/core/jwt.d.ts.map +1 -0
- package/dist/core/jwt.js +46 -0
- package/dist/core/jwt.js.map +1 -0
- package/dist/core/password.d.ts +10 -0
- package/dist/core/password.d.ts.map +1 -0
- package/dist/core/password.js +13 -0
- package/dist/core/password.js.map +1 -0
- package/dist/core/rate-limit.d.ts +34 -0
- package/dist/core/rate-limit.d.ts.map +1 -0
- package/dist/core/rate-limit.js +76 -0
- package/dist/core/rate-limit.js.map +1 -0
- package/dist/core/runner-session-token.d.ts +6 -0
- package/dist/core/runner-session-token.d.ts.map +1 -0
- package/dist/core/runner-session-token.js +37 -0
- package/dist/core/runner-session-token.js.map +1 -0
- package/dist/db/db.d.ts +1723 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +28 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/email-verifications.d.ts +39 -0
- package/dist/db/email-verifications.d.ts.map +1 -0
- package/dist/db/email-verifications.js +95 -0
- package/dist/db/email-verifications.js.map +1 -0
- package/dist/db/migrations.d.ts +2 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +5 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/password-resets.d.ts +23 -0
- package/dist/db/password-resets.d.ts.map +1 -0
- package/dist/db/password-resets.js +37 -0
- package/dist/db/password-resets.js.map +1 -0
- package/dist/db/rate-limits.d.ts +18 -0
- package/dist/db/rate-limits.d.ts.map +1 -0
- package/dist/db/rate-limits.js +44 -0
- package/dist/db/rate-limits.js.map +1 -0
- package/dist/db/refresh-tokens.d.ts +44 -0
- package/dist/db/refresh-tokens.d.ts.map +1 -0
- package/dist/db/refresh-tokens.js +71 -0
- package/dist/db/refresh-tokens.js.map +1 -0
- package/dist/db/schema/common.d.ts +2 -0
- package/dist/db/schema/common.d.ts.map +1 -0
- package/dist/db/schema/common.js +4 -0
- package/dist/db/schema/common.js.map +1 -0
- package/dist/db/schema/email-verifications.d.ts +114 -0
- package/dist/db/schema/email-verifications.d.ts.map +1 -0
- package/dist/db/schema/email-verifications.js +35 -0
- package/dist/db/schema/email-verifications.js.map +1 -0
- package/dist/db/schema/outbox.d.ts +195 -0
- package/dist/db/schema/outbox.d.ts.map +1 -0
- package/dist/db/schema/outbox.js +5 -0
- package/dist/db/schema/outbox.js.map +1 -0
- package/dist/db/schema/password-resets.d.ts +114 -0
- package/dist/db/schema/password-resets.d.ts.map +1 -0
- package/dist/db/schema/password-resets.js +35 -0
- package/dist/db/schema/password-resets.js.map +1 -0
- package/dist/db/schema/rate-limits.d.ts +145 -0
- package/dist/db/schema/rate-limits.d.ts.map +1 -0
- package/dist/db/schema/rate-limits.js +25 -0
- package/dist/db/schema/rate-limits.js.map +1 -0
- package/dist/db/schema/refresh-tokens.d.ts +165 -0
- package/dist/db/schema/refresh-tokens.d.ts.map +1 -0
- package/dist/db/schema/refresh-tokens.js +47 -0
- package/dist/db/schema/refresh-tokens.js.map +1 -0
- package/dist/db/schema/users.d.ts +149 -0
- package/dist/db/schema/users.d.ts.map +1 -0
- package/dist/db/schema/users.js +40 -0
- package/dist/db/schema/users.js.map +1 -0
- package/dist/db/users.d.ts +28 -0
- package/dist/db/users.d.ts.map +1 -0
- package/dist/db/users.js +83 -0
- package/dist/db/users.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/index.d.ts +2 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +3 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/instance.d.ts +16 -0
- package/dist/metrics/instance.d.ts.map +1 -0
- package/dist/metrics/instance.js +52 -0
- package/dist/metrics/instance.js.map +1 -0
- package/dist/presentation/auth/bearer-token-auth.d.ts +15 -0
- package/dist/presentation/auth/bearer-token-auth.d.ts.map +1 -0
- package/dist/presentation/auth/bearer-token-auth.js +31 -0
- package/dist/presentation/auth/bearer-token-auth.js.map +1 -0
- package/dist/presentation/auth/jwt-auth.d.ts +10 -0
- package/dist/presentation/auth/jwt-auth.d.ts.map +1 -0
- package/dist/presentation/auth/jwt-auth.js +31 -0
- package/dist/presentation/auth/jwt-auth.js.map +1 -0
- package/dist/presentation/auth/lease-token-auth.d.ts +15 -0
- package/dist/presentation/auth/lease-token-auth.d.ts.map +1 -0
- package/dist/presentation/auth/lease-token-auth.js +27 -0
- package/dist/presentation/auth/lease-token-auth.js.map +1 -0
- package/dist/presentation/auth/refresh-cookie.d.ts +7 -0
- package/dist/presentation/auth/refresh-cookie.d.ts.map +1 -0
- package/dist/presentation/auth/refresh-cookie.js +29 -0
- package/dist/presentation/auth/refresh-cookie.js.map +1 -0
- package/dist/presentation/auth/runner-session-auth.d.ts +3 -0
- package/dist/presentation/auth/runner-session-auth.d.ts.map +1 -0
- package/dist/presentation/auth/runner-session-auth.js +16 -0
- package/dist/presentation/auth/runner-session-auth.js.map +1 -0
- package/dist/presentation/dto/user.d.ts +8 -0
- package/dist/presentation/dto/user.d.ts.map +1 -0
- package/dist/presentation/dto/user.js +19 -0
- package/dist/presentation/dto/user.js.map +1 -0
- package/dist/presentation/e2eRoutes/create-session.d.ts +2 -0
- package/dist/presentation/e2eRoutes/create-session.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/create-session.js +49 -0
- package/dist/presentation/e2eRoutes/create-session.js.map +1 -0
- package/dist/presentation/e2eRoutes/create-user.d.ts +2 -0
- package/dist/presentation/e2eRoutes/create-user.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/create-user.js +42 -0
- package/dist/presentation/e2eRoutes/create-user.js.map +1 -0
- package/dist/presentation/e2eRoutes/index.d.ts +3 -0
- package/dist/presentation/e2eRoutes/index.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/index.js +15 -0
- package/dist/presentation/e2eRoutes/index.js.map +1 -0
- package/dist/presentation/routes/email-verification/verify-email-confirm.d.ts +2 -0
- package/dist/presentation/routes/email-verification/verify-email-confirm.d.ts.map +1 -0
- package/dist/presentation/routes/email-verification/verify-email-confirm.js +35 -0
- package/dist/presentation/routes/email-verification/verify-email-confirm.js.map +1 -0
- package/dist/presentation/routes/email-verification/verify-email-resend.d.ts +2 -0
- package/dist/presentation/routes/email-verification/verify-email-resend.d.ts.map +1 -0
- package/dist/presentation/routes/email-verification/verify-email-resend.js +27 -0
- package/dist/presentation/routes/email-verification/verify-email-resend.js.map +1 -0
- package/dist/presentation/routes/index.d.ts +3 -0
- package/dist/presentation/routes/index.d.ts.map +1 -0
- package/dist/presentation/routes/index.js +31 -0
- package/dist/presentation/routes/index.js.map +1 -0
- package/dist/presentation/routes/password/change-password.d.ts +2 -0
- package/dist/presentation/routes/password/change-password.d.ts.map +1 -0
- package/dist/presentation/routes/password/change-password.js +50 -0
- package/dist/presentation/routes/password/change-password.js.map +1 -0
- package/dist/presentation/routes/password/password-reset-confirm.d.ts +2 -0
- package/dist/presentation/routes/password/password-reset-confirm.d.ts.map +1 -0
- package/dist/presentation/routes/password/password-reset-confirm.js +36 -0
- package/dist/presentation/routes/password/password-reset-confirm.js.map +1 -0
- package/dist/presentation/routes/password/password-reset-request.d.ts +2 -0
- package/dist/presentation/routes/password/password-reset-request.d.ts.map +1 -0
- package/dist/presentation/routes/password/password-reset-request.js +26 -0
- package/dist/presentation/routes/password/password-reset-request.js.map +1 -0
- package/dist/presentation/routes/rate-limit.d.ts +10 -0
- package/dist/presentation/routes/rate-limit.d.ts.map +1 -0
- package/dist/presentation/routes/rate-limit.js +101 -0
- package/dist/presentation/routes/rate-limit.js.map +1 -0
- package/dist/presentation/routes/registration/signup.d.ts +2 -0
- package/dist/presentation/routes/registration/signup.d.ts.map +1 -0
- package/dist/presentation/routes/registration/signup.js +82 -0
- package/dist/presentation/routes/registration/signup.js.map +1 -0
- package/dist/presentation/routes/session/login.d.ts +2 -0
- package/dist/presentation/routes/session/login.d.ts.map +1 -0
- package/dist/presentation/routes/session/login.js +48 -0
- package/dist/presentation/routes/session/login.js.map +1 -0
- package/dist/presentation/routes/session/logout.d.ts +2 -0
- package/dist/presentation/routes/session/logout.d.ts.map +1 -0
- package/dist/presentation/routes/session/logout.js +25 -0
- package/dist/presentation/routes/session/logout.js.map +1 -0
- package/dist/presentation/routes/session/me.d.ts +2 -0
- package/dist/presentation/routes/session/me.d.ts.map +1 -0
- package/dist/presentation/routes/session/me.js +42 -0
- package/dist/presentation/routes/session/me.js.map +1 -0
- package/dist/presentation/routes/session/refresh.d.ts +2 -0
- package/dist/presentation/routes/session/refresh.d.ts.map +1 -0
- package/dist/presentation/routes/session/refresh.js +49 -0
- package/dist/presentation/routes/session/refresh.js.map +1 -0
- package/dist/presentation/subscribers/index.d.ts +3 -0
- package/dist/presentation/subscribers/index.d.ts.map +1 -0
- package/dist/presentation/subscribers/index.js +4 -0
- package/dist/presentation/subscribers/index.js.map +1 -0
- package/dist/presentation/subscribers/on-email-verification-send-requested.d.ts +3 -0
- package/dist/presentation/subscribers/on-email-verification-send-requested.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-email-verification-send-requested.js +13 -0
- package/dist/presentation/subscribers/on-email-verification-send-requested.js.map +1 -0
- package/dist/presentation/subscribers/on-password-reset-send-requested.d.ts +3 -0
- package/dist/presentation/subscribers/on-password-reset-send-requested.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-password-reset-send-requested.js +14 -0
- package/dist/presentation/subscribers/on-password-reset-send-requested.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/drizzle/0000_initial.sql +81 -0
- package/drizzle/meta/0000_snapshot.json +643 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +7 -0
- package/package.json +88 -0
- package/src/config.ts +88 -0
- package/src/core/auth.test.ts +675 -0
- package/src/core/auth.ts +560 -0
- package/src/core/entities/email-verification.ts +8 -0
- package/src/core/entities/password-reset.ts +8 -0
- package/src/core/entities/refresh-token.ts +11 -0
- package/src/core/entities/user.ts +12 -0
- package/src/core/errors.ts +123 -0
- package/src/core/job-lease-token.test.ts +201 -0
- package/src/core/job-lease-token.ts +83 -0
- package/src/core/jwt.test.ts +180 -0
- package/src/core/jwt.ts +66 -0
- package/src/core/password.test.ts +28 -0
- package/src/core/password.ts +21 -0
- package/src/core/rate-limit.test.ts +462 -0
- package/src/core/rate-limit.ts +130 -0
- package/src/core/runner-session-token.test.ts +126 -0
- package/src/core/runner-session-token.ts +49 -0
- package/src/db/db.ts +28 -0
- package/src/db/email-verifications.test.ts +61 -0
- package/src/db/email-verifications.ts +160 -0
- package/src/db/migrations.ts +4 -0
- package/src/db/password-resets.test.ts +61 -0
- package/src/db/password-resets.ts +69 -0
- package/src/db/rate-limits.ts +69 -0
- package/src/db/refresh-tokens.test.ts +164 -0
- package/src/db/refresh-tokens.ts +149 -0
- package/src/db/schema/common.ts +3 -0
- package/src/db/schema/email-verifications.ts +37 -0
- package/src/db/schema/outbox.ts +4 -0
- package/src/db/schema/password-resets.ts +37 -0
- package/src/db/schema/rate-limits.ts +27 -0
- package/src/db/schema/refresh-tokens.ts +43 -0
- package/src/db/schema/users.ts +37 -0
- package/src/db/users.test.ts +75 -0
- package/src/db/users.ts +121 -0
- package/src/index.test.ts +39 -0
- package/src/index.ts +69 -0
- package/src/metrics/index.ts +13 -0
- package/src/metrics/instance.test.ts +69 -0
- package/src/metrics/instance.ts +77 -0
- package/src/presentation/auth/bearer-token-auth.test.ts +99 -0
- package/src/presentation/auth/bearer-token-auth.ts +51 -0
- package/src/presentation/auth/jwt-auth.test.ts +136 -0
- package/src/presentation/auth/jwt-auth.ts +39 -0
- package/src/presentation/auth/lease-token-auth.test.ts +56 -0
- package/src/presentation/auth/lease-token-auth.ts +25 -0
- package/src/presentation/auth/refresh-cookie.test.ts +39 -0
- package/src/presentation/auth/refresh-cookie.ts +34 -0
- package/src/presentation/auth/runner-session-auth.test.ts +54 -0
- package/src/presentation/auth/runner-session-auth.ts +13 -0
- package/src/presentation/dto/user.ts +21 -0
- package/src/presentation/e2eRoutes/create-session.ts +39 -0
- package/src/presentation/e2eRoutes/create-user.ts +38 -0
- package/src/presentation/e2eRoutes/index.test.ts +102 -0
- package/src/presentation/e2eRoutes/index.ts +10 -0
- package/src/presentation/routes/email-verification/verify-email-confirm.test.ts +66 -0
- package/src/presentation/routes/email-verification/verify-email-confirm.ts +37 -0
- package/src/presentation/routes/email-verification/verify-email-resend.test.ts +100 -0
- package/src/presentation/routes/email-verification/verify-email-resend.ts +26 -0
- package/src/presentation/routes/index.ts +29 -0
- package/src/presentation/routes/password/change-password.test.ts +106 -0
- package/src/presentation/routes/password/change-password.ts +45 -0
- package/src/presentation/routes/password/password-reset-confirm.test.ts +63 -0
- package/src/presentation/routes/password/password-reset-confirm.ts +35 -0
- package/src/presentation/routes/password/password-reset-request.test.ts +56 -0
- package/src/presentation/routes/password/password-reset-request.ts +25 -0
- package/src/presentation/routes/rate-limit.test.ts +341 -0
- package/src/presentation/routes/rate-limit.ts +119 -0
- package/src/presentation/routes/registration/signup.test.ts +284 -0
- package/src/presentation/routes/registration/signup.ts +81 -0
- package/src/presentation/routes/session/login.test.ts +77 -0
- package/src/presentation/routes/session/login.ts +50 -0
- package/src/presentation/routes/session/logout.test.ts +55 -0
- package/src/presentation/routes/session/logout.ts +23 -0
- package/src/presentation/routes/session/me.test.ts +56 -0
- package/src/presentation/routes/session/me.ts +35 -0
- package/src/presentation/routes/session/refresh.test.ts +89 -0
- package/src/presentation/routes/session/refresh.ts +54 -0
- package/src/presentation/subscribers/email-send-requested.test.ts +77 -0
- package/src/presentation/subscribers/index.ts +2 -0
- package/src/presentation/subscribers/on-email-verification-send-requested.ts +10 -0
- package/src/presentation/subscribers/on-password-reset-send-requested.ts +13 -0
- package/test/env.ts +10 -0
- package/test/factories/index.ts +1 -0
- package/test/factories/user.ts +36 -0
- package/test/globalSetup.ts +18 -0
- package/test/index.ts +1 -0
- package/test/routes.ts +275 -0
- package/test/setup.ts +17 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +4 -0
- package/tsconfig.test.json +8 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { User } from './entities/user.js';
|
|
2
|
+
import { AuthDependencyUnavailableError, EmailNotVerifiedError, InvalidCredentialsError, UserNotFoundError } from './errors.js';
|
|
3
|
+
export interface SignupParams {
|
|
4
|
+
email: string;
|
|
5
|
+
password: string;
|
|
6
|
+
name?: string | undefined;
|
|
7
|
+
}
|
|
8
|
+
export interface ProvisionUserParams {
|
|
9
|
+
email: string;
|
|
10
|
+
name?: string | null | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates a verified, password-less user for an external identity provider.
|
|
14
|
+
* Existing users are returned unchanged, including their password and profile.
|
|
15
|
+
*/
|
|
16
|
+
export declare function provisionUser(params: ProvisionUserParams): Promise<User>;
|
|
17
|
+
export declare function signup(params: SignupParams): Promise<User>;
|
|
18
|
+
export interface SignupWithInvitationParams extends SignupParams {
|
|
19
|
+
invitationToken: string;
|
|
20
|
+
}
|
|
21
|
+
export interface SignupWithInvitationResult extends LoginResult {
|
|
22
|
+
membership: {
|
|
23
|
+
id: string;
|
|
24
|
+
userId: string;
|
|
25
|
+
workspaceId: string;
|
|
26
|
+
} | null;
|
|
27
|
+
acceptError?: {
|
|
28
|
+
code: string;
|
|
29
|
+
message: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export declare function signupWithInvitation(params: SignupWithInvitationParams): Promise<SignupWithInvitationResult>;
|
|
33
|
+
export interface CreateUserParams extends SignupParams {
|
|
34
|
+
verified: boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare function createUser(params: CreateUserParams): Promise<User>;
|
|
37
|
+
export interface LoginParams {
|
|
38
|
+
email: string;
|
|
39
|
+
password: string;
|
|
40
|
+
}
|
|
41
|
+
export interface LoginResult {
|
|
42
|
+
token: string;
|
|
43
|
+
refreshToken: string;
|
|
44
|
+
user: User;
|
|
45
|
+
}
|
|
46
|
+
export declare function login(params: LoginParams): Promise<LoginResult>;
|
|
47
|
+
export interface CreateSessionForUserParams {
|
|
48
|
+
userId?: string | undefined;
|
|
49
|
+
email?: string | undefined;
|
|
50
|
+
}
|
|
51
|
+
export interface CreateSessionForUserResult {
|
|
52
|
+
token: string;
|
|
53
|
+
refreshToken: string;
|
|
54
|
+
user: User;
|
|
55
|
+
}
|
|
56
|
+
export type CreateSessionForUserError = AuthDependencyUnavailableError | EmailNotVerifiedError | InvalidCredentialsError | UserNotFoundError;
|
|
57
|
+
export declare function createSessionForUser(params: CreateSessionForUserParams): Promise<CreateSessionForUserResult>;
|
|
58
|
+
export interface RefreshAccessTokenResult {
|
|
59
|
+
token: string;
|
|
60
|
+
/** Undefined on a grace-window hit: keep the existing cookie instead of rotating it. */
|
|
61
|
+
refreshToken: string | undefined;
|
|
62
|
+
user: User;
|
|
63
|
+
}
|
|
64
|
+
export declare function refreshAccessToken(params: {
|
|
65
|
+
refreshToken: string;
|
|
66
|
+
}): Promise<RefreshAccessTokenResult>;
|
|
67
|
+
export interface ConfirmEmailVerificationResult {
|
|
68
|
+
token: string;
|
|
69
|
+
refreshToken: string;
|
|
70
|
+
user: User;
|
|
71
|
+
}
|
|
72
|
+
export interface ResendEmailVerificationResult {
|
|
73
|
+
nextResendAvailableAt: Date;
|
|
74
|
+
}
|
|
75
|
+
export declare function confirmEmailVerification(params: {
|
|
76
|
+
token: string;
|
|
77
|
+
}): Promise<ConfirmEmailVerificationResult>;
|
|
78
|
+
export declare function resendEmailVerification(params: {
|
|
79
|
+
email: string;
|
|
80
|
+
}): Promise<ResendEmailVerificationResult>;
|
|
81
|
+
export declare function requestPasswordReset(params: {
|
|
82
|
+
email: string;
|
|
83
|
+
}): Promise<void>;
|
|
84
|
+
export interface ConfirmPasswordResetResult {
|
|
85
|
+
token: string;
|
|
86
|
+
refreshToken: string;
|
|
87
|
+
user: User;
|
|
88
|
+
}
|
|
89
|
+
export declare function confirmPasswordReset(params: {
|
|
90
|
+
token: string;
|
|
91
|
+
newPassword: string;
|
|
92
|
+
}): Promise<ConfirmPasswordResetResult>;
|
|
93
|
+
export declare function changePassword(params: {
|
|
94
|
+
userId: string;
|
|
95
|
+
currentPassword: string;
|
|
96
|
+
newPassword: string;
|
|
97
|
+
refreshToken?: string | undefined;
|
|
98
|
+
}): Promise<void>;
|
|
99
|
+
export declare function logout(params: {
|
|
100
|
+
refreshToken?: string | undefined;
|
|
101
|
+
}): Promise<void>;
|
|
102
|
+
export interface GetCurrentUserResult {
|
|
103
|
+
user: User;
|
|
104
|
+
}
|
|
105
|
+
export declare function getCurrentUser(params: {
|
|
106
|
+
userId: string;
|
|
107
|
+
}): Promise<GetCurrentUserResult>;
|
|
108
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/core/auth.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EACL,8BAA8B,EAC9B,qBAAqB,EAErB,uBAAuB,EAEvB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AA8ErB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9E;AAED,wBAAsB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhE;AAED,MAAM,WAAW,0BAA2B,SAAQ,YAAY;IAC9D,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,0BAA2B,SAAQ,WAAW;IAC7D,UAAU,EAAE;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAC;IACrE,WAAW,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,CAAC;CAC/C;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,0BAA0B,CAAC,CA2ErC;AAED,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAexE;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,wBAAsB,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAyBrE;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,yBAAyB,GACjC,8BAA8B,GAC9B,qBAAqB,GACrB,uBAAuB,GACvB,iBAAiB,CAAC;AAEtB,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,0BAA0B,CAAC,CAqBrC;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAmDpC;AAED,MAAM,WAAW,8BAA8B;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,6BAA6B;IAC5C,qBAAqB,EAAE,IAAI,CAAC;CAC7B;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAe1C;AAED,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAazC;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBjF;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAuBtC;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBhB;AAED,wBAAsB,MAAM,CAAC,MAAM,EAAE;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvF;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAO5F"}
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import { EMAIL_VERIFICATION_RESEND_COOLDOWN_SECONDS, emailSchema } from '@shipfox/api-auth-dto';
|
|
2
|
+
import { acceptWorkspaceInvitation, InvitationEmailMismatchError, listMembershipsByUser, peekInvitationByRawToken, TokenAlreadyUsedError, TokenExpiredError, TokenInvalidError as WorkspacesTokenInvalidError } from '@shipfox/api-workspaces';
|
|
3
|
+
import { generateOpaqueToken, hashOpaqueToken } from '@shipfox/node-tokens';
|
|
4
|
+
import { config } from '#config.js';
|
|
5
|
+
import { consumeEmailVerification, createEmailVerification, createResendEmailVerification } from '#db/email-verifications.js';
|
|
6
|
+
import { consumePasswordReset, createPasswordReset } from '#db/password-resets.js';
|
|
7
|
+
import { createRefreshToken, findActiveRefreshTokenByHash, findRefreshTokenByHash, revokeRefreshTokenByHash, revokeRefreshTokensForUser, rotateRefreshToken } from '#db/refresh-tokens.js';
|
|
8
|
+
import { createUser as createDbUser, findUserByEmail, findUserById, markEmailVerified, provisionUser as provisionDbUser, updateUserPassword } from '#db/users.js';
|
|
9
|
+
import { recordTokenRefreshed } from '#metrics/index.js';
|
|
10
|
+
import { AuthDependencyUnavailableError, EmailNotVerifiedError, EmailTakenError, InvalidCredentialsError, TokenInvalidError, UserNotFoundError } from './errors.js';
|
|
11
|
+
import { signUserToken } from './jwt.js';
|
|
12
|
+
import { hashPassword, verifyPassword } from './password.js';
|
|
13
|
+
const VERIFICATION_TTL_HOURS = 24;
|
|
14
|
+
const RESET_TTL_HOURS = 1;
|
|
15
|
+
let dummyHashCache;
|
|
16
|
+
async function getDummyHash() {
|
|
17
|
+
if (!dummyHashCache) {
|
|
18
|
+
dummyHashCache = await hashPassword({
|
|
19
|
+
password: 'dummy-for-timing-parity'
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return dummyHashCache;
|
|
23
|
+
}
|
|
24
|
+
function hoursFromNow(hours) {
|
|
25
|
+
return new Date(Date.now() + hours * 60 * 60 * 1000);
|
|
26
|
+
}
|
|
27
|
+
function daysFromNow(days) {
|
|
28
|
+
return new Date(Date.now() + days * 24 * 60 * 60 * 1000);
|
|
29
|
+
}
|
|
30
|
+
function recordRefreshOutcome(outcome) {
|
|
31
|
+
recordTokenRefreshed(outcome);
|
|
32
|
+
}
|
|
33
|
+
async function signAccessToken(user) {
|
|
34
|
+
const memberships = await listMembershipsByUser({
|
|
35
|
+
userId: user.id
|
|
36
|
+
}).catch((error)=>{
|
|
37
|
+
throw new AuthDependencyUnavailableError('workspaces', error);
|
|
38
|
+
});
|
|
39
|
+
return await signUserToken({
|
|
40
|
+
userId: user.id,
|
|
41
|
+
email: user.email,
|
|
42
|
+
name: user.name,
|
|
43
|
+
memberships: memberships.map((m)=>({
|
|
44
|
+
workspaceId: m.workspaceId,
|
|
45
|
+
role: 'admin'
|
|
46
|
+
})),
|
|
47
|
+
secret: config.AUTH_JWT_SECRET,
|
|
48
|
+
expiresIn: config.AUTH_JWT_EXPIRES_IN
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function isWithinRotationGrace(refreshToken) {
|
|
52
|
+
if (!refreshToken.rotatedAt) return false;
|
|
53
|
+
const graceMs = config.AUTH_REFRESH_ROTATION_GRACE_SECONDS * 1000;
|
|
54
|
+
return Date.now() - refreshToken.rotatedAt.getTime() <= graceMs;
|
|
55
|
+
}
|
|
56
|
+
async function createRefreshSession(user) {
|
|
57
|
+
const refreshToken = generateOpaqueToken('refreshToken');
|
|
58
|
+
await createRefreshToken({
|
|
59
|
+
userId: user.id,
|
|
60
|
+
hashedToken: hashOpaqueToken(refreshToken),
|
|
61
|
+
expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS)
|
|
62
|
+
});
|
|
63
|
+
return refreshToken;
|
|
64
|
+
}
|
|
65
|
+
function verificationLink(rawToken) {
|
|
66
|
+
return `${config.CLIENT_BASE_URL}/auth/verify-email?token=${rawToken}`;
|
|
67
|
+
}
|
|
68
|
+
function passwordResetLink(rawToken) {
|
|
69
|
+
return `${config.CLIENT_BASE_URL}/auth/reset?token=${rawToken}`;
|
|
70
|
+
}
|
|
71
|
+
async function createAndQueueEmailVerification(user) {
|
|
72
|
+
const rawToken = generateOpaqueToken('emailVerification');
|
|
73
|
+
await createEmailVerification({
|
|
74
|
+
userId: user.id,
|
|
75
|
+
hashedToken: hashOpaqueToken(rawToken),
|
|
76
|
+
expiresAt: hoursFromNow(VERIFICATION_TTL_HOURS),
|
|
77
|
+
sendEmail: {
|
|
78
|
+
email: user.email,
|
|
79
|
+
verifyLink: verificationLink(rawToken)
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Creates a verified, password-less user for an external identity provider.
|
|
85
|
+
* Existing users are returned unchanged, including their password and profile.
|
|
86
|
+
*/ export async function provisionUser(params) {
|
|
87
|
+
return await provisionDbUser({
|
|
88
|
+
email: emailSchema.parse(params.email),
|
|
89
|
+
name: params.name ?? null
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
export async function signup(params) {
|
|
93
|
+
const existing = await findUserByEmail({
|
|
94
|
+
email: params.email
|
|
95
|
+
});
|
|
96
|
+
if (existing) {
|
|
97
|
+
throw new EmailTakenError(params.email);
|
|
98
|
+
}
|
|
99
|
+
const hashedPassword = await hashPassword({
|
|
100
|
+
password: params.password
|
|
101
|
+
});
|
|
102
|
+
const user = await createDbUser({
|
|
103
|
+
email: params.email,
|
|
104
|
+
hashedPassword,
|
|
105
|
+
name: params.name ?? null
|
|
106
|
+
});
|
|
107
|
+
await createAndQueueEmailVerification(user);
|
|
108
|
+
return user;
|
|
109
|
+
}
|
|
110
|
+
export async function signupWithInvitation(params) {
|
|
111
|
+
// Step 1: Pre-validate the invitation BEFORE any user write so an invalid
|
|
112
|
+
// token never produces an orphan user. Re-validation happens again inside
|
|
113
|
+
// acceptWorkspaceInvitation (race-safe) after the user is created.
|
|
114
|
+
const invitation = await peekInvitationByRawToken({
|
|
115
|
+
token: params.invitationToken
|
|
116
|
+
});
|
|
117
|
+
if (!invitation) {
|
|
118
|
+
throw new WorkspacesTokenInvalidError('Invitation token is invalid');
|
|
119
|
+
}
|
|
120
|
+
if (invitation.acceptedAt !== null) {
|
|
121
|
+
throw new TokenAlreadyUsedError();
|
|
122
|
+
}
|
|
123
|
+
if (invitation.expiresAt.getTime() <= Date.now()) {
|
|
124
|
+
throw new TokenExpiredError();
|
|
125
|
+
}
|
|
126
|
+
if (invitation.email !== params.email) {
|
|
127
|
+
throw new InvitationEmailMismatchError();
|
|
128
|
+
}
|
|
129
|
+
// Step 2: Create the user as verified. The invitation email is the proof of
|
|
130
|
+
// ownership, and this avoids a second auth-table write just to mark verified.
|
|
131
|
+
const existing = await findUserByEmail({
|
|
132
|
+
email: params.email
|
|
133
|
+
});
|
|
134
|
+
if (existing) {
|
|
135
|
+
throw new EmailTakenError(params.email);
|
|
136
|
+
}
|
|
137
|
+
const hashedPassword = await hashPassword({
|
|
138
|
+
password: params.password
|
|
139
|
+
});
|
|
140
|
+
const user = await createDbUser({
|
|
141
|
+
email: params.email,
|
|
142
|
+
hashedPassword,
|
|
143
|
+
name: params.name ?? null,
|
|
144
|
+
emailVerifiedAt: new Date()
|
|
145
|
+
});
|
|
146
|
+
// Step 3: Accept the invitation through the workspaces module boundary.
|
|
147
|
+
// This is intentionally not one database transaction with auth; treat it like
|
|
148
|
+
// calling another service. If it fails after user creation, return a verified
|
|
149
|
+
// signed-in user and an explicit retryable accept error.
|
|
150
|
+
let membership = null;
|
|
151
|
+
let acceptError;
|
|
152
|
+
try {
|
|
153
|
+
const result = await acceptWorkspaceInvitation({
|
|
154
|
+
token: params.invitationToken,
|
|
155
|
+
userId: user.id,
|
|
156
|
+
email: user.email,
|
|
157
|
+
name: user.name
|
|
158
|
+
});
|
|
159
|
+
membership = {
|
|
160
|
+
id: result.membership.id,
|
|
161
|
+
userId: result.membership.userId,
|
|
162
|
+
workspaceId: result.membership.workspaceId
|
|
163
|
+
};
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (error instanceof WorkspacesTokenInvalidError || error instanceof TokenAlreadyUsedError || error instanceof TokenExpiredError || error instanceof InvitationEmailMismatchError) {
|
|
166
|
+
acceptError = {
|
|
167
|
+
code: error.name,
|
|
168
|
+
message: error.message
|
|
169
|
+
};
|
|
170
|
+
} else {
|
|
171
|
+
acceptError = {
|
|
172
|
+
code: 'AcceptFailed',
|
|
173
|
+
message: 'Could not accept the invitation; please retry from the invite link.'
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Step 4: Issue session. signAccessToken reads memberships through the
|
|
178
|
+
// workspaces module API, so a successful accept is reflected in the JWT.
|
|
179
|
+
const token = await signAccessToken(user);
|
|
180
|
+
const refreshToken = await createRefreshSession(user);
|
|
181
|
+
if (acceptError) {
|
|
182
|
+
return {
|
|
183
|
+
token,
|
|
184
|
+
refreshToken,
|
|
185
|
+
user,
|
|
186
|
+
membership,
|
|
187
|
+
acceptError
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
token,
|
|
192
|
+
refreshToken,
|
|
193
|
+
user,
|
|
194
|
+
membership
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
export async function createUser(params) {
|
|
198
|
+
const existing = await findUserByEmail({
|
|
199
|
+
email: params.email
|
|
200
|
+
});
|
|
201
|
+
if (existing) {
|
|
202
|
+
throw new EmailTakenError(params.email);
|
|
203
|
+
}
|
|
204
|
+
const hashedPassword = await hashPassword({
|
|
205
|
+
password: params.password
|
|
206
|
+
});
|
|
207
|
+
const user = await createDbUser({
|
|
208
|
+
email: params.email,
|
|
209
|
+
hashedPassword,
|
|
210
|
+
name: params.name ?? null,
|
|
211
|
+
emailVerifiedAt: params.verified ? new Date() : null
|
|
212
|
+
});
|
|
213
|
+
return user;
|
|
214
|
+
}
|
|
215
|
+
export async function login(params) {
|
|
216
|
+
const user = await findUserByEmail({
|
|
217
|
+
email: params.email
|
|
218
|
+
});
|
|
219
|
+
if (!user) {
|
|
220
|
+
await verifyPassword({
|
|
221
|
+
password: params.password,
|
|
222
|
+
hash: await getDummyHash()
|
|
223
|
+
});
|
|
224
|
+
throw new InvalidCredentialsError();
|
|
225
|
+
}
|
|
226
|
+
const hasPassword = user.hashedPassword !== null;
|
|
227
|
+
const ok = await verifyPassword({
|
|
228
|
+
password: params.password,
|
|
229
|
+
hash: user.hashedPassword ?? await getDummyHash()
|
|
230
|
+
});
|
|
231
|
+
if (!hasPassword || !ok || user.status !== 'active') {
|
|
232
|
+
throw new InvalidCredentialsError();
|
|
233
|
+
}
|
|
234
|
+
if (user.emailVerifiedAt === null) {
|
|
235
|
+
throw new EmailNotVerifiedError();
|
|
236
|
+
}
|
|
237
|
+
const token = await signAccessToken(user);
|
|
238
|
+
const refreshToken = await createRefreshSession(user);
|
|
239
|
+
return {
|
|
240
|
+
token,
|
|
241
|
+
refreshToken,
|
|
242
|
+
user
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
export async function createSessionForUser(params) {
|
|
246
|
+
const user = params.userId ? await findUserById({
|
|
247
|
+
id: params.userId
|
|
248
|
+
}) : params.email ? await findUserByEmail({
|
|
249
|
+
email: params.email
|
|
250
|
+
}) : undefined;
|
|
251
|
+
if (!user) {
|
|
252
|
+
throw new UserNotFoundError(params.userId ?? params.email ?? 'unknown');
|
|
253
|
+
}
|
|
254
|
+
if (user.emailVerifiedAt === null) {
|
|
255
|
+
throw new EmailNotVerifiedError();
|
|
256
|
+
}
|
|
257
|
+
if (user.status !== 'active') {
|
|
258
|
+
throw new InvalidCredentialsError();
|
|
259
|
+
}
|
|
260
|
+
const token = await signAccessToken(user);
|
|
261
|
+
const refreshToken = await createRefreshSession(user);
|
|
262
|
+
return {
|
|
263
|
+
token,
|
|
264
|
+
refreshToken,
|
|
265
|
+
user
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
export async function refreshAccessToken(params) {
|
|
269
|
+
const currentHashedToken = hashOpaqueToken(params.refreshToken);
|
|
270
|
+
const current = await findRefreshTokenByHash({
|
|
271
|
+
hashedToken: currentHashedToken
|
|
272
|
+
});
|
|
273
|
+
if (!current) {
|
|
274
|
+
recordRefreshOutcome('rejected');
|
|
275
|
+
throw new TokenInvalidError('Refresh token is invalid or expired');
|
|
276
|
+
}
|
|
277
|
+
const user = await findUserById({
|
|
278
|
+
id: current.userId
|
|
279
|
+
});
|
|
280
|
+
if (user?.status !== 'active') {
|
|
281
|
+
await revokeRefreshTokenByHash({
|
|
282
|
+
hashedToken: currentHashedToken
|
|
283
|
+
});
|
|
284
|
+
recordRefreshOutcome('rejected');
|
|
285
|
+
throw new TokenInvalidError('Refresh token is invalid or expired');
|
|
286
|
+
}
|
|
287
|
+
// Within the grace window a rotated token means a concurrent refresh (e.g. a
|
|
288
|
+
// second tab); past it, reuse of a retired token means a compromised session.
|
|
289
|
+
if (current.rotatedAt) {
|
|
290
|
+
if (isWithinRotationGrace(current)) {
|
|
291
|
+
const token = await signAccessToken(user);
|
|
292
|
+
recordRefreshOutcome('grace');
|
|
293
|
+
return {
|
|
294
|
+
token,
|
|
295
|
+
refreshToken: undefined,
|
|
296
|
+
user
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
await revokeRefreshTokensForUser({
|
|
300
|
+
userId: user.id
|
|
301
|
+
});
|
|
302
|
+
recordRefreshOutcome('rejected');
|
|
303
|
+
throw new TokenInvalidError('Refresh token reused after rotation');
|
|
304
|
+
}
|
|
305
|
+
// Losing the CAS requires a state check: another request may have rotated,
|
|
306
|
+
// revoked, or expired the token before this refresh could claim it.
|
|
307
|
+
const nextRefreshToken = generateOpaqueToken('refreshToken');
|
|
308
|
+
const rotated = await rotateRefreshToken({
|
|
309
|
+
id: current.id,
|
|
310
|
+
currentHashedToken,
|
|
311
|
+
nextHashedToken: hashOpaqueToken(nextRefreshToken),
|
|
312
|
+
expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS)
|
|
313
|
+
});
|
|
314
|
+
if (!rotated) {
|
|
315
|
+
const latest = await findRefreshTokenByHash({
|
|
316
|
+
hashedToken: currentHashedToken
|
|
317
|
+
});
|
|
318
|
+
if (!latest || !isWithinRotationGrace(latest)) {
|
|
319
|
+
recordRefreshOutcome('rejected');
|
|
320
|
+
throw new TokenInvalidError('Refresh token is invalid or expired');
|
|
321
|
+
}
|
|
322
|
+
const token = await signAccessToken(user);
|
|
323
|
+
recordRefreshOutcome('grace');
|
|
324
|
+
return {
|
|
325
|
+
token,
|
|
326
|
+
refreshToken: undefined,
|
|
327
|
+
user
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
const token = await signAccessToken(user);
|
|
331
|
+
recordRefreshOutcome('rotated');
|
|
332
|
+
return {
|
|
333
|
+
token,
|
|
334
|
+
refreshToken: nextRefreshToken,
|
|
335
|
+
user
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
export async function confirmEmailVerification(params) {
|
|
339
|
+
const consumed = await consumeEmailVerification({
|
|
340
|
+
hashedToken: hashOpaqueToken(params.token)
|
|
341
|
+
});
|
|
342
|
+
if (!consumed) {
|
|
343
|
+
throw new TokenInvalidError('Verification token is invalid or expired');
|
|
344
|
+
}
|
|
345
|
+
const user = await markEmailVerified({
|
|
346
|
+
userId: consumed.userId
|
|
347
|
+
});
|
|
348
|
+
if (user?.status !== 'active') {
|
|
349
|
+
throw new TokenInvalidError('Verification token is invalid or expired');
|
|
350
|
+
}
|
|
351
|
+
const token = await signAccessToken(user);
|
|
352
|
+
const refreshToken = await createRefreshSession(user);
|
|
353
|
+
return {
|
|
354
|
+
token,
|
|
355
|
+
refreshToken,
|
|
356
|
+
user
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
export async function resendEmailVerification(params) {
|
|
360
|
+
const rawToken = generateOpaqueToken('emailVerification');
|
|
361
|
+
const result = await createResendEmailVerification({
|
|
362
|
+
email: params.email,
|
|
363
|
+
hashedToken: hashOpaqueToken(rawToken),
|
|
364
|
+
expiresAt: hoursFromNow(VERIFICATION_TTL_HOURS),
|
|
365
|
+
cooldownSeconds: EMAIL_VERIFICATION_RESEND_COOLDOWN_SECONDS,
|
|
366
|
+
sendEmail: {
|
|
367
|
+
verifyLink: verificationLink(rawToken)
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
return {
|
|
371
|
+
nextResendAvailableAt: result.nextResendAvailableAt
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
export async function requestPasswordReset(params) {
|
|
375
|
+
const user = await findUserByEmail({
|
|
376
|
+
email: params.email
|
|
377
|
+
});
|
|
378
|
+
if (user?.status !== 'active' || user.hashedPassword === null) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const rawToken = generateOpaqueToken('passwordReset');
|
|
382
|
+
await createPasswordReset({
|
|
383
|
+
userId: user.id,
|
|
384
|
+
hashedToken: hashOpaqueToken(rawToken),
|
|
385
|
+
expiresAt: hoursFromNow(RESET_TTL_HOURS),
|
|
386
|
+
sendEmail: {
|
|
387
|
+
email: user.email,
|
|
388
|
+
resetLink: passwordResetLink(rawToken),
|
|
389
|
+
expiresInHours: RESET_TTL_HOURS
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
export async function confirmPasswordReset(params) {
|
|
394
|
+
const consumed = await consumePasswordReset({
|
|
395
|
+
hashedToken: hashOpaqueToken(params.token)
|
|
396
|
+
});
|
|
397
|
+
if (!consumed) {
|
|
398
|
+
throw new TokenInvalidError('Reset token is invalid or expired');
|
|
399
|
+
}
|
|
400
|
+
const existingUser = await findUserById({
|
|
401
|
+
id: consumed.userId
|
|
402
|
+
});
|
|
403
|
+
if (existingUser?.status !== 'active' || existingUser.hashedPassword === null) {
|
|
404
|
+
throw new TokenInvalidError('Reset token is invalid or expired');
|
|
405
|
+
}
|
|
406
|
+
const hashedPassword = await hashPassword({
|
|
407
|
+
password: params.newPassword
|
|
408
|
+
});
|
|
409
|
+
const user = await updateUserPassword({
|
|
410
|
+
userId: consumed.userId,
|
|
411
|
+
hashedPassword
|
|
412
|
+
});
|
|
413
|
+
if (user?.status !== 'active') {
|
|
414
|
+
throw new TokenInvalidError('Reset token is invalid or expired');
|
|
415
|
+
}
|
|
416
|
+
await revokeRefreshTokensForUser({
|
|
417
|
+
userId: consumed.userId
|
|
418
|
+
});
|
|
419
|
+
const token = await signAccessToken(user);
|
|
420
|
+
const refreshToken = await createRefreshSession(user);
|
|
421
|
+
return {
|
|
422
|
+
token,
|
|
423
|
+
refreshToken,
|
|
424
|
+
user
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
export async function changePassword(params) {
|
|
428
|
+
const user = await findUserById({
|
|
429
|
+
id: params.userId
|
|
430
|
+
});
|
|
431
|
+
if (!user) {
|
|
432
|
+
throw new UserNotFoundError(params.userId);
|
|
433
|
+
}
|
|
434
|
+
const hasPassword = user.hashedPassword !== null;
|
|
435
|
+
const ok = await verifyPassword({
|
|
436
|
+
password: params.currentPassword,
|
|
437
|
+
hash: user.hashedPassword ?? await getDummyHash()
|
|
438
|
+
});
|
|
439
|
+
if (!hasPassword || !ok) {
|
|
440
|
+
throw new InvalidCredentialsError();
|
|
441
|
+
}
|
|
442
|
+
const hashedPassword = await hashPassword({
|
|
443
|
+
password: params.newPassword
|
|
444
|
+
});
|
|
445
|
+
const currentRefreshSession = params.refreshToken ? await findActiveRefreshTokenByHash({
|
|
446
|
+
hashedToken: hashOpaqueToken(params.refreshToken)
|
|
447
|
+
}) : undefined;
|
|
448
|
+
const exceptRefreshTokenId = currentRefreshSession?.userId === user.id ? currentRefreshSession.id : undefined;
|
|
449
|
+
await updateUserPassword({
|
|
450
|
+
userId: user.id,
|
|
451
|
+
hashedPassword
|
|
452
|
+
});
|
|
453
|
+
await revokeRefreshTokensForUser({
|
|
454
|
+
userId: user.id,
|
|
455
|
+
exceptRefreshTokenId
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
export async function logout(params) {
|
|
459
|
+
if (!params.refreshToken) return;
|
|
460
|
+
await revokeRefreshTokenByHash({
|
|
461
|
+
hashedToken: hashOpaqueToken(params.refreshToken)
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
export async function getCurrentUser(params) {
|
|
465
|
+
const user = await findUserById({
|
|
466
|
+
id: params.userId
|
|
467
|
+
});
|
|
468
|
+
if (!user) {
|
|
469
|
+
throw new UserNotFoundError(params.userId);
|
|
470
|
+
}
|
|
471
|
+
return {
|
|
472
|
+
user
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/auth.ts"],"sourcesContent":["import {EMAIL_VERIFICATION_RESEND_COOLDOWN_SECONDS, emailSchema} from '@shipfox/api-auth-dto';\nimport {\n acceptWorkspaceInvitation,\n InvitationEmailMismatchError,\n listMembershipsByUser,\n peekInvitationByRawToken,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError as WorkspacesTokenInvalidError,\n} from '@shipfox/api-workspaces';\nimport {generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {config} from '#config.js';\nimport {\n consumeEmailVerification,\n createEmailVerification,\n createResendEmailVerification,\n} from '#db/email-verifications.js';\nimport {consumePasswordReset, createPasswordReset} from '#db/password-resets.js';\nimport {\n createRefreshToken,\n findActiveRefreshTokenByHash,\n findRefreshTokenByHash,\n revokeRefreshTokenByHash,\n revokeRefreshTokensForUser,\n rotateRefreshToken,\n} from '#db/refresh-tokens.js';\nimport {\n createUser as createDbUser,\n findUserByEmail,\n findUserById,\n markEmailVerified,\n provisionUser as provisionDbUser,\n updateUserPassword,\n} from '#db/users.js';\nimport {type AuthTokenRefreshOutcome, recordTokenRefreshed} from '#metrics/index.js';\nimport type {RefreshToken} from './entities/refresh-token.js';\nimport type {User} from './entities/user.js';\nimport {\n AuthDependencyUnavailableError,\n EmailNotVerifiedError,\n EmailTakenError,\n InvalidCredentialsError,\n TokenInvalidError,\n UserNotFoundError,\n} from './errors.js';\nimport {signUserToken} from './jwt.js';\nimport {hashPassword, verifyPassword} from './password.js';\n\nconst VERIFICATION_TTL_HOURS = 24;\nconst RESET_TTL_HOURS = 1;\n\nlet dummyHashCache: string | undefined;\nasync function getDummyHash(): Promise<string> {\n if (!dummyHashCache) {\n dummyHashCache = await hashPassword({password: 'dummy-for-timing-parity'});\n }\n return dummyHashCache;\n}\n\nfunction hoursFromNow(hours: number): Date {\n return new Date(Date.now() + hours * 60 * 60 * 1000);\n}\n\nfunction daysFromNow(days: number): Date {\n return new Date(Date.now() + days * 24 * 60 * 60 * 1000);\n}\n\nfunction recordRefreshOutcome(outcome: AuthTokenRefreshOutcome): void {\n recordTokenRefreshed(outcome);\n}\n\nasync function signAccessToken(user: User): Promise<string> {\n const memberships = await listMembershipsByUser({userId: user.id}).catch((error: unknown) => {\n throw new AuthDependencyUnavailableError('workspaces', error);\n });\n return await signUserToken({\n userId: user.id,\n email: user.email,\n name: user.name,\n memberships: memberships.map((m) => ({workspaceId: m.workspaceId, role: 'admin' as const})),\n secret: config.AUTH_JWT_SECRET,\n expiresIn: config.AUTH_JWT_EXPIRES_IN,\n });\n}\n\nfunction isWithinRotationGrace(refreshToken: RefreshToken): boolean {\n if (!refreshToken.rotatedAt) return false;\n const graceMs = config.AUTH_REFRESH_ROTATION_GRACE_SECONDS * 1000;\n return Date.now() - refreshToken.rotatedAt.getTime() <= graceMs;\n}\n\nasync function createRefreshSession(user: User): Promise<string> {\n const refreshToken = generateOpaqueToken('refreshToken');\n await createRefreshToken({\n userId: user.id,\n hashedToken: hashOpaqueToken(refreshToken),\n expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS),\n });\n return refreshToken;\n}\n\nfunction verificationLink(rawToken: string): string {\n return `${config.CLIENT_BASE_URL}/auth/verify-email?token=${rawToken}`;\n}\n\nfunction passwordResetLink(rawToken: string): string {\n return `${config.CLIENT_BASE_URL}/auth/reset?token=${rawToken}`;\n}\n\nasync function createAndQueueEmailVerification(user: User): Promise<void> {\n const rawToken = generateOpaqueToken('emailVerification');\n await createEmailVerification({\n userId: user.id,\n hashedToken: hashOpaqueToken(rawToken),\n expiresAt: hoursFromNow(VERIFICATION_TTL_HOURS),\n sendEmail: {\n email: user.email,\n verifyLink: verificationLink(rawToken),\n },\n });\n}\n\nexport interface SignupParams {\n email: string;\n password: string;\n name?: string | undefined;\n}\n\nexport interface ProvisionUserParams {\n email: string;\n name?: string | null | undefined;\n}\n\n/**\n * Creates a verified, password-less user for an external identity provider.\n * Existing users are returned unchanged, including their password and profile.\n */\nexport async function provisionUser(params: ProvisionUserParams): Promise<User> {\n return await provisionDbUser({\n email: emailSchema.parse(params.email),\n name: params.name ?? null,\n });\n}\n\nexport async function signup(params: SignupParams): Promise<User> {\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n });\n\n await createAndQueueEmailVerification(user);\n\n return user;\n}\n\nexport interface SignupWithInvitationParams extends SignupParams {\n invitationToken: string;\n}\n\nexport interface SignupWithInvitationResult extends LoginResult {\n membership: {id: string; userId: string; workspaceId: string} | null;\n acceptError?: {code: string; message: string};\n}\n\nexport async function signupWithInvitation(\n params: SignupWithInvitationParams,\n): Promise<SignupWithInvitationResult> {\n // Step 1: Pre-validate the invitation BEFORE any user write so an invalid\n // token never produces an orphan user. Re-validation happens again inside\n // acceptWorkspaceInvitation (race-safe) after the user is created.\n const invitation = await peekInvitationByRawToken({token: params.invitationToken});\n if (!invitation) {\n throw new WorkspacesTokenInvalidError('Invitation token is invalid');\n }\n if (invitation.acceptedAt !== null) {\n throw new TokenAlreadyUsedError();\n }\n if (invitation.expiresAt.getTime() <= Date.now()) {\n throw new TokenExpiredError();\n }\n if (invitation.email !== params.email) {\n throw new InvitationEmailMismatchError();\n }\n\n // Step 2: Create the user as verified. The invitation email is the proof of\n // ownership, and this avoids a second auth-table write just to mark verified.\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n emailVerifiedAt: new Date(),\n });\n\n // Step 3: Accept the invitation through the workspaces module boundary.\n // This is intentionally not one database transaction with auth; treat it like\n // calling another service. If it fails after user creation, return a verified\n // signed-in user and an explicit retryable accept error.\n let membership: SignupWithInvitationResult['membership'] = null;\n let acceptError: SignupWithInvitationResult['acceptError'];\n try {\n const result = await acceptWorkspaceInvitation({\n token: params.invitationToken,\n userId: user.id,\n email: user.email,\n name: user.name,\n });\n membership = {\n id: result.membership.id,\n userId: result.membership.userId,\n workspaceId: result.membership.workspaceId,\n };\n } catch (error) {\n if (\n error instanceof WorkspacesTokenInvalidError ||\n error instanceof TokenAlreadyUsedError ||\n error instanceof TokenExpiredError ||\n error instanceof InvitationEmailMismatchError\n ) {\n acceptError = {code: error.name, message: error.message};\n } else {\n acceptError = {\n code: 'AcceptFailed',\n message: 'Could not accept the invitation; please retry from the invite link.',\n };\n }\n }\n\n // Step 4: Issue session. signAccessToken reads memberships through the\n // workspaces module API, so a successful accept is reflected in the JWT.\n const token = await signAccessToken(user);\n const refreshToken = await createRefreshSession(user);\n\n if (acceptError) {\n return {token, refreshToken, user, membership, acceptError};\n }\n return {token, refreshToken, user, membership};\n}\n\nexport interface CreateUserParams extends SignupParams {\n verified: boolean;\n}\n\nexport async function createUser(params: CreateUserParams): Promise<User> {\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n emailVerifiedAt: params.verified ? new Date() : null,\n });\n\n return user;\n}\n\nexport interface LoginParams {\n email: string;\n password: string;\n}\n\nexport interface LoginResult {\n token: string;\n refreshToken: string;\n user: User;\n}\n\nexport async function login(params: LoginParams): Promise<LoginResult> {\n const user = await findUserByEmail({email: params.email});\n\n if (!user) {\n await verifyPassword({password: params.password, hash: await getDummyHash()});\n throw new InvalidCredentialsError();\n }\n\n const hasPassword = user.hashedPassword !== null;\n const ok = await verifyPassword({\n password: params.password,\n hash: user.hashedPassword ?? (await getDummyHash()),\n });\n if (!hasPassword || !ok || user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n\n const token = await signAccessToken(user);\n const refreshToken = await createRefreshSession(user);\n\n return {token, refreshToken, user};\n}\n\nexport interface CreateSessionForUserParams {\n userId?: string | undefined;\n email?: string | undefined;\n}\n\nexport interface CreateSessionForUserResult {\n token: string;\n refreshToken: string;\n user: User;\n}\n\nexport type CreateSessionForUserError =\n | AuthDependencyUnavailableError\n | EmailNotVerifiedError\n | InvalidCredentialsError\n | UserNotFoundError;\n\nexport async function createSessionForUser(\n params: CreateSessionForUserParams,\n): Promise<CreateSessionForUserResult> {\n const user = params.userId\n ? await findUserById({id: params.userId})\n : params.email\n ? await findUserByEmail({email: params.email})\n : undefined;\n\n if (!user) {\n throw new UserNotFoundError(params.userId ?? params.email ?? 'unknown');\n }\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n if (user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n const token = await signAccessToken(user);\n const refreshToken = await createRefreshSession(user);\n\n return {token, refreshToken, user};\n}\n\nexport interface RefreshAccessTokenResult {\n token: string;\n /** Undefined on a grace-window hit: keep the existing cookie instead of rotating it. */\n refreshToken: string | undefined;\n user: User;\n}\n\nexport async function refreshAccessToken(params: {\n refreshToken: string;\n}): Promise<RefreshAccessTokenResult> {\n const currentHashedToken = hashOpaqueToken(params.refreshToken);\n const current = await findRefreshTokenByHash({hashedToken: currentHashedToken});\n if (!current) {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n\n const user = await findUserById({id: current.userId});\n if (user?.status !== 'active') {\n await revokeRefreshTokenByHash({hashedToken: currentHashedToken});\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n\n // Within the grace window a rotated token means a concurrent refresh (e.g. a\n // second tab); past it, reuse of a retired token means a compromised session.\n if (current.rotatedAt) {\n if (isWithinRotationGrace(current)) {\n const token = await signAccessToken(user);\n recordRefreshOutcome('grace');\n return {token, refreshToken: undefined, user};\n }\n await revokeRefreshTokensForUser({userId: user.id});\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token reused after rotation');\n }\n\n // Losing the CAS requires a state check: another request may have rotated,\n // revoked, or expired the token before this refresh could claim it.\n const nextRefreshToken = generateOpaqueToken('refreshToken');\n const rotated = await rotateRefreshToken({\n id: current.id,\n currentHashedToken,\n nextHashedToken: hashOpaqueToken(nextRefreshToken),\n expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS),\n });\n if (!rotated) {\n const latest = await findRefreshTokenByHash({hashedToken: currentHashedToken});\n if (!latest || !isWithinRotationGrace(latest)) {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const token = await signAccessToken(user);\n recordRefreshOutcome('grace');\n return {token, refreshToken: undefined, user};\n }\n\n const token = await signAccessToken(user);\n recordRefreshOutcome('rotated');\n return {token, refreshToken: nextRefreshToken, user};\n}\n\nexport interface ConfirmEmailVerificationResult {\n token: string;\n refreshToken: string;\n user: User;\n}\n\nexport interface ResendEmailVerificationResult {\n nextResendAvailableAt: Date;\n}\n\nexport async function confirmEmailVerification(params: {\n token: string;\n}): Promise<ConfirmEmailVerificationResult> {\n const consumed = await consumeEmailVerification({hashedToken: hashOpaqueToken(params.token)});\n if (!consumed) {\n throw new TokenInvalidError('Verification token is invalid or expired');\n }\n\n const user = await markEmailVerified({userId: consumed.userId});\n if (user?.status !== 'active') {\n throw new TokenInvalidError('Verification token is invalid or expired');\n }\n\n const token = await signAccessToken(user);\n const refreshToken = await createRefreshSession(user);\n\n return {token, refreshToken, user};\n}\n\nexport async function resendEmailVerification(params: {\n email: string;\n}): Promise<ResendEmailVerificationResult> {\n const rawToken = generateOpaqueToken('emailVerification');\n const result = await createResendEmailVerification({\n email: params.email,\n hashedToken: hashOpaqueToken(rawToken),\n expiresAt: hoursFromNow(VERIFICATION_TTL_HOURS),\n cooldownSeconds: EMAIL_VERIFICATION_RESEND_COOLDOWN_SECONDS,\n sendEmail: {\n verifyLink: verificationLink(rawToken),\n },\n });\n\n return {nextResendAvailableAt: result.nextResendAvailableAt};\n}\n\nexport async function requestPasswordReset(params: {email: string}): Promise<void> {\n const user = await findUserByEmail({email: params.email});\n if (user?.status !== 'active' || user.hashedPassword === null) {\n return;\n }\n\n const rawToken = generateOpaqueToken('passwordReset');\n await createPasswordReset({\n userId: user.id,\n hashedToken: hashOpaqueToken(rawToken),\n expiresAt: hoursFromNow(RESET_TTL_HOURS),\n sendEmail: {\n email: user.email,\n resetLink: passwordResetLink(rawToken),\n expiresInHours: RESET_TTL_HOURS,\n },\n });\n}\n\nexport interface ConfirmPasswordResetResult {\n token: string;\n refreshToken: string;\n user: User;\n}\n\nexport async function confirmPasswordReset(params: {\n token: string;\n newPassword: string;\n}): Promise<ConfirmPasswordResetResult> {\n const consumed = await consumePasswordReset({hashedToken: hashOpaqueToken(params.token)});\n if (!consumed) {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n const existingUser = await findUserById({id: consumed.userId});\n if (existingUser?.status !== 'active' || existingUser.hashedPassword === null) {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n const hashedPassword = await hashPassword({password: params.newPassword});\n const user = await updateUserPassword({userId: consumed.userId, hashedPassword});\n if (user?.status !== 'active') {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n await revokeRefreshTokensForUser({userId: consumed.userId});\n\n const token = await signAccessToken(user);\n const refreshToken = await createRefreshSession(user);\n\n return {token, refreshToken, user};\n}\n\nexport async function changePassword(params: {\n userId: string;\n currentPassword: string;\n newPassword: string;\n refreshToken?: string | undefined;\n}): Promise<void> {\n const user = await findUserById({id: params.userId});\n if (!user) {\n throw new UserNotFoundError(params.userId);\n }\n\n const hasPassword = user.hashedPassword !== null;\n const ok = await verifyPassword({\n password: params.currentPassword,\n hash: user.hashedPassword ?? (await getDummyHash()),\n });\n if (!hasPassword || !ok) {\n throw new InvalidCredentialsError();\n }\n\n const hashedPassword = await hashPassword({password: params.newPassword});\n const currentRefreshSession = params.refreshToken\n ? await findActiveRefreshTokenByHash({hashedToken: hashOpaqueToken(params.refreshToken)})\n : undefined;\n const exceptRefreshTokenId =\n currentRefreshSession?.userId === user.id ? currentRefreshSession.id : undefined;\n\n await updateUserPassword({userId: user.id, hashedPassword});\n await revokeRefreshTokensForUser({userId: user.id, exceptRefreshTokenId});\n}\n\nexport async function logout(params: {refreshToken?: string | undefined}): Promise<void> {\n if (!params.refreshToken) return;\n await revokeRefreshTokenByHash({hashedToken: hashOpaqueToken(params.refreshToken)});\n}\n\nexport interface GetCurrentUserResult {\n user: User;\n}\n\nexport async function getCurrentUser(params: {userId: string}): Promise<GetCurrentUserResult> {\n const user = await findUserById({id: params.userId});\n if (!user) {\n throw new UserNotFoundError(params.userId);\n }\n\n return {user};\n}\n"],"names":["EMAIL_VERIFICATION_RESEND_COOLDOWN_SECONDS","emailSchema","acceptWorkspaceInvitation","InvitationEmailMismatchError","listMembershipsByUser","peekInvitationByRawToken","TokenAlreadyUsedError","TokenExpiredError","TokenInvalidError","WorkspacesTokenInvalidError","generateOpaqueToken","hashOpaqueToken","config","consumeEmailVerification","createEmailVerification","createResendEmailVerification","consumePasswordReset","createPasswordReset","createRefreshToken","findActiveRefreshTokenByHash","findRefreshTokenByHash","revokeRefreshTokenByHash","revokeRefreshTokensForUser","rotateRefreshToken","createUser","createDbUser","findUserByEmail","findUserById","markEmailVerified","provisionUser","provisionDbUser","updateUserPassword","recordTokenRefreshed","AuthDependencyUnavailableError","EmailNotVerifiedError","EmailTakenError","InvalidCredentialsError","UserNotFoundError","signUserToken","hashPassword","verifyPassword","VERIFICATION_TTL_HOURS","RESET_TTL_HOURS","dummyHashCache","getDummyHash","password","hoursFromNow","hours","Date","now","daysFromNow","days","recordRefreshOutcome","outcome","signAccessToken","user","memberships","userId","id","catch","error","email","name","map","m","workspaceId","role","secret","AUTH_JWT_SECRET","expiresIn","AUTH_JWT_EXPIRES_IN","isWithinRotationGrace","refreshToken","rotatedAt","graceMs","AUTH_REFRESH_ROTATION_GRACE_SECONDS","getTime","createRefreshSession","hashedToken","expiresAt","AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS","verificationLink","rawToken","CLIENT_BASE_URL","passwordResetLink","createAndQueueEmailVerification","sendEmail","verifyLink","params","parse","signup","existing","hashedPassword","signupWithInvitation","invitation","token","invitationToken","acceptedAt","emailVerifiedAt","membership","acceptError","result","code","message","verified","login","hash","hasPassword","ok","status","createSessionForUser","undefined","refreshAccessToken","currentHashedToken","current","nextRefreshToken","rotated","nextHashedToken","latest","confirmEmailVerification","consumed","resendEmailVerification","cooldownSeconds","nextResendAvailableAt","requestPasswordReset","resetLink","expiresInHours","confirmPasswordReset","existingUser","newPassword","changePassword","currentPassword","currentRefreshSession","exceptRefreshTokenId","logout","getCurrentUser"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-verification.d.ts","sourceRoot":"","sources":["../../../src/core/entities/email-verification.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,IAAI,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;CACjB"}
|