@@ -121,16 +121,29 @@ class AuthService {
121
121
122
122
/// Completes the email sign-in process by verifying the code.
123
123
///
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
+ ///
126
137
/// 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).
128
140
/// Throws [OperationFailedException] for user lookup/creation or token errors.
129
141
Future <({User user, String token})> completeEmailSignIn (
130
142
String email,
131
143
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 ,
134
147
}) async {
135
148
// 1. Validate the code for standard sign-in
136
149
final isValidCode = await _verificationCodeStorageService
@@ -151,146 +164,65 @@ class AuthService {
151
164
);
152
165
}
153
166
154
- // 2. Find or create the user, and migrate data if anonymous
167
+ // 2. Find or create the user based on the context
155
168
User user;
156
169
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);
165
173
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.
187
182
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 .' ,
190
184
);
191
- // Log and continue, new defaults will be created.
185
+ throw const UnauthorizedException ( 'User account does not exist.' );
192
186
}
193
187
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 (),
196
199
email: email,
197
- roles: [ UserRoles .standardUser] ,
200
+ roles: roles ,
198
201
);
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,
202
210
);
211
+ print ('Created default UserAppSettings for user: ${user .id }' );
203
212
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 }' );
284
220
}
285
221
} 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.' );
290
224
} 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 ' );
294
226
throw const OperationFailedException ('Failed to process user account.' );
295
227
}
296
228
@@ -304,7 +236,7 @@ class AuthService {
304
236
throw const OperationFailedException (
305
237
'Failed to generate authentication token.' ,
306
238
);
307
- }
239
+ }
308
240
}
309
241
310
242
/// Performs anonymous sign-in.
0 commit comments