@umituz/react-native-firebase 2.6.3 → 2.6.4
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/package.json +1 -1
- package/src/application/auth/index.ts +2 -34
- package/src/application/auth/use-cases/index.ts +1 -21
- package/src/domains/account-deletion/domain/index.ts +1 -8
- package/src/domains/account-deletion/index.ts +0 -42
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor.ts +79 -0
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionTypes.ts +0 -1
- package/src/domains/account-deletion/infrastructure/services/account-deletion.service.ts +2 -14
- package/src/domains/auth/index.ts +3 -12
- package/src/domains/auth/infrastructure.ts +11 -0
- package/src/domains/firestore/domain/entities/Collection.ts +0 -2
- package/src/domains/firestore/domain/index.ts +6 -2
- package/src/domains/firestore/domain/value-objects/WhereClause.ts +0 -14
- package/src/domains/firestore/presentation/hooks/useFirestoreMutation.ts +1 -1
- package/src/domains/firestore/presentation/hooks/useFirestoreQuery.ts +1 -1
- package/src/shared/infrastructure/config/base/ServiceClientSingleton.ts +29 -0
- package/src/application/auth/ports/AuthPort.ts.bak +0 -164
- package/src/application/auth/ports/AuthPort_part_aa +0 -150
- package/src/application/auth/ports/AuthPort_part_ab +0 -14
- package/src/application/auth/use-cases/SignInUseCase.ts.bak +0 -253
- package/src/application/auth/use-cases/SignInUseCaseHelpers.ts +0 -0
- package/src/application/auth/use-cases/SignInUseCaseMain.ts +0 -0
- package/src/application/auth/use-cases/SignInUseCase_part_aa +0 -150
- package/src/application/auth/use-cases/SignInUseCase_part_ab +0 -103
- package/src/application/auth/use-cases/SignOutUseCase.ts.bak +0 -288
- package/src/application/auth/use-cases/SignOutUseCaseCleanup.ts +0 -0
- package/src/application/auth/use-cases/SignOutUseCaseMain.ts +0 -0
- package/src/application/auth/use-cases/SignOutUseCase_part_aa +0 -150
- package/src/application/auth/use-cases/SignOutUseCase_part_ab +0 -138
- package/src/domains/account-deletion/domain/services/UserValidationHelpers.ts.bak +0 -181
- package/src/domains/account-deletion/domain/services/UserValidationHelpers_part_aa +0 -150
- package/src/domains/account-deletion/domain/services/UserValidationHelpers_part_ab +0 -31
- package/src/domains/account-deletion/domain/services/UserValidationService.ts.bak +0 -286
- package/src/domains/account-deletion/domain/services/UserValidationService_part_aa +0 -150
- package/src/domains/account-deletion/domain/services/UserValidationService_part_ab +0 -136
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor.ts.bak +0 -230
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_aa +0 -150
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_ab +0 -80
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler.ts.bak +0 -174
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler_part_aa +0 -150
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler_part_ab +0 -24
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository.ts.bak +0 -266
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository_part_aa +0 -150
- package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository_part_ab +0 -116
- package/src/domains/account-deletion/infrastructure/services/reauthentication.service.ts.bak +0 -160
- package/src/domains/account-deletion/infrastructure/services/reauthentication.service_part_aa +0 -150
- package/src/domains/account-deletion/infrastructure/services/reauthentication.service_part_ab +0 -10
- package/src/domains/auth/infrastructure.ts.bak +0 -156
- package/src/domains/auth/infrastructure_part_aa +0 -150
- package/src/domains/auth/infrastructure_part_ab +0 -6
- package/src/domains/auth/presentation/hooks/GoogleOAuthHelpers.ts +0 -0
- package/src/domains/auth/presentation/hooks/GoogleOAuthHookService.ts.bak +0 -247
- package/src/domains/auth/presentation/hooks/GoogleOAuthHookService_part_aa +0 -150
- package/src/domains/auth/presentation/hooks/GoogleOAuthHookService_part_ab +0 -97
- package/src/domains/auth/presentation/hooks/GoogleOAuthService.ts +0 -0
- package/src/domains/firestore/domain/entities/Collection.ts.bak +0 -288
- package/src/domains/firestore/domain/entities/Collection_part_aa +0 -150
- package/src/domains/firestore/domain/entities/Collection_part_ab +0 -138
- package/src/domains/firestore/domain/entities/Document.ts.bak +0 -233
- package/src/domains/firestore/domain/entities/DocumentHelpers.ts +0 -0
- package/src/domains/firestore/domain/entities/DocumentMain.ts +0 -0
- package/src/domains/firestore/domain/entities/Document_part_aa +0 -150
- package/src/domains/firestore/domain/entities/Document_part_ab +0 -83
- package/src/domains/firestore/domain/services/QueryService.ts.bak +0 -182
- package/src/domains/firestore/domain/services/QueryServiceAnalysis.ts.bak +0 -169
- package/src/domains/firestore/domain/services/QueryServiceAnalysis_part_aa +0 -150
- package/src/domains/firestore/domain/services/QueryServiceAnalysis_part_ab +0 -19
- package/src/domains/firestore/domain/services/QueryServiceHelpers.ts.bak +0 -151
- package/src/domains/firestore/domain/services/QueryServiceHelpers_part_aa +0 -150
- package/src/domains/firestore/domain/services/QueryServiceHelpers_part_ab +0 -1
- package/src/domains/firestore/domain/services/QueryService_part_aa +0 -150
- package/src/domains/firestore/domain/services/QueryService_part_ab +0 -32
- package/src/domains/firestore/domain/value-objects/QueryOptions.ts.bak +0 -191
- package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization.ts.bak +0 -207
- package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization_part_aa +0 -150
- package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization_part_ab +0 -57
- package/src/domains/firestore/domain/value-objects/QueryOptionsValidation.ts.bak +0 -182
- package/src/domains/firestore/domain/value-objects/QueryOptionsValidation_part_aa +0 -150
- package/src/domains/firestore/domain/value-objects/QueryOptionsValidation_part_ab +0 -32
- package/src/domains/firestore/domain/value-objects/QueryOptions_part_aa +0 -150
- package/src/domains/firestore/domain/value-objects/QueryOptions_part_ab +0 -41
- package/src/domains/firestore/domain/value-objects/WhereClause.ts.bak +0 -299
- package/src/domains/firestore/domain/value-objects/WhereClauseFactory.ts.bak +0 -207
- package/src/domains/firestore/domain/value-objects/WhereClauseFactory_part_aa +0 -150
- package/src/domains/firestore/domain/value-objects/WhereClauseFactory_part_ab +0 -57
- package/src/domains/firestore/domain/value-objects/WhereClause_part_aa +0 -150
- package/src/domains/firestore/domain/value-objects/WhereClause_part_ab +0 -149
- package/src/shared/infrastructure/base/ErrorHandler.ts.bak +0 -189
- package/src/shared/infrastructure/base/ErrorHandler_part_aa +0 -150
- package/src/shared/infrastructure/base/ErrorHandler_part_ab +0 -39
- package/src/shared/infrastructure/base/ServiceBase.ts.bak +0 -220
- package/src/shared/infrastructure/base/ServiceBase_part_aa +0 -150
- package/src/shared/infrastructure/base/ServiceBase_part_ab +0 -70
- package/src/shared/infrastructure/config/base/ServiceClientSingleton.ts.bak +0 -155
- package/src/shared/infrastructure/config/base/ServiceClientSingleton_part_aa +0 -150
- package/src/shared/infrastructure/config/base/ServiceClientSingleton_part_ab +0 -5
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* User Validation Service (Main)
|
|
3
|
-
* Single Responsibility: Validate user state for account operations
|
|
4
|
-
*
|
|
5
|
-
* Domain service that encapsulates user validation logic.
|
|
6
|
-
* Moves business logic from infrastructure to domain layer.
|
|
7
|
-
*
|
|
8
|
-
* Max lines: 150 (enforced for maintainability)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { Auth, User } from 'firebase/auth';
|
|
12
|
-
import type { Result } from '../../../../shared/domain/utils';
|
|
13
|
-
import { successResult, failureResultFrom } from '../../../../shared/domain/utils';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* User validation service
|
|
17
|
-
* Provides user validation functionality for account operations
|
|
18
|
-
*/
|
|
19
|
-
export class UserValidationService {
|
|
20
|
-
/**
|
|
21
|
-
* Validate user is ready for deletion
|
|
22
|
-
* Checks user exists, not anonymous, and has required provider
|
|
23
|
-
*/
|
|
24
|
-
validateForDeletion(user: User | null): Result<{ userId: string; provider: string }> {
|
|
25
|
-
if (!user) {
|
|
26
|
-
return failureResultFrom('auth/not-ready', 'No authenticated user');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (user.isAnonymous) {
|
|
30
|
-
return failureResultFrom('auth/anonymous', 'Cannot delete anonymous account');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const provider = this.getUserAuthProvider(user);
|
|
34
|
-
if (!provider) {
|
|
35
|
-
return failureResultFrom('auth/unsupported', 'Unsupported auth provider');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return successResult({
|
|
39
|
-
userId: user.uid,
|
|
40
|
-
provider,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Validate user hasn't changed during operation
|
|
46
|
-
* Critical for preventing race conditions
|
|
47
|
-
*/
|
|
48
|
-
validateUserUnchanged(auth: Auth | null, originalUserId: string): Result<void> {
|
|
49
|
-
if (!auth) {
|
|
50
|
-
return failureResultFrom('auth/not-ready', 'Auth not initialized');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const currentUserId = auth.currentUser?.uid;
|
|
54
|
-
|
|
55
|
-
if (!currentUserId) {
|
|
56
|
-
return failureResultFrom('auth/user-signed-out', 'User signed out during operation');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (currentUserId !== originalUserId) {
|
|
60
|
-
return failureResultFrom('auth/user-changed', 'User changed during operation');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return successResult();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Validate user credentials are present
|
|
68
|
-
*/
|
|
69
|
-
validateCredentials(
|
|
70
|
-
user: User,
|
|
71
|
-
options: {
|
|
72
|
-
password?: string;
|
|
73
|
-
googleIdToken?: string;
|
|
74
|
-
}
|
|
75
|
-
): Result<void> {
|
|
76
|
-
const provider = this.getUserAuthProvider(user);
|
|
77
|
-
|
|
78
|
-
if (provider === 'password' && !options.password) {
|
|
79
|
-
return failureResultFrom('auth/password-required', 'Password required for reauthentication');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (provider === 'google.com' && !options.googleIdToken) {
|
|
83
|
-
return failureResultFrom('auth/google-token-required', 'Google ID token required for reauthentication');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return successResult();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Get user's auth provider
|
|
91
|
-
* Returns provider ID or null if unknown
|
|
92
|
-
*/
|
|
93
|
-
getUserAuthProvider(user: User): string | null {
|
|
94
|
-
if (!user.providerData || user.providerData.length === 0) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Check all providers
|
|
99
|
-
for (const userInfo of user.providerData) {
|
|
100
|
-
if (userInfo.providerId) {
|
|
101
|
-
return userInfo.providerId;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Check if user has specific provider
|
|
110
|
-
*/
|
|
111
|
-
hasProvider(user: User, providerId: string): boolean {
|
|
112
|
-
if (!user.providerData || user.providerData.length === 0) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return user.providerData.some(
|
|
117
|
-
userInfo => userInfo.providerId === providerId
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Check if user is email/password user
|
|
123
|
-
*/
|
|
124
|
-
isEmailPasswordUser(user: User): boolean {
|
|
125
|
-
return this.hasProvider(user, 'password');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Check if user is Google user
|
|
130
|
-
*/
|
|
131
|
-
isGoogleUser(user: User): boolean {
|
|
132
|
-
return this.hasProvider(user, 'google.com');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Check if user is Apple user
|
|
137
|
-
*/
|
|
138
|
-
isAppleUser(user: User): boolean {
|
|
139
|
-
return this.hasProvider(user, 'apple.com');
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Check if user requires reauthentication
|
|
144
|
-
* Based on provider and operation type
|
|
145
|
-
*/
|
|
146
|
-
requiresReauthentication(user: User, operation: 'delete' | 'update'): boolean {
|
|
147
|
-
// Email/password users always need recent auth
|
|
148
|
-
if (this.isEmailPasswordUser(user)) {
|
|
149
|
-
return true;
|
|
150
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
// Social providers may require recent auth for sensitive operations
|
|
3
|
-
if (operation === 'delete') {
|
|
4
|
-
return true;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
return false;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Validate user email
|
|
12
|
-
*/
|
|
13
|
-
validateEmail(user: User): Result<void> {
|
|
14
|
-
const email = user.email;
|
|
15
|
-
|
|
16
|
-
if (!email) {
|
|
17
|
-
return failureResultFrom('auth/no-email', 'User has no email');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
21
|
-
if (!emailRegex.test(email)) {
|
|
22
|
-
return failureResultFrom('auth/invalid-email', 'Invalid email format');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return successResult();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Validate user is verified (if applicable)
|
|
30
|
-
*/
|
|
31
|
-
validateVerified(user: User, requireVerification: boolean = false): Result<void> {
|
|
32
|
-
if (requireVerification && !user.emailVerified) {
|
|
33
|
-
return failureResultFrom('auth/unverified', 'Email not verified');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return successResult();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Get user metadata
|
|
41
|
-
*/
|
|
42
|
-
getUserMetadata(user: User): {
|
|
43
|
-
readonly createdAt: number | null;
|
|
44
|
-
readonly lastSignInAt: number | null;
|
|
45
|
-
} {
|
|
46
|
-
return {
|
|
47
|
-
createdAt: user.metadata.creationTime ? new Date(user.metadata.creationTime).getTime() : null,
|
|
48
|
-
lastSignInAt: user.metadata.lastSignInTime ? new Date(user.metadata.lastSignInTime).getTime() : null,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Check if account is new (created recently)
|
|
54
|
-
*/
|
|
55
|
-
isAccountNew(user: User, maxAgeMs: number = 24 * 60 * 60 * 1000): boolean {
|
|
56
|
-
const metadata = this.getUserMetadata(user);
|
|
57
|
-
if (!metadata.createdAt) return false;
|
|
58
|
-
|
|
59
|
-
const age = Date.now() - metadata.createdAt;
|
|
60
|
-
return age <= maxAgeMs;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Check if user recently signed in
|
|
65
|
-
*/
|
|
66
|
-
isRecentSignIn(user: User, maxAgeMs: number = 5 * 60 * 1000): boolean {
|
|
67
|
-
const metadata = this.getUserMetadata(user);
|
|
68
|
-
if (!metadata.lastSignInAt) return false;
|
|
69
|
-
|
|
70
|
-
const timeSinceSignIn = Date.now() - metadata.lastSignInAt;
|
|
71
|
-
return timeSinceSignIn <= maxAgeMs;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Validate user can perform operation
|
|
76
|
-
* Comprehensive check combining multiple validations
|
|
77
|
-
*/
|
|
78
|
-
validateCanPerformOperation(
|
|
79
|
-
user: User | null,
|
|
80
|
-
operation: 'delete' | 'update',
|
|
81
|
-
options: {
|
|
82
|
-
requireVerified?: boolean;
|
|
83
|
-
maxSignInAge?: number;
|
|
84
|
-
password?: string;
|
|
85
|
-
googleIdToken?: string;
|
|
86
|
-
} = {}
|
|
87
|
-
): Result<{ userId: string; provider: string }> {
|
|
88
|
-
// Validate user ready for operation
|
|
89
|
-
const deletionValidation = this.validateForDeletion(user);
|
|
90
|
-
if (!deletionValidation.success) {
|
|
91
|
-
return deletionValidation;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const userId = deletionValidation.data!.userId;
|
|
95
|
-
const provider = deletionValidation.data!.provider;
|
|
96
|
-
|
|
97
|
-
// Validate email
|
|
98
|
-
if (user) {
|
|
99
|
-
const emailValidation = this.validateEmail(user);
|
|
100
|
-
if (!emailValidation.success) {
|
|
101
|
-
return emailValidation;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Validate verification status
|
|
105
|
-
const verifiedValidation = this.validateVerified(user, options.requireVerified);
|
|
106
|
-
if (!verifiedValidation.success) {
|
|
107
|
-
return verifiedValidation;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Check if recent sign-in required
|
|
111
|
-
if (options.maxSignInAge && !this.isRecentSignIn(user, options.maxSignInAge)) {
|
|
112
|
-
return failureResultFrom('auth/stale-session', 'Session too old, please sign in again');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Validate credentials
|
|
116
|
-
const credentialsValidation = this.validateCredentials(user, options);
|
|
117
|
-
if (!credentialsValidation.success) {
|
|
118
|
-
return credentialsValidation;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return successResult({ userId, provider });
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Factory function to create user validation service
|
|
128
|
-
*/
|
|
129
|
-
export function createUserValidationService(): UserValidationService {
|
|
130
|
-
return new UserValidationService();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Default instance for convenience
|
|
135
|
-
*/
|
|
136
|
-
export const userValidationService = createUserValidationService();
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Account Deletion Executor (Main)
|
|
3
|
-
* Single Responsibility: Execute account deletion with retry logic
|
|
4
|
-
*
|
|
5
|
-
* Infrastructure service that executes account deletion operations.
|
|
6
|
-
* Coordinates reauthentication and deletion with error handling.
|
|
7
|
-
*
|
|
8
|
-
* Max lines: 150 (enforced for maintainability)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { User } from 'firebase/auth';
|
|
12
|
-
import { getFirebaseAuth } from '../../../auth/infrastructure/config/FirebaseAuthClient';
|
|
13
|
-
import { AccountDeletionRepository } from './AccountDeletionRepository';
|
|
14
|
-
import { userValidationService } from '../../domain/services/UserValidationService';
|
|
15
|
-
import type { Result } from '../../../../shared/domain/utils';
|
|
16
|
-
import type { AccountDeletionOptions } from '../../application/ports/reauthentication.types';
|
|
17
|
-
import type { AccountDeletionResult } from './AccountDeletionTypes';
|
|
18
|
-
import { handleReauthentication } from './AccountDeletionReauthHandler';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Account deletion executor
|
|
22
|
-
* Executes account deletion with automatic reauthentication
|
|
23
|
-
*/
|
|
24
|
-
export class AccountDeletionExecutor {
|
|
25
|
-
private readonly repository: AccountDeletionRepository;
|
|
26
|
-
private deletionInProgress = false;
|
|
27
|
-
|
|
28
|
-
constructor(repository?: AccountDeletionRepository) {
|
|
29
|
-
this.repository = repository || new AccountDeletionRepository();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Delete current user account
|
|
34
|
-
* Handles reauthentication automatically if enabled
|
|
35
|
-
*/
|
|
36
|
-
async deleteCurrentUser(
|
|
37
|
-
options: AccountDeletionOptions = { autoReauthenticate: true }
|
|
38
|
-
): Promise<AccountDeletionResult> {
|
|
39
|
-
// Prevent concurrent deletion attempts
|
|
40
|
-
if (this.deletionInProgress) {
|
|
41
|
-
return {
|
|
42
|
-
success: false,
|
|
43
|
-
error: { code: 'auth/operation-in-progress', message: 'Account deletion already in progress' },
|
|
44
|
-
requiresReauth: false,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
this.deletionInProgress = true;
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const auth = getFirebaseAuth();
|
|
52
|
-
const user = auth?.currentUser;
|
|
53
|
-
|
|
54
|
-
if (!auth || !user) {
|
|
55
|
-
return {
|
|
56
|
-
success: false,
|
|
57
|
-
error: { code: 'auth/not-ready', message: 'Auth not ready' },
|
|
58
|
-
requiresReauth: false,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const originalUserId = user.uid;
|
|
63
|
-
|
|
64
|
-
// Validate user for deletion
|
|
65
|
-
const validation = await this.repository.validateForDeletion(user);
|
|
66
|
-
if (!validation.success) {
|
|
67
|
-
return {
|
|
68
|
-
success: false,
|
|
69
|
-
error: validation.error,
|
|
70
|
-
requiresReauth: false,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const provider = validation.data!.provider;
|
|
75
|
-
|
|
76
|
-
// Check if reauthentication is needed
|
|
77
|
-
const needsReauth = this.shouldReauthenticate(user, options, provider);
|
|
78
|
-
if (needsReauth) {
|
|
79
|
-
const reauthResult = await handleReauthentication(user, options, originalUserId, this.repository);
|
|
80
|
-
if (reauthResult) {
|
|
81
|
-
return reauthResult;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Attempt deletion
|
|
86
|
-
return await this.performDeletion(user, originalUserId, options);
|
|
87
|
-
} finally {
|
|
88
|
-
this.deletionInProgress = false;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Delete specific user account
|
|
94
|
-
* Direct deletion without reauthentication
|
|
95
|
-
*/
|
|
96
|
-
async deleteUserAccount(user: User | null): Promise<AccountDeletionResult> {
|
|
97
|
-
if (!user || user.isAnonymous) {
|
|
98
|
-
return {
|
|
99
|
-
success: false,
|
|
100
|
-
error: { code: 'auth/invalid', message: 'Invalid user' },
|
|
101
|
-
requiresReauth: false,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
const result = await this.repository.deleteAccount(user);
|
|
107
|
-
if (result.success) {
|
|
108
|
-
return { success: true };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
success: false,
|
|
113
|
-
error: result.error,
|
|
114
|
-
requiresReauth: result.error?.code === 'auth/requires-recent-login',
|
|
115
|
-
};
|
|
116
|
-
} catch (error: unknown) {
|
|
117
|
-
return {
|
|
118
|
-
success: false,
|
|
119
|
-
error: {
|
|
120
|
-
code: 'auth/failed',
|
|
121
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
122
|
-
},
|
|
123
|
-
requiresReauth: false,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Check if reauthentication is needed
|
|
130
|
-
*/
|
|
131
|
-
private shouldReauthenticate(
|
|
132
|
-
user: User,
|
|
133
|
-
options: AccountDeletionOptions,
|
|
134
|
-
provider: string
|
|
135
|
-
): boolean {
|
|
136
|
-
// Password users need reauthentication
|
|
137
|
-
if (provider === 'password' && options.autoReauthenticate && options.onPasswordRequired) {
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Check if credentials are provided
|
|
142
|
-
const hasCredentials = !!(options.password || options.googleIdToken);
|
|
143
|
-
if (hasCredentials) {
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Perform account deletion
|
|
152
|
-
*/
|
|
153
|
-
private async performDeletion(
|
|
154
|
-
user: User,
|
|
155
|
-
originalUserId: string,
|
|
156
|
-
options: AccountDeletionOptions
|
|
157
|
-
): Promise<AccountDeletionResult> {
|
|
158
|
-
try {
|
|
159
|
-
// Validate user hasn't changed
|
|
160
|
-
const auth = getFirebaseAuth();
|
|
161
|
-
const validation = userValidationService.validateUserUnchanged(auth, originalUserId);
|
|
162
|
-
if (!validation.success) {
|
|
163
|
-
return {
|
|
164
|
-
success: false,
|
|
165
|
-
error: validation.error!,
|
|
166
|
-
requiresReauth: false,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Delete account
|
|
171
|
-
const result = await this.repository.deleteAccount(user);
|
|
172
|
-
if (result.success) {
|
|
173
|
-
return { success: true };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Check if reauthentication can help
|
|
177
|
-
const error = result.error;
|
|
178
|
-
if (
|
|
179
|
-
error?.code === 'auth/requires-recent-login' &&
|
|
180
|
-
options.autoReauthenticate
|
|
181
|
-
) {
|
|
182
|
-
const reauthResult = await handleReauthentication(user, options, originalUserId, this.repository);
|
|
183
|
-
if (reauthResult) {
|
|
184
|
-
return reauthResult;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return {
|
|
189
|
-
success: false,
|
|
190
|
-
error,
|
|
191
|
-
requiresReauth: error?.code === 'auth/requires-recent-login',
|
|
192
|
-
};
|
|
193
|
-
} catch (error: unknown) {
|
|
194
|
-
return {
|
|
195
|
-
success: false,
|
|
196
|
-
error: {
|
|
197
|
-
code: 'auth/failed',
|
|
198
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
199
|
-
},
|
|
200
|
-
requiresReauth: false,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Check if deletion is in progress
|
|
207
|
-
*/
|
|
208
|
-
isDeletionInProgress(): boolean {
|
|
209
|
-
return this.deletionInProgress;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Get the repository instance
|
|
214
|
-
*/
|
|
215
|
-
getRepository(): AccountDeletionRepository {
|
|
216
|
-
return this.repository;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Factory function to create account deletion executor
|
|
222
|
-
*/
|
|
223
|
-
export function createAccountDeletionExecutor(): AccountDeletionExecutor {
|
|
224
|
-
return new AccountDeletionExecutor();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Default singleton instance
|
|
229
|
-
*/
|
|
230
|
-
export const accountDeletionExecutor = createAccountDeletionExecutor();
|
package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_aa
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Account Deletion Executor (Main)
|
|
3
|
-
* Single Responsibility: Execute account deletion with retry logic
|
|
4
|
-
*
|
|
5
|
-
* Infrastructure service that executes account deletion operations.
|
|
6
|
-
* Coordinates reauthentication and deletion with error handling.
|
|
7
|
-
*
|
|
8
|
-
* Max lines: 150 (enforced for maintainability)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { User } from 'firebase/auth';
|
|
12
|
-
import { getFirebaseAuth } from '../../../auth/infrastructure/config/FirebaseAuthClient';
|
|
13
|
-
import { AccountDeletionRepository } from './AccountDeletionRepository';
|
|
14
|
-
import { userValidationService } from '../../domain/services/UserValidationService';
|
|
15
|
-
import type { Result } from '../../../../shared/domain/utils';
|
|
16
|
-
import type { AccountDeletionOptions } from '../../application/ports/reauthentication.types';
|
|
17
|
-
import type { AccountDeletionResult } from './AccountDeletionTypes';
|
|
18
|
-
import { handleReauthentication } from './AccountDeletionReauthHandler';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Account deletion executor
|
|
22
|
-
* Executes account deletion with automatic reauthentication
|
|
23
|
-
*/
|
|
24
|
-
export class AccountDeletionExecutor {
|
|
25
|
-
private readonly repository: AccountDeletionRepository;
|
|
26
|
-
private deletionInProgress = false;
|
|
27
|
-
|
|
28
|
-
constructor(repository?: AccountDeletionRepository) {
|
|
29
|
-
this.repository = repository || new AccountDeletionRepository();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Delete current user account
|
|
34
|
-
* Handles reauthentication automatically if enabled
|
|
35
|
-
*/
|
|
36
|
-
async deleteCurrentUser(
|
|
37
|
-
options: AccountDeletionOptions = { autoReauthenticate: true }
|
|
38
|
-
): Promise<AccountDeletionResult> {
|
|
39
|
-
// Prevent concurrent deletion attempts
|
|
40
|
-
if (this.deletionInProgress) {
|
|
41
|
-
return {
|
|
42
|
-
success: false,
|
|
43
|
-
error: { code: 'auth/operation-in-progress', message: 'Account deletion already in progress' },
|
|
44
|
-
requiresReauth: false,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
this.deletionInProgress = true;
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const auth = getFirebaseAuth();
|
|
52
|
-
const user = auth?.currentUser;
|
|
53
|
-
|
|
54
|
-
if (!auth || !user) {
|
|
55
|
-
return {
|
|
56
|
-
success: false,
|
|
57
|
-
error: { code: 'auth/not-ready', message: 'Auth not ready' },
|
|
58
|
-
requiresReauth: false,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const originalUserId = user.uid;
|
|
63
|
-
|
|
64
|
-
// Validate user for deletion
|
|
65
|
-
const validation = await this.repository.validateForDeletion(user);
|
|
66
|
-
if (!validation.success) {
|
|
67
|
-
return {
|
|
68
|
-
success: false,
|
|
69
|
-
error: validation.error,
|
|
70
|
-
requiresReauth: false,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const provider = validation.data!.provider;
|
|
75
|
-
|
|
76
|
-
// Check if reauthentication is needed
|
|
77
|
-
const needsReauth = this.shouldReauthenticate(user, options, provider);
|
|
78
|
-
if (needsReauth) {
|
|
79
|
-
const reauthResult = await handleReauthentication(user, options, originalUserId, this.repository);
|
|
80
|
-
if (reauthResult) {
|
|
81
|
-
return reauthResult;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Attempt deletion
|
|
86
|
-
return await this.performDeletion(user, originalUserId, options);
|
|
87
|
-
} finally {
|
|
88
|
-
this.deletionInProgress = false;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Delete specific user account
|
|
94
|
-
* Direct deletion without reauthentication
|
|
95
|
-
*/
|
|
96
|
-
async deleteUserAccount(user: User | null): Promise<AccountDeletionResult> {
|
|
97
|
-
if (!user || user.isAnonymous) {
|
|
98
|
-
return {
|
|
99
|
-
success: false,
|
|
100
|
-
error: { code: 'auth/invalid', message: 'Invalid user' },
|
|
101
|
-
requiresReauth: false,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
const result = await this.repository.deleteAccount(user);
|
|
107
|
-
if (result.success) {
|
|
108
|
-
return { success: true };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return {
|
|
112
|
-
success: false,
|
|
113
|
-
error: result.error,
|
|
114
|
-
requiresReauth: result.error?.code === 'auth/requires-recent-login',
|
|
115
|
-
};
|
|
116
|
-
} catch (error: unknown) {
|
|
117
|
-
return {
|
|
118
|
-
success: false,
|
|
119
|
-
error: {
|
|
120
|
-
code: 'auth/failed',
|
|
121
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
122
|
-
},
|
|
123
|
-
requiresReauth: false,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Check if reauthentication is needed
|
|
130
|
-
*/
|
|
131
|
-
private shouldReauthenticate(
|
|
132
|
-
user: User,
|
|
133
|
-
options: AccountDeletionOptions,
|
|
134
|
-
provider: string
|
|
135
|
-
): boolean {
|
|
136
|
-
// Password users need reauthentication
|
|
137
|
-
if (provider === 'password' && options.autoReauthenticate && options.onPasswordRequired) {
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Check if credentials are provided
|
|
142
|
-
const hasCredentials = !!(options.password || options.googleIdToken);
|
|
143
|
-
if (hasCredentials) {
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|