/// API endpoints for authentication-related functions, like registration and login. module auth.api; import handy_httpd; import handy_httpd.components.optional; import slf4d; import auth.model; import auth.data; import auth.service; import auth.data_impl_fs; import util.json; void postLogin(ref HttpRequestContext ctx) { LoginCredentials loginCredentials = readJsonPayload!LoginCredentials(ctx); if (!validateUsername(loginCredentials.username)) { ctx.response.status = HttpStatus.UNAUTHORIZED; return; } UserRepository userRepo = new FileSystemUserRepository(); Optional!User optionalUser = userRepo.findByUsername(loginCredentials.username); if (optionalUser.isNull) { ctx.response.status = HttpStatus.UNAUTHORIZED; return; } import botan.passhash.bcrypt : checkBcrypt; if (!checkBcrypt(loginCredentials.password, optionalUser.value.passwordHash)) { ctx.response.status = HttpStatus.UNAUTHORIZED; return; } string token = generateAccessToken(optionalUser.value); writeJsonBody(ctx, TokenResponse(token)); } void getUsernameAvailability(ref HttpRequestContext ctx) { Optional!string username = ctx.request.queryParams.getFirst("username"); if (username.isNull) { ctx.response.status = HttpStatus.BAD_REQUEST; ctx.response.writeBodyString("Missing username parameter."); return; } UserRepository userRepo = new FileSystemUserRepository(); bool available = userRepo.findByUsername(username.value).isNull; writeJsonBody(ctx, UsernameAvailabilityResponse(available)); } void postRegister(ref HttpRequestContext ctx) { RegistrationData registrationData = readJsonPayload!RegistrationData(ctx); if (!validateUsername(registrationData.username)) { ctx.response.status = HttpStatus.BAD_REQUEST; ctx.response.writeBodyString("Invalid username."); return; } if (!validatePassword(registrationData.password)) { ctx.response.status = HttpStatus.BAD_REQUEST; ctx.response.writeBodyString("Invalid password."); return; } UserRepository userRepo = new FileSystemUserRepository(); if (!userRepo.findByUsername(registrationData.username).isNull) { ctx.response.status = HttpStatus.BAD_REQUEST; ctx.response.writeBodyString("Username is taken."); return; } import botan.passhash.bcrypt : generateBcrypt; import botan.rng.auto_rng; RandomNumberGenerator rng = new AutoSeededRNG(); string passwordHash = generateBcrypt(registrationData.password, rng, 12); User user = userRepo.createUser(registrationData.username, passwordHash); infoF!"Created user: %s"(registrationData.username); string token = generateAccessToken(user); writeJsonBody(ctx, TokenResponse(token)); } void getMyUser(ref HttpRequestContext ctx) { AuthContext auth = getAuthContext(ctx); ctx.response.writeBodyString(auth.user.username); }