Skip to content

Commit

Permalink
Implement fetching wedding basic details and user logout
Browse files Browse the repository at this point in the history
  • Loading branch information
shubhamsinghshubham777 committed Sep 29, 2024
1 parent 5329b37 commit c2ee2b9
Show file tree
Hide file tree
Showing 56 changed files with 1,056 additions and 411 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@
*.g.*
*.freezed.*
*.mocks.*

.idea
4 changes: 2 additions & 2 deletions backend/routes/_middleware.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import 'package:mongo_dart/mongo_dart.dart';
import '../src/models/token_config.dart';
import '../src/repositories/credential_manager.dart';
import '../src/repositories/user_repository.dart';
import '../src/utils/extensions.dart';

Handler middleware(Handler handler) {
return handler
.use(requestLogger())
// Uses => `db`, Provides => `Future<UserRepository>`
.use(Middlewares.userRepository)
.use(Middlewares.db)
.use(Middlewares.credentialManager);
Expand All @@ -21,7 +21,7 @@ class Middlewares {

static final userRepository = provider<Future<UserRepository>>(
(context) async => MongoUserRepository(
mongoDb: await context.read<Future<Db>>(),
mongoDb: await context.injectMongoDB(),
),
);

Expand Down
87 changes: 45 additions & 42 deletions backend/routes/auth/login.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,63 @@ import 'dart:io';
import 'package:core/models/auth/login_request.dart';
import 'package:dart_frog/dart_frog.dart';

import '../../src/repositories/credential_manager.dart';
import '../../src/repositories/user_repository.dart';
import '../../src/utils/extensions.dart';
import '../../src/utils/global_instances.dart';

Future<Response> onRequest(RequestContext context) async {
if (!context.isPostRequest) {
return Response(
statusCode: HttpStatus.badRequest,
body: 'This endpoint only supports POST requests!',
);
}
return await context.onRequestMethod(
post: () async {
final loginRequest = LoginRequest.fromJson(
await context.request.bodyAsMap,
);

final loginRequest = LoginRequest.fromJson(
await context.request.bodyAsMap,
);
final userRepository = await context.injectUserRepository();

final userRepository = await context.read<Future<UserRepository>>();
// Check if a user with the provided e-mail exists
final user = await userRepository.findUser(loginRequest.email);

// Check if a user with the provided e-mail exists
final user = await userRepository.findUser(loginRequest.email);
// If user doesn't exist, return an error response
if (user == null) {
logger.e('User for e-mail (${loginRequest.email}) does not exist!');
return Response(
statusCode: HttpStatus.expectationFailed,
body: _loginErrorMessage,
);
}

// If user doesn't exist, return an error response
if (user == null) {
logger.e('User for e-mail (${loginRequest.email}) does not exist!');
return Response(
statusCode: HttpStatus.expectationFailed,
body: _loginErrorMessage,
);
}
// If user exists, generate its hashed password and compare with the one
// in DB
final credentialManager = context.injectCredentialManager();
final hashedPassword = credentialManager.getHashedPassword(
password: loginRequest.password,
salt: user.passwordSalt!,
);

// If user exists, generate its hashed password and compare with the one in DB
final credentialManager = context.read<CredentialManager>();
final hashedPassword = credentialManager.getHashedPassword(
password: loginRequest.password,
salt: user.passwordSalt!,
);
// If hashed passwords do not match, return an error response
if (hashedPassword != user.hashedPassword) {
logger.e('Password for user (${loginRequest.email}) is invalid!');
return Response(
statusCode: HttpStatus.expectationFailed,
body: _loginErrorMessage,
);
}

// If hashed passwords do not match, return an error response
if (hashedPassword != user.hashedPassword) {
logger.e('Password for user (${loginRequest.email}) is invalid!');
return Response(
statusCode: HttpStatus.expectationFailed,
body: _loginErrorMessage,
);
}
// If hashed passwords match, generate & return user access & refresh
// tokens
final tokens = credentialManager.generateJWT(
email: loginRequest.email,
generateRefreshToken: true,
);

// If hashed passwords match, generate & return user access & refresh tokens
final tokens = credentialManager.generateJWT(
email: loginRequest.email,
generateRefreshToken: true,
return Response.json(body: tokens.toJson());
},
orElse: () {
return Response(
statusCode: HttpStatus.badRequest,
body: 'This endpoint only supports POST requests!',
);
},
);

return Response.json(body: tokens.toJson());
}

// The error message is deliberately a vague one to ensure that a bad
Expand Down
93 changes: 48 additions & 45 deletions backend/routes/auth/register.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,56 @@ import '../../src/repositories/user_repository.dart';
import '../../src/utils/extensions.dart';

Future<Response> onRequest(RequestContext context) async {
if (!context.isPostRequest) {
return Response(
statusCode: HttpStatus.badRequest,
body: 'This endpoint only supports POST requests!',
);
}

final registerRequest = RegisterRequest.fromJson(
await context.request.bodyAsMap,
);
return await context.onRequestMethod(
post: () async {
final registerRequest = RegisterRequest.fromJson(
await context.request.bodyAsMap,
);

final userRepo = await context.read<Future<UserRepository>>();

// Check if the user already exists
final userOrNull = await userRepo.findUser(registerRequest.email);

// If the user exists, throw an error
if (userOrNull != null) {
return Response(
statusCode: HttpStatus.expectationFailed,
body: 'A user with the provided credentials already exists! Please '
'login instead.',
);
}

// If the user doesn't exist, create it, and return a success message
final credentialManager = context.read<CredentialManager>();
final passwordSalt = credentialManager.generateRandomSalt();
final hashedPassword = credentialManager.getHashedPassword(
password: registerRequest.password,
salt: passwordSalt,
);
final userRepo = await context.read<Future<UserRepository>>();

final isInserted = await userRepo.insertUser(
DTAUser(
firstName: registerRequest.firstName,
email: registerRequest.email,
hashedPassword: hashedPassword,
gender: registerRequest.gender,
passwordSalt: passwordSalt,
),
);
// Check if the user already exists
final userOrNull = await userRepo.findUser(registerRequest.email);

// If the user exists, throw an error
if (userOrNull != null) {
return Response(
statusCode: HttpStatus.expectationFailed,
body: 'A user with the provided credentials already exists! Please '
'login instead.',
);
}

// If the user doesn't exist, create it, and return a success message
final credentialManager = context.read<CredentialManager>();
final passwordSalt = credentialManager.generateRandomSalt();
final hashedPassword = credentialManager.getHashedPassword(
password: registerRequest.password,
salt: passwordSalt,
);

final isInserted = await userRepo.upsertUser(
DTAUser(
firstName: registerRequest.firstName,
email: registerRequest.email,
gender: registerRequest.gender,
hashedPassword: hashedPassword,
passwordSalt: passwordSalt,
),
);

return Response(
statusCode: isInserted ? HttpStatus.ok : HttpStatus.expectationFailed,
body: isInserted
? 'User registered successfully!'
: 'User was NOT registered!',
return Response(
statusCode: isInserted ? HttpStatus.ok : HttpStatus.expectationFailed,
body: isInserted
? 'User registered successfully!'
: 'User was NOT registered!',
);
},
orElse: () {
return Response(
statusCode: HttpStatus.badRequest,
body: 'This endpoint only supports POST requests!',
);
},
);
}
71 changes: 40 additions & 31 deletions backend/routes/user/profile.dart
Original file line number Diff line number Diff line change
@@ -1,40 +1,49 @@
import 'dart:io';

import 'package:core/models/auth/dta_user.dart';
import 'package:dart_frog/dart_frog.dart';

import '../../src/utils/custom_errors.dart';
import '../../src/utils/extensions.dart';
import '../../src/utils/utils.dart';

Future<Response> onRequest(RequestContext context) async {
if (!context.isGetRequest) {
return Response(
statusCode: HttpStatus.badRequest,
body: 'This endpoint only supports GET requests!',
);
}

try {
final user = await context.userFromJWT;
if (user == null) throw JWTException('Invalid token!');

return Response.json(
body: user.copyWith(
hashedPassword: null,
passwordSalt: null,
),
);
} catch (e) {
if (e is JWTException) {
Future<Response> onRequest(RequestContext context) {
return protected(
context,
body: (userFromJWT) async {
return await context.onRequestMethod(
get: () async {
return Response.json(
body: userFromJWT?.copyWith(
hashedPassword: null,
passwordSalt: null,
),
);
},
patch: () async {
final userRepo = await context.injectUserRepository();
final rawUser = await context.request.bodyAsMap;
final user = DTAUser.fromJson(rawUser);
final success = await userRepo.upsertUser(user);
return Response(
statusCode: success ? HttpStatus.ok : HttpStatus.expectationFailed,
body: success
? null
: 'Could not update user! Please check your input(s) or try '
'again later.',
);
},
orElse: () => Response(
statusCode: HttpStatus.badRequest,
body: 'This endpoint only supports GET and PATCH requests!',
),
);
},
onError: (e, st) {
return Response(
statusCode: HttpStatus.unauthorized,
body: 'Your access token is either invalid or expired! Please make '
'sure to use a valid token.',
statusCode: HttpStatus.internalServerError,
body: 'The server is facing some issues in addressing your request at '
'the moment. Please try again later.',
);
}
return Response(
statusCode: HttpStatus.internalServerError,
body: 'The server is facing some issues in addressing your request at '
'the moment. Please try again later.',
);
}
},
);
}
16 changes: 9 additions & 7 deletions backend/src/repositories/credential_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class CredentialManagerImpl implements CredentialManager {
JWTPayload? verifyJWT(String token) {
final parts = token.split('.');
if (parts.length != 3) {
throw JWTFormatException('Invalid token format');
throw const JWTFormatException(cause: 'Invalid token format');
}

try {
Expand All @@ -103,7 +103,9 @@ class CredentialManagerImpl implements CredentialManager {

// Verify algorithm
if (header['alg'] != 'HS256') {
throw JWTFormatException('Unsupported algorithm: ${header['alg']}');
throw JWTFormatException(
cause: 'Unsupported algorithm: ${header['alg']}',
);
}

// Verify signature
Expand All @@ -113,25 +115,25 @@ class CredentialManagerImpl implements CredentialManager {
final computedSignature = _base64UrlEncode(digest.bytes);

if (computedSignature != parts[2]) {
throw JWTInvalidSignatureException('Invalid signature');
throw const JWTInvalidSignatureException(cause: 'Invalid signature');
}

// Verify expiration
final expiration = DateTime.fromMillisecondsSinceEpoch(
(payload['exp'] as int) * 1000,
);
if (DateTime.now().isAfter(expiration)) {
throw JWTExpiredException('Token has expired');
throw const JWTExpiredException(cause: 'Token has expired');
}

// Verify audience
if (payload['aud'] != config.jwtAudience) {
throw JWTInvalidClaimException('Invalid audience');
throw const JWTInvalidClaimException(cause: 'Invalid audience');
}

// Verify issuer
if (payload['iss'] != config.jwtIssuer) {
throw JWTInvalidClaimException('Invalid issuer');
throw const JWTInvalidClaimException(cause: 'Invalid issuer');
}

return JWTPayload(
Expand All @@ -142,7 +144,7 @@ class CredentialManagerImpl implements CredentialManager {
);
} catch (e) {
if (e is JWTException) rethrow;
throw JWTFormatException('Failed to parse token: $e');
throw JWTFormatException(cause: 'Failed to parse token: $e');
}
}

Expand Down
Loading

0 comments on commit c2ee2b9

Please sign in to comment.