Skip to content

Commit 305388e

Browse files
committed
refactor(auth): make code verification context-aware
Refactors the `AuthService.completeEmailSignIn` method to be context-aware based on an `isDashboardLogin` flag. - The method now accepts `isDashboardLogin` instead of `clientType` and `currentAuthUser`. - If `isDashboardLogin` is true, the method only attempts to find and log in an existing user. It will not create a new account. - If `isDashboardLogin` is false, it performs the standard sign-in/sign-up flow for the user-facing app. - A special case is added to grant 'admin' role to a new user signing up with '[email protected]' for easier in-memory setup. - The redundant logic for anonymous user account linking has been removed, as it is correctly handled by `completeLinkEmailProcess`.
1 parent 0a93674 commit 305388e

File tree

1 file changed

+64
-132
lines changed

1 file changed

+64
-132
lines changed

lib/src/services/auth_service.dart

Lines changed: 64 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,29 @@ class AuthService {
121121

122122
/// Completes the email sign-in process by verifying the code.
123123
///
124-
/// If the code is valid, finds or creates the user, generates an auth token.
125-
/// Returns the authenticated User and the generated token.
124+
/// This method is context-aware based on the [isDashboardLogin] flag.
125+
///
126+
/// - If `isDashboardLogin` is `true`, it validates the code and logs in the
127+
/// existing user. It will not create a new user.
128+
/// - If `isDashboardLogin` is `false` (default), it validates the code and
129+
/// either logs in the existing user or creates a new one if they don't
130+
/// exist.
131+
///
132+
/// As a special case for in-memory setup, if a new user signs up with the
133+
/// email '[email protected]', they will be granted the 'admin' role.
134+
///
135+
/// Returns the authenticated [User] and a new authentication token.
136+
///
126137
/// Throws [InvalidInputException] if the code is invalid or expired.
127-
/// Throws [AuthenticationException] for specific code mismatch.
138+
/// Throws [UnauthorizedException] if `isDashboardLogin` is true and the user
139+
/// is not found (as a safeguard).
128140
/// Throws [OperationFailedException] for user lookup/creation or token errors.
129141
Future<({User user, String token})> completeEmailSignIn(
130142
String email,
131143
String code, {
132-
User? currentAuthUser, // Parameter for potential future linking logic
133-
String? clientType, // e.g., 'dashboard', 'mobile_app'
144+
// Flag to indicate if this is a login attempt from the dashboard,
145+
// which enforces stricter checks.
146+
bool isDashboardLogin = false,
134147
}) async {
135148
// 1. Validate the code for standard sign-in
136149
final isValidCode = await _verificationCodeStorageService
@@ -151,146 +164,65 @@ class AuthService {
151164
);
152165
}
153166

154-
// 2. Find or create the user, and migrate data if anonymous
167+
// 2. Find or create the user based on the context
155168
User user;
156169
try {
157-
if (currentAuthUser != null &&
158-
currentAuthUser.roles.contains(UserRoles.guestUser)) {
159-
// This is an anonymous user linking their account.
160-
// Migrate their existing data to the new permanent user.
161-
print(
162-
'Anonymous user ${currentAuthUser.id} is linking email $email. '
163-
'Migrating data...',
164-
);
170+
// Attempt to find user by email
171+
final query = {'email': email};
172+
final paginatedResponse = await _userRepository.readAllByQuery(query);
165173

166-
// Fetch existing settings and preferences for the anonymous user
167-
UserAppSettings? existingAppSettings;
168-
UserContentPreferences? existingUserPreferences;
169-
try {
170-
existingAppSettings = await _userAppSettingsRepository.read(
171-
id: currentAuthUser.id,
172-
userId: currentAuthUser.id,
173-
);
174-
existingUserPreferences = await _userContentPreferencesRepository
175-
.read(id: currentAuthUser.id, userId: currentAuthUser.id);
176-
print(
177-
'Fetched existing settings and preferences for anonymous user '
178-
'${currentAuthUser.id}.',
179-
);
180-
} on NotFoundException {
181-
print(
182-
'No existing settings/preferences found for anonymous user '
183-
'${currentAuthUser.id}. Creating new ones.',
184-
);
185-
// If not found, proceed to create new ones later.
186-
} catch (e) {
174+
if (paginatedResponse.items.isNotEmpty) {
175+
user = paginatedResponse.items.first;
176+
print('Found existing user: ${user.id} for email $email');
177+
} else {
178+
// User not found.
179+
if (isDashboardLogin) {
180+
// This should not happen if the request-code flow is correct.
181+
// It's a safeguard.
187182
print(
188-
'Error fetching existing settings/preferences for anonymous user '
189-
'${currentAuthUser.id}: $e',
183+
'Error: Dashboard login verification failed for non-existent user $email.',
190184
);
191-
// Log and continue, new defaults will be created.
185+
throw const UnauthorizedException('User account does not exist.');
192186
}
193187

194-
// Update the existing anonymous user to be permanent
195-
user = currentAuthUser.copyWith(
188+
// Create a new user for the standard app flow.
189+
print('User not found for $email, creating new user.');
190+
191+
// Hardcoded admin email check for in-memory setup.
192+
const adminEmail = '[email protected]';
193+
final roles = (email == adminEmail)
194+
? [UserRoles.standardUser, UserRoles.admin]
195+
: [UserRoles.standardUser];
196+
197+
user = User(
198+
id: _uuid.v4(),
196199
email: email,
197-
roles: [UserRoles.standardUser],
200+
roles: roles,
198201
);
199-
user = await _userRepository.update(id: user.id, item: user);
200-
print(
201-
'Updated anonymous user ${user.id} to permanent with email $email.',
202+
user = await _userRepository.create(item: user);
203+
print('Created new user: ${user.id} with roles: ${user.roles}');
204+
205+
// Create default UserAppSettings for the new user
206+
final defaultAppSettings = UserAppSettings(id: user.id);
207+
await _userAppSettingsRepository.create(
208+
item: defaultAppSettings,
209+
userId: user.id,
202210
);
211+
print('Created default UserAppSettings for user: ${user.id}');
203212

204-
// Update or create UserAppSettings for the now-permanent user
205-
if (existingAppSettings != null) {
206-
// Update existing settings with the new user ID (though it's the same)
207-
// and persist.
208-
await _userAppSettingsRepository.update(
209-
id: existingAppSettings.id,
210-
item: existingAppSettings.copyWith(id: user.id),
211-
userId: user.id,
212-
);
213-
print('Migrated UserAppSettings for user: ${user.id}');
214-
} else {
215-
// Create default settings if none existed for the anonymous user
216-
final defaultAppSettings = UserAppSettings(id: user.id);
217-
await _userAppSettingsRepository.create(
218-
item: defaultAppSettings,
219-
userId: user.id,
220-
);
221-
print('Created default UserAppSettings for user: ${user.id}');
222-
}
223-
224-
// Update or create UserContentPreferences for the now-permanent user
225-
if (existingUserPreferences != null) {
226-
// Update existing preferences with the new user ID (though it's the same)
227-
// and persist.
228-
await _userContentPreferencesRepository.update(
229-
id: existingUserPreferences.id,
230-
item: existingUserPreferences.copyWith(id: user.id),
231-
userId: user.id,
232-
);
233-
print('Migrated UserContentPreferences for user: ${user.id}');
234-
} else {
235-
// Create default preferences if none existed for the anonymous user
236-
final defaultUserPreferences = UserContentPreferences(id: user.id);
237-
await _userContentPreferencesRepository.create(
238-
item: defaultUserPreferences,
239-
userId: user.id,
240-
);
241-
print('Created default UserContentPreferences for user: ${user.id}');
242-
}
243-
} else {
244-
// Standard sign-in/sign-up flow (not anonymous linking)
245-
// Attempt to find user by email
246-
final query = {'email': email};
247-
final paginatedResponse = await _userRepository.readAllByQuery(query);
248-
249-
if (paginatedResponse.items.isNotEmpty) {
250-
user = paginatedResponse.items.first;
251-
print('Found existing user: ${user.id} for email $email');
252-
} else {
253-
// User not found, create a new one
254-
print('User not found for $email, creating new user.');
255-
// Assign roles based on client type. New users from the dashboard
256-
// could be granted publisher rights, for example.
257-
final roles = (clientType == 'dashboard')
258-
? [UserRoles.standardUser, UserRoles.publisher]
259-
: [UserRoles.standardUser];
260-
user = User(
261-
id: _uuid.v4(), // Generate new ID
262-
email: email,
263-
roles: roles,
264-
);
265-
user = await _userRepository.create(item: user); // Save the new user
266-
print('Created new user: ${user.id}');
267-
268-
// Create default UserAppSettings for the new user
269-
final defaultAppSettings = UserAppSettings(id: user.id);
270-
await _userAppSettingsRepository.create(
271-
item: defaultAppSettings,
272-
userId: user.id, // Pass user ID for scoping
273-
);
274-
print('Created default UserAppSettings for user: ${user.id}');
275-
276-
// Create default UserContentPreferences for the new user
277-
final defaultUserPreferences = UserContentPreferences(id: user.id);
278-
await _userContentPreferencesRepository.create(
279-
item: defaultUserPreferences,
280-
userId: user.id, // Pass user ID for scoping
281-
);
282-
print('Created default UserContentPreferences for user: ${user.id}');
283-
}
213+
// Create default UserContentPreferences for the new user
214+
final defaultUserPreferences = UserContentPreferences(id: user.id);
215+
await _userContentPreferencesRepository.create(
216+
item: defaultUserPreferences,
217+
userId: user.id,
218+
);
219+
print('Created default UserContentPreferences for user: ${user.id}');
284220
}
285221
} on HtHttpException catch (e) {
286-
print('Error finding/creating/migrating user for $email: $e');
287-
throw const OperationFailedException(
288-
'Failed to find, create, or migrate user account.',
289-
);
222+
print('Error finding/creating user for $email: $e');
223+
throw const OperationFailedException('Failed to find or create user account.');
290224
} catch (e) {
291-
print(
292-
'Unexpected error during user lookup/creation/migration for $email: $e',
293-
);
225+
print('Unexpected error during user lookup/creation for $email: $e');
294226
throw const OperationFailedException('Failed to process user account.');
295227
}
296228

@@ -304,7 +236,7 @@ class AuthService {
304236
throw const OperationFailedException(
305237
'Failed to generate authentication token.',
306238
);
307-
}
239+
}
308240
}
309241

310242
/// Performs anonymous sign-in.

0 commit comments

Comments
 (0)