return the user as well as the session when authenticating
All checks were successful
Deploy API / deploy (ubuntu-latest, 2.44.0) (push) Successful in 41s

This commit is contained in:
Braydon 2024-09-18 18:23:18 -04:00
parent 29f0d39a78
commit a929a4ee48
6 changed files with 49 additions and 21 deletions

@ -2,7 +2,7 @@ name: Deploy API
on: on:
push: push:
branches: ["master"] branches: [ "master" ]
paths-ignore: paths-ignore:
- README.md - README.md
- LICENSE - LICENSE
@ -11,8 +11,8 @@ jobs:
deploy: deploy:
strategy: strategy:
matrix: matrix:
arch: ["ubuntu-latest"] arch: [ "ubuntu-latest" ]
git-version: ["2.44.0"] git-version: [ "2.44.0" ]
runs-on: ${{ matrix.arch }} runs-on: ${{ matrix.arch }}
# Steps to run # Steps to run

@ -18,7 +18,7 @@ public final class StringUtils {
private static final SecureRandom RANDOM = new SecureRandom(); private static final SecureRandom RANDOM = new SecureRandom();
private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Za-z0-9+_.-]+@(.+)$"); private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Za-z0-9+_.-]+@(.+)$");
private static final Pattern USERNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_.]*$"); private static final Pattern USERNAME_PATTERN = Pattern.compile("^[a-z0-9_.]*$");
/** /**
* Check if the given email is valid. * Check if the given email is valid.

@ -1,9 +1,9 @@
package cc.pulseapp.api.controller.v1; package cc.pulseapp.api.controller.v1;
import cc.pulseapp.api.exception.impl.BadRequestException; import cc.pulseapp.api.exception.impl.BadRequestException;
import cc.pulseapp.api.model.user.Session;
import cc.pulseapp.api.model.user.input.UserLoginInput; import cc.pulseapp.api.model.user.input.UserLoginInput;
import cc.pulseapp.api.model.user.input.UserRegistrationInput; import cc.pulseapp.api.model.user.input.UserRegistrationInput;
import cc.pulseapp.api.model.user.response.UserAuthResponse;
import cc.pulseapp.api.service.AuthService; import cc.pulseapp.api.service.AuthService;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.NonNull; import lombok.NonNull;
@ -39,11 +39,11 @@ public final class AuthController {
* *
* @param request the http request * @param request the http request
* @param input the registration input * @param input the registration input
* @return the session for the registered user * @return the user auth response
* @throws BadRequestException if the registration fails * @throws BadRequestException if the registration fails
*/ */
@PostMapping("/register") @ResponseBody @NonNull @PostMapping("/register") @ResponseBody @NonNull
public ResponseEntity<Session> register(@NonNull HttpServletRequest request, UserRegistrationInput input) throws BadRequestException { public ResponseEntity<UserAuthResponse> register(@NonNull HttpServletRequest request, UserRegistrationInput input) throws BadRequestException {
return ResponseEntity.ok(authService.registerUser(request, input)); return ResponseEntity.ok(authService.registerUser(request, input));
} }
@ -52,11 +52,11 @@ public final class AuthController {
* *
* @param request the http request * @param request the http request
* @param input the login input * @param input the login input
* @return the session for the login user * @return the user auth response
* @throws BadRequestException if the login fails * @throws BadRequestException if the login fails
*/ */
@PostMapping("/login") @ResponseBody @NonNull @PostMapping("/login") @ResponseBody @NonNull
public ResponseEntity<Session> login(@NonNull HttpServletRequest request, UserLoginInput input) throws BadRequestException { public ResponseEntity<UserAuthResponse> login(@NonNull HttpServletRequest request, UserLoginInput input) throws BadRequestException {
return ResponseEntity.ok(authService.loginUser(request, input)); return ResponseEntity.ok(authService.loginUser(request, input));
} }
} }

@ -0,0 +1,26 @@
package cc.pulseapp.api.model.user.response;
import cc.pulseapp.api.model.user.Session;
import cc.pulseapp.api.model.user.UserDTO;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
/**
* The response for successfully logging in.
*
* @author Braydon
*/
@AllArgsConstructor @Getter @ToString
public final class UserAuthResponse {
/**
* The created session for the user.
*/
@NonNull private final Session session;
/**
* The user logging in.
*/
@NonNull private final UserDTO user;
}

@ -7,12 +7,10 @@ import cc.pulseapp.api.exception.impl.BadRequestException;
import cc.pulseapp.api.exception.impl.ResourceNotFoundException; import cc.pulseapp.api.exception.impl.ResourceNotFoundException;
import cc.pulseapp.api.model.Feature; import cc.pulseapp.api.model.Feature;
import cc.pulseapp.api.model.IGenericResponse; import cc.pulseapp.api.model.IGenericResponse;
import cc.pulseapp.api.model.user.Session; import cc.pulseapp.api.model.user.*;
import cc.pulseapp.api.model.user.User;
import cc.pulseapp.api.model.user.UserFlag;
import cc.pulseapp.api.model.user.UserTier;
import cc.pulseapp.api.model.user.input.UserLoginInput; import cc.pulseapp.api.model.user.input.UserLoginInput;
import cc.pulseapp.api.model.user.input.UserRegistrationInput; import cc.pulseapp.api.model.user.input.UserRegistrationInput;
import cc.pulseapp.api.model.user.response.UserAuthResponse;
import cc.pulseapp.api.repository.SessionRepository; import cc.pulseapp.api.repository.SessionRepository;
import cc.pulseapp.api.repository.UserRepository; import cc.pulseapp.api.repository.UserRepository;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@ -64,11 +62,11 @@ public final class AuthService {
* *
* @param request the http request * @param request the http request
* @param input the registration input * @param input the registration input
* @return the registered user's auth token * @return the user auth response
* @throws BadRequestException if the input has an error * @throws BadRequestException if the input has an error
*/ */
@NonNull @NonNull
public Session registerUser(@NonNull HttpServletRequest request, UserRegistrationInput input) throws BadRequestException { public UserAuthResponse registerUser(@NonNull HttpServletRequest request, UserRegistrationInput input) throws BadRequestException {
// Ensure user registration is enabled // Ensure user registration is enabled
if (!Feature.USER_REGISTRATION_ENABLED.isEnabled()) { if (!Feature.USER_REGISTRATION_ENABLED.isEnabled()) {
throw new BadRequestException(Error.REGISTRATION_DISABLED); throw new BadRequestException(Error.REGISTRATION_DISABLED);
@ -83,11 +81,12 @@ public final class AuthService {
// Create the user and return it // Create the user and return it
byte[] salt = HashUtils.generateSalt(); byte[] salt = HashUtils.generateSalt();
Date now = new Date(); Date now = new Date();
return generateSession(request, userRepository.save(new User( User user = userRepository.save(new User(
snowflakeService.generateSnowflake(), input.getEmail(), input.getUsername().toLowerCase(), snowflakeService.generateSnowflake(), input.getEmail(), input.getUsername().toLowerCase(),
HashUtils.hash(salt, input.getPassword()), Base64.getEncoder().encodeToString(salt), HashUtils.hash(salt, input.getPassword()), Base64.getEncoder().encodeToString(salt),
null, UserTier.FREE, 0, now null, UserTier.FREE, 0, now
))); ));
return new UserAuthResponse(generateSession(request, user), UserDTO.asDTO(user, now));
} }
/** /**
@ -95,11 +94,11 @@ public final class AuthService {
* *
* @param request the http request * @param request the http request
* @param input the login input * @param input the login input
* @return the logged in user's auth token * @return the user auth response
* @throws BadRequestException if the input has an error * @throws BadRequestException if the input has an error
*/ */
@NonNull @NonNull
public Session loginUser(@NonNull HttpServletRequest request, UserLoginInput input) throws BadRequestException { public UserAuthResponse loginUser(@NonNull HttpServletRequest request, UserLoginInput input) throws BadRequestException {
validateLoginInput(input); // Ensure the input is valid validateLoginInput(input); // Ensure the input is valid
// Lookup the user by the email or username and ensure the user exists // Lookup the user by the email or username and ensure the user exists
@ -112,7 +111,9 @@ public final class AuthService {
throw new BadRequestException(Error.PASSWORDS_DO_NOT_MATCH); throw new BadRequestException(Error.PASSWORDS_DO_NOT_MATCH);
} }
user.setLastLogin(new Date()); user.setLastLogin(new Date());
return generateSession(request, userRepository.save(user)); user = userRepository.save(user);
return new UserAuthResponse(generateSession(request, user),
UserDTO.asDTO(user, new Date(snowflakeService.extractCreationTime(user.getSnowflake()))));
} }
/** /**

@ -1,5 +1,6 @@
package cc.pulseapp.api.service; package cc.pulseapp.api.service;
import cc.pulseapp.api.common.EnvironmentUtils;
import cc.pulseapp.api.exception.impl.BadRequestException; import cc.pulseapp.api.exception.impl.BadRequestException;
import cc.pulseapp.api.model.IGenericResponse; import cc.pulseapp.api.model.IGenericResponse;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@ -33,7 +34,7 @@ public final class CaptchaService {
.header(HttpHeaders.CONTENT_TYPE, "application/json") .header(HttpHeaders.CONTENT_TYPE, "application/json")
.body(body) .body(body)
.asJson(); .asJson();
if (!response.getBody().getObject().getBoolean("success")) { if (EnvironmentUtils.isProduction() && !response.getBody().getObject().getBoolean("success")) {
throw new BadRequestException(Error.CAPTCHA_INVALID); throw new BadRequestException(Error.CAPTCHA_INVALID);
} }
} }