Skip to content

Commit

Permalink
feat: setup 2fa
Browse files Browse the repository at this point in the history
  • Loading branch information
OskarWiedeweg committed Dec 15, 2023
1 parent 73a0c31 commit da4138f
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public User validate(String token, Long twoFACode) {
return user;
}

if (codeVerifier.isValidCode(user.getTwoFASecret(), twoFACode.toString())) {
if (!codeVerifier.isValidCode(user.getTwoFASecret(), twoFACode.toString())) {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "2FA Code is invalid!");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.oskarwiedeweg.cloudwork.user;

import com.oskarwiedeweg.cloudwork.user.dto.Setup2FADto;
import lombok.Data;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@Data
@RestController
@RequestMapping("/v1/user")
public class UserController {

private final UserService userService;

@GetMapping("/2fa/setup")
@PreAuthorize("isAuthenticated()")
public Setup2FADto setup2FA(@AuthenticationPrincipal Long userId) {
return userService.setup2FA(userId);
}

@PostMapping("/2fa/verify/{code}")
@PreAuthorize("isAuthenticated()")
public Map<String, Boolean> verify2FA(@AuthenticationPrincipal Long userId, @PathVariable("code") Long code, @RequestParam("tempToken") String tempToken) {
return Map.of("valid", userService.setup2FA(userId, code, tempToken));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ public Optional<User> findUserByName(String username) {
}
}

public Optional<User> findUserById(Long userId) {
try {
User user = jdbcTemplate.queryForObject("select * from users where id = ?", rowMapper, userId);
return Optional.ofNullable(user);
} catch (IncorrectResultSizeDataAccessException ok) {
return Optional.empty();
}
}

public void updateUserSettingsWith2FASecret(Long userId, Long settings, String user2FASecret) {
jdbcTemplate.update("update users set settings = ?, \"2fa_key\" = ? where id = ?", settings, user2FASecret, userId);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.oskarwiedeweg.cloudwork.user;

import com.oskarwiedeweg.cloudwork.auth.twofa.TwoFAService;
import com.oskarwiedeweg.cloudwork.exception.DuplicateUserException;
import com.oskarwiedeweg.cloudwork.user.dto.Setup2FADto;
import lombok.Data;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import java.util.Optional;

Expand All @@ -13,6 +17,7 @@ public class UserService {

private final UserDao userDao;
private final PasswordEncoder passwordEncoder;
private final TwoFAService twoFAService;

public Long createUser(String name, String email, String password) throws DuplicateUserException {
String encoded = passwordEncoder.encode(password);
Expand All @@ -23,4 +28,21 @@ public Optional<User> getUserByName(String username) {
return userDao.findUserByName(username);
}

public Setup2FADto setup2FA(Long userId) {
User user = userDao.findUserById(userId).orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "User does not exist"));

String setupQrCode = twoFAService.setup2FA(user);
String tempToken = twoFAService.create2FAChallenge(user);

return new Setup2FADto(setupQrCode, tempToken);
}

public boolean setup2FA(Long userId, Long code, String tempToken) {
try {
User validate = twoFAService.validate(tempToken, code);
return userId.equals(validate.getId());
} catch (ResponseStatusException e) {
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.oskarwiedeweg.cloudwork.user.dto;

import lombok.Data;

@Data
public class Setup2FADto {

private final String qrCode;
private final String tempToken;

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.oskarwiedeweg.cloudwork.user;

import com.oskarwiedeweg.cloudwork.auth.twofa.TwoFAService;
import lombok.SneakyThrows;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -15,14 +16,16 @@ class UserServiceTest {

private UserDao userDao;
private PasswordEncoder passwordEncoder;
private TwoFAService twoFAService;

private UserService underTest;

@BeforeEach
public void setup() {
userDao = mock(UserDao.class);
passwordEncoder = mock(PasswordEncoder.class);
underTest = new UserService(userDao, passwordEncoder);
twoFAService = mock(TwoFAService.class);
underTest = new UserService(userDao, passwordEncoder, twoFAService);
}

@SneakyThrows
Expand Down

0 comments on commit da4138f

Please sign in to comment.