/// API endpoints for authentication-related functions, like registration and login. module auth.api; import handy_httpd; import handy_httpd.components.optional; import slf4d; import asdf; import auth.model; import auth.data; import auth.service; import auth.data_impl_fs; void postLogin(ref HttpRequestContext ctx) { LoginCredentials loginCredentials; try { loginCredentials = deserialize!(LoginCredentials)(ctx.request.readBodyAsString()); } catch (Exception e) { ctx.response.status = HttpStatus.BAD_REQUEST; } 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); ctx.response.status = HttpStatus.OK; TokenResponse resp = TokenResponse(token); ctx.response.writeBodyString(serializeToJson(resp), "application/json"); } void postRegister(ref HttpRequestContext ctx) { RegistrationData registrationData; try { registrationData = deserialize!(RegistrationData)(ctx.request.readBodyAsString()); } catch (Exception e) { ctx.response.status = HttpStatus.BAD_REQUEST; return; } 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); userRepo.createUser(registrationData.username, passwordHash); infoF!"Created user: %s"(registrationData.username); } void getMyUser(ref HttpRequestContext ctx) { AuthContext auth = getAuthContext(ctx); ctx.response.writeBodyString(auth.user.username); }