Skip to content
3 changes: 0 additions & 3 deletions lib/src/config/app_dependencies.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import 'package:ht_email_inmemory/ht_email_inmemory.dart';
import 'package:ht_email_repository/ht_email_repository.dart';
import 'package:ht_shared/ht_shared.dart';
import 'package:logging/logging.dart';
import 'package:uuid/uuid.dart';

/// {@template app_dependencies}
/// A singleton class responsible for initializing and providing all application
Expand Down Expand Up @@ -173,7 +172,6 @@ class AppDependencies {
authTokenService = JwtAuthTokenService(
userRepository: userRepository,
blacklistService: tokenBlacklistService,
uuidGenerator: const Uuid(),
log: Logger('JwtAuthTokenService'),
);
verificationCodeStorageService = InMemoryVerificationCodeStorageService();
Expand All @@ -186,7 +184,6 @@ class AppDependencies {
emailRepository: emailRepository,
userAppSettingsRepository: userAppSettingsRepository,
userContentPreferencesRepository: userContentPreferencesRepository,
uuidGenerator: const Uuid(),
log: Logger('AuthService'),
);
dashboardSummaryService = DashboardSummaryService(
Expand Down
12 changes: 5 additions & 7 deletions lib/src/services/auth_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:ht_data_repository/ht_data_repository.dart';
import 'package:ht_email_repository/ht_email_repository.dart';
import 'package:ht_shared/ht_shared.dart';
import 'package:logging/logging.dart';
import 'package:uuid/uuid.dart';
import 'package:mongo_dart/mongo_dart.dart';

/// {@template auth_service}
/// Service responsible for orchestrating authentication logic on the backend.
Expand All @@ -25,7 +25,6 @@ class AuthService {
required HtDataRepository<UserContentPreferences>
userContentPreferencesRepository,
required PermissionService permissionService,
required Uuid uuidGenerator,
required Logger log,
}) : _userRepository = userRepository,
_authTokenService = authTokenService,
Expand All @@ -34,7 +33,6 @@ class AuthService {
_emailRepository = emailRepository,
_userAppSettingsRepository = userAppSettingsRepository,
_userContentPreferencesRepository = userContentPreferencesRepository,
_uuid = uuidGenerator,
_log = log;

final HtDataRepository<User> _userRepository;
Expand All @@ -46,7 +44,6 @@ class AuthService {
_userContentPreferencesRepository;
final PermissionService _permissionService;
final Logger _log;
final Uuid _uuid;

/// Initiates the email sign-in process.
///
Expand Down Expand Up @@ -212,7 +209,7 @@ class AuthService {
// All new users created via the public API get the standard role.
// Admin users must be provisioned out-of-band (e.g., via fixtures).
user = User(
id: _uuid.v4(),
id: ObjectId().oid,
email: email,
appRole: AppUserRole.standardUser,
dashboardRole: DashboardUserRole.none,
Expand Down Expand Up @@ -268,11 +265,12 @@ class AuthService {
// 1. Create anonymous user
User user;
try {
final newId = ObjectId().oid;
user = User(
id: _uuid.v4(),
id: newId,
// Use a unique placeholder email for anonymous users to satisfy the
// non-nullable email constraint.
email: '${_uuid.v4()}@anonymous.com',
email: '$newId@anonymous.com',
appRole: AppUserRole.guestUser,
dashboardRole: DashboardUserRole.none,
createdAt: DateTime.now(),
Expand Down
10 changes: 3 additions & 7 deletions lib/src/services/jwt_auth_token_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:ht_api/src/services/token_blacklist_service.dart';
import 'package:ht_data_repository/ht_data_repository.dart';
import 'package:ht_shared/ht_shared.dart';
import 'package:logging/logging.dart';
import 'package:uuid/uuid.dart';
import 'package:mongo_dart/mongo_dart.dart';

/// {@template jwt_auth_token_service}
/// An implementation of [AuthTokenService] using JSON Web Tokens (JWT).
Expand All @@ -19,20 +19,16 @@ class JwtAuthTokenService implements AuthTokenService {
/// - [userRepository]: To fetch user details after validating the token's
/// subject claim.
/// - [blacklistService]: To manage the blacklist of invalidated tokens.
/// - [uuidGenerator]: For creating unique JWT IDs (jti).
const JwtAuthTokenService({
required HtDataRepository<User> userRepository,
required TokenBlacklistService blacklistService,
required Uuid uuidGenerator,
required Logger log,
}) : _userRepository = userRepository,
_blacklistService = blacklistService,
_uuid = uuidGenerator,
_log = log;

final HtDataRepository<User> _userRepository;
final TokenBlacklistService _blacklistService;
final Uuid _uuid;
final Logger _log;

// --- Configuration ---
Expand Down Expand Up @@ -61,7 +57,7 @@ class JwtAuthTokenService implements AuthTokenService {
'exp': expiry.millisecondsSinceEpoch ~/ 1000, // Expiration Time
'iat': now.millisecondsSinceEpoch ~/ 1000, // Issued At
'iss': _issuer, // Issuer
'jti': _uuid.v4(), // JWT ID (for potential blacklisting)
'jti': ObjectId().oid, // JWT ID (for potential blacklisting)
// Custom claims (optional, include what's useful)
'email': user.email, // Kept for convenience
// Embed the new enum-based roles. Use .name for string value.
Expand All @@ -70,7 +66,7 @@ class JwtAuthTokenService implements AuthTokenService {
},
issuer: _issuer,
subject: user.id,
jwtId: _uuid.v4(), // Re-setting jti here for clarity if needed
jwtId: ObjectId().oid, // Re-setting jti here for clarity if needed
);

// Sign the token using HMAC-SHA256
Expand Down
1 change: 0 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ dependencies:
meta: ^1.16.0
mongo_dart: ^0.10.5
shelf_cors_headers: ^0.1.5
uuid: ^4.5.1

dev_dependencies:
mocktail: ^1.0.3
Expand Down
6 changes: 2 additions & 4 deletions routes/_middleware.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import 'package:ht_data_repository/ht_data_repository.dart';
import 'package:ht_email_repository/ht_email_repository.dart';
import 'package:ht_shared/ht_shared.dart';
import 'package:logging/logging.dart';
import 'package:uuid/uuid.dart';
import 'package:mongo_dart/mongo_dart.dart';

// --- Middleware Definition ---
final _log = Logger('RootMiddleware');
Expand Down Expand Up @@ -55,8 +55,7 @@ Handler middleware(Handler handler) {
_log.info(
'[REQ_LIFECYCLE] Request received. Generating RequestId...',
);
final uuid = context.read<Uuid>();
final requestId = RequestId(uuid.v4());
final requestId = RequestId(ObjectId().oid);
_log.info('[REQ_LIFECYCLE] RequestId generated: ${requestId.id}');
return innerHandler(context.provide<RequestId>(() => requestId));
};
Expand All @@ -76,7 +75,6 @@ Handler middleware(Handler handler) {
final deps = AppDependencies.instance;
return handler
.use(provider<ModelRegistryMap>((_) => modelRegistry))
.use(provider<Uuid>((_) => const Uuid()))
.use(
provider<HtDataRepository<Headline>>(
(_) => deps.headlineRepository,
Expand Down
3 changes: 3 additions & 0 deletions routes/api/v1/data/[id]/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ Future<Response> _handlePut(
throw const BadRequestException('Missing or invalid request body.');
}

// Standardize timestamp before model creation
requestBody['updatedAt'] = DateTime.now().toUtc().toIso8601String();

// Deserialize using ModelConfig's fromJson, catching TypeErrors locally
dynamic itemToUpdate;
try {
Expand Down
7 changes: 7 additions & 0 deletions routes/api/v1/data/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:ht_api/src/rbac/permission_service.dart';
import 'package:ht_api/src/registry/model_registry.dart';
import 'package:ht_data_repository/ht_data_repository.dart';
import 'package:ht_shared/ht_shared.dart';
import 'package:mongo_dart/mongo_dart.dart';

/// Handles requests for the /api/v1/data collection endpoint.
/// Dispatches requests to specific handlers based on the HTTP method.
Expand Down Expand Up @@ -152,6 +153,12 @@ Future<Response> _handlePost(RequestContext context) async {
throw const BadRequestException('Missing or invalid request body.');
}

// Standardize ID and timestamps before model creation
final now = DateTime.now().toUtc().toIso8601String();
requestBody['id'] = ObjectId().oid;
requestBody['createdAt'] = now;
requestBody['updatedAt'] = now;

dynamic itemToCreate;
try {
itemToCreate = modelConfig.fromJson(requestBody);
Expand Down
Loading