Skip to content

Commit

Permalink
Merge pull request #17 from IvanoskiHarmonia/qus-2-user-authenticatio…
Browse files Browse the repository at this point in the history
…n-via-google

Google oauth
  • Loading branch information
IvanoskiHarmonia authored Dec 26, 2023
2 parents 202fb5e + 53d0c56 commit 72bcaed
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 24 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ out/

### VS Code ###
.vscode/

# oauth keys
src/main/resources/oauth2.txt
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
implementation 'org.slf4j:slf4j-api:2.0.9'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
Expand Down
11 changes: 0 additions & 11 deletions src/main/java/com/quizapp/service/QuizServiceApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class QuizServiceApplication {
Expand All @@ -12,12 +10,3 @@ public static void main(String[] args) {
SpringApplication.run(QuizServiceApplication.class, args);
}
}

@RestController
class TestQuizController {

@GetMapping("/")
public String getQuiz() {
return "Hello World";
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/quizapp/service/QuizServiceConstants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.quizapp.service;

public final class QuizServiceConstants {

private QuizServiceConstants() {
throw new IllegalStateException("Utility class");
}

public static final String SECRET_KEY = System.getenv("SECRET_KEY_QUIZ_SERVICE");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.quizapp.service.data.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class AppConfig {

@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://localhost:3000");
config.addAllowedHeader("*");
config.addAllowedMethod("*");

source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@CrossOrigin
@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true")
@RestController
@RequestMapping("/api/questions")
public class QuestionController {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@CrossOrigin
@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true")
@RestController
@RequestMapping("api/quizzes")
public class QuizController {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.quizapp.service.data.controller;

import com.quizapp.service.QuizServiceConstants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true")
@RestController
@RequestMapping("/api/session")
public class SessionController {

private static final Logger logger = LoggerFactory.getLogger(SessionController.class);

@GetMapping("/validate")
public ResponseEntity<String> validateSession(HttpServletRequest request) {
boolean isValidSession = false;
String token = null;

Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("authToken".equals(cookie.getName())) {
token = cookie.getValue();
break;
}
}
}

if (token != null && !token.isEmpty()) {
try {
Claims claims =
Jwts.parser()
.setSigningKey(QuizServiceConstants.SECRET_KEY)
.parseClaimsJws(token)
.getBody();

if (claims.getExpiration().after(new Date())) {
isValidSession = true;
}
} catch (Exception e) {
logger.error("Error validating session: {}", e.getMessage());
}
}

return ResponseEntity.ok().body("{\"isValidSession\":" + isValidSession + "}");
}

@PostMapping("/logout")
public ResponseEntity<String> logout(HttpServletResponse response) {
Cookie cookie = new Cookie("authToken", null);
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setMaxAge(0);
response.addCookie(cookie);

return ResponseEntity.ok().body("{\"message\":\"Successfully logged out.\"}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.quizapp.service.data.controller;

import com.quizapp.service.data.dto.UserDataDTO;
import com.quizapp.service.data.entity.User;
import com.quizapp.service.data.service.UserService;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.transaction.Transactional;
import jakarta.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true")
@RestController
@RequestMapping("/api/users")
public class UserController {

private final UserService userService;

@Autowired
public UserController(UserService userService) {
this.userService = userService;
}

@Transactional
@PostMapping("/login")
public ResponseEntity<User> createUser(
@NotNull @RequestBody UserDataDTO userData, HttpServletResponse response) {

User user =
userService.createOrUpdateUser(
userData.getEmail(), userData.getToken(), userData.getExpiresAt());

Cookie cookie = new Cookie("token", userData.getToken());
cookie.setHttpOnly(true);
cookie.setMaxAge(60 * 60 * 24 * 365); // 1 year
cookie.setSecure(true);
cookie.setPath("/");
response.addCookie(cookie);

return ResponseEntity.ok(user);
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/quizapp/service/data/dto/UserDataDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.quizapp.service.data.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDataDTO {

private String token;
private Long expiresAt;
private String email;
}
17 changes: 6 additions & 11 deletions src/main/java/com/quizapp/service/data/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,33 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import jakarta.persistence.Table;
import java.util.Set;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Entity
@Table(name = "\"User\"")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long id;

private String username;
private String password;
@Column(unique = true, nullable = false)
private String email;

@PrePersist
@PreUpdate
private void hashPassword() {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
this.password = passwordEncoder.encode(this.password);
}
@Column(unique = true)
private String token;

private Long expiresAt;

@ToString.Exclude
@EqualsAndHashCode.Exclude
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.quizapp.service.data.repository;

import com.quizapp.service.data.entity.User;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {

Optional<User> findByEmail(String email);
}
33 changes: 33 additions & 0 deletions src/main/java/com/quizapp/service/data/service/UserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.quizapp.service.data.service;

import com.quizapp.service.data.entity.User;
import com.quizapp.service.data.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
private final UserRepository userRepository;

@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

public User createOrUpdateUser(String email, String token, Long expiresAt) {
User existingUser = userRepository.findByEmail(email).orElse(null);

if (existingUser != null) {
existingUser.setToken(token);
existingUser.setExpiresAt(expiresAt);
return userRepository.save(existingUser);
} else {
User user = new User();
user.setEmail(email);
user.setToken(token);
user.setExpiresAt(expiresAt);

return userRepository.save(user);
}
}
}

0 comments on commit 72bcaed

Please sign in to comment.