diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..5138771
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ "associatedIndex": 5
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1744519930017
+
+
+ 1744519930017
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backend/auth-service/.gitignore b/backend/auth-service/.gitignore
index b63da45..265d677 100644
--- a/backend/auth-service/.gitignore
+++ b/backend/auth-service/.gitignore
@@ -39,4 +39,6 @@ bin/
.vscode/
### Mac OS ###
-.DS_Store
\ No newline at end of file
+.DS_Store
+
+/src/main/resources/
\ No newline at end of file
diff --git a/backend/auth-service/build.gradle b/backend/auth-service/build.gradle
index fdf3622..eb6fc12 100644
--- a/backend/auth-service/build.gradle
+++ b/backend/auth-service/build.gradle
@@ -28,7 +28,11 @@ dependencies {
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
-
+// implementation 'org.bouncycastle:bcprov-jdk16:1.45'
+ implementation 'org.bouncycastle:bcpkix-jdk15on:1.70'
+ implementation 'org.postgresql:postgresql:42.7.3'
+ implementation 'redis.clients:jedis:5.1.0'
+ implementation 'de.mkammerer:argon2-jvm:2.11'
}
tasks.named('test') {
diff --git a/backend/auth-service/pom.xml b/backend/auth-service/pom.xml
index 85be148..639f7a3 100644
--- a/backend/auth-service/pom.xml
+++ b/backend/auth-service/pom.xml
@@ -17,4 +17,5 @@
0.11.5
runtime
+
diff --git a/backend/auth-service/src/main/java/com/city/demo/AuthController.java b/backend/auth-service/src/main/java/com/city/demo/AuthController.java
deleted file mode 100644
index 9d99a03..0000000
--- a/backend/auth-service/src/main/java/com/city/demo/AuthController.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.city.demo;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-
-@RestController
-@RequestMapping("/api/auth")
-public class AuthController {
-
- private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
-
- @Autowired
- private AuthService authService;
-
- @PostMapping("/login")
- public ResponseEntity login(@RequestBody LoginRequest request) {
- logger.info("Login request: {}", request);
- return authService.login(request.getLogin(), request.getPassword());
- }
-
- @PostMapping("/refresh")
- public TokenResponse refresh(@RequestBody TokenResponse request) {
- return authService.refresh(request.getRefreshToken());
- }
-}
diff --git a/backend/auth-service/src/main/java/com/city/demo/AuthService.java b/backend/auth-service/src/main/java/com/city/demo/AuthService.java
deleted file mode 100644
index edd11d8..0000000
--- a/backend/auth-service/src/main/java/com/city/demo/AuthService.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.city.demo;
-
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Service;
-
-@Service
-public class AuthService {
-
- public ResponseEntity login(String login, String password) {
- // Временная реализация
- String accessToken = "generatedAccessToken";
- String refreshToken = "generatedRefreshToken";
- return ResponseEntity.ok(new TokenResponse(accessToken, refreshToken));
- }
-
- public TokenResponse refresh(String refreshToken) {
- // Временная реализация
- String newAccessToken = "newGeneratedAccessToken";
- String newRefreshToken = "newGeneratedRefreshToken";
- return new TokenResponse(newAccessToken, newRefreshToken);
- }
-}
diff --git a/backend/auth-service/src/main/java/com/city/demo/JwtTest.java b/backend/auth-service/src/main/java/com/city/demo/JwtTest.java
deleted file mode 100644
index 8ed07e9..0000000
--- a/backend/auth-service/src/main/java/com/city/demo/JwtTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.city.demo;
-
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.security.Keys;
-
-import javax.crypto.SecretKey;
-import java.util.HashMap;
-import java.util.Map;
-
-public class JwtTest {
- public static void main(String[] args) {
- // 1. Generate a secret key
- String secret = "my-very-secret-key-that-is-32-bytes-long!";
- SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
-
- // 2. Create an instance of JwtUtil
- JwtUtil jwtUtil = new JwtUtil(secret, "example-app");
-
- // 3. Generate a token
- Map claims = new HashMap<>();
- claims.put("username", "testuser");
- claims.put("role", "admin");
- String token = jwtUtil.generateToken(claims);
- System.out.println("Generated token: " + token);
-
- // 4. Decode the token
- JwtDecoder decoder = new JwtDecoder(key);
- System.out.println("\nDecode result:");
- Claims decodedClaims = decoder.decode(token);
- if (decodedClaims != null) {
- System.out.println("Subject: " + decodedClaims.getSubject());
- System.out.println("Username: " + decodedClaims.get("username"));
- System.out.println("Role: " + decodedClaims.get("role"));
- }
- }
-}
diff --git a/backend/auth-service/src/main/java/com/city/demo/controller/AuthController.java b/backend/auth-service/src/main/java/com/city/demo/controller/AuthController.java
new file mode 100644
index 0000000..4f6be0e
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/controller/AuthController.java
@@ -0,0 +1,53 @@
+package com.city.demo.controller;
+
+import com.city.demo.service.dto.RefreshRequest;
+import com.city.demo.service.impl.AuthServiceImpl;
+import com.city.demo.service.dto.LoginRequest;
+import com.city.demo.service.dto.TokenResponse;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/auth")
+public class AuthController {
+
+ private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
+
+ @Autowired
+ private AuthServiceImpl authService;
+
+ @PostMapping("/login")
+ public ResponseEntity login(@RequestBody LoginRequest request, HttpServletResponse response) {
+ logger.info("Login request: {}", request);
+
+ TokenResponse res = authService.login(request.getLogin(), request.getPassword());
+
+ Cookie cookie = new Cookie("refreshToken", res.getRefreshToken());
+ cookie.setPath("/");
+ // TODO: Change to the refresh token ttl value
+ cookie.setMaxAge(420);
+ cookie.setHttpOnly(true);
+ response.addCookie(cookie);
+
+ return ResponseEntity.ok(res);
+ }
+
+ @PostMapping("/refresh")
+ public ResponseEntity refresh(@RequestBody RefreshRequest request, HttpServletResponse response) {
+ TokenResponse res = authService.refresh(request.getRefreshToken());
+
+ Cookie cookie = new Cookie("refreshToken", res.getRefreshToken());
+ cookie.setPath("/");
+ // TODO: Change to the refresh token ttl value
+ cookie.setMaxAge(420);
+ cookie.setHttpOnly(true);
+ response.addCookie(cookie);
+
+ return ResponseEntity.ok(res);
+ }
+}
diff --git a/backend/auth-service/src/main/java/com/city/demo/domain/User.java b/backend/auth-service/src/main/java/com/city/demo/domain/User.java
new file mode 100644
index 0000000..9008068
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/domain/User.java
@@ -0,0 +1,47 @@
+package com.city.demo.domain;
+
+import jakarta.persistence.*;
+
+import java.util.UUID;
+
+@Entity
+@Table(name = "users")
+public class User {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.UUID)
+ private UUID id;
+
+ @Column(name = "email", unique = true, nullable = false)
+ private String email;
+
+ @Column(name = "password", nullable = false)
+ private byte[] password;
+
+ public User(){
+
+ }
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public byte[] getPassword() {
+ return password;
+ }
+
+ public void setPassword(byte[] password) {
+ this.password = password;
+ }
+
+ public void setId(UUID id) {
+ this.id = id;
+ }
+
+ public UUID getId() {
+ return id;
+ }
+}
diff --git a/backend/auth-service/src/main/java/com/city/demo/repository/UserRepository.java b/backend/auth-service/src/main/java/com/city/demo/repository/UserRepository.java
new file mode 100644
index 0000000..c1b4304
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/repository/UserRepository.java
@@ -0,0 +1,14 @@
+package com.city.demo.repository;
+
+import com.city.demo.domain.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.UUID;
+
+@Repository
+public interface UserRepository extends JpaRepository {
+ User findByEmail(String email);
+// User findById(UUID id);
+}
+
diff --git a/backend/auth-service/src/main/java/com/city/demo/security/HashPassword.java b/backend/auth-service/src/main/java/com/city/demo/security/HashPassword.java
new file mode 100644
index 0000000..991c4b4
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/security/HashPassword.java
@@ -0,0 +1,33 @@
+package com.city.demo.security;
+
+import de.mkammerer.argon2.Argon2;
+import de.mkammerer.argon2.Argon2Factory;
+
+
+public class HashPassword {
+// public static String hashPassword(String password) {
+// Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
+//
+// int iterations = 3;
+// int memory = 65536; // 64MB
+// int parallelism = 1;
+//
+// try {
+// return argon2.hash(iterations, memory, parallelism, password.toCharArray());
+// } finally {
+// argon2.wipeArray(password.toCharArray()); // Optional
+// }
+// }
+
+ public static boolean verifyPassword(String hashedPassword, String password) {
+ Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
+ return argon2.verify(hashedPassword, password.toCharArray());
+ }
+
+ public static void main(String[] args) {
+ String rawPassword = "1234ddd";
+ String HashedPassword = "$argon2id$v=19$m=65536,t=3,p=1$5a8tHxwH8HmE6h3IbmNw3A$SU0R8Pnmm8u05GVrec4aTMUgNzSK5txXPJK9NumLurE";
+ boolean match = verifyPassword(HashedPassword, rawPassword);
+ System.out.println("Password match: " + match);
+ }
+}
diff --git a/backend/auth-service/src/main/java/com/city/demo/JwtDecoder.java b/backend/auth-service/src/main/java/com/city/demo/security/JwtDecoder.java
similarity index 95%
rename from backend/auth-service/src/main/java/com/city/demo/JwtDecoder.java
rename to backend/auth-service/src/main/java/com/city/demo/security/JwtDecoder.java
index 0f8552d..f391596 100644
--- a/backend/auth-service/src/main/java/com/city/demo/JwtDecoder.java
+++ b/backend/auth-service/src/main/java/com/city/demo/security/JwtDecoder.java
@@ -1,8 +1,7 @@
-package com.city.demo;
+package com.city.demo.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
diff --git a/backend/auth-service/src/main/java/com/city/demo/security/JwtTest.java b/backend/auth-service/src/main/java/com/city/demo/security/JwtTest.java
new file mode 100644
index 0000000..322714d
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/security/JwtTest.java
@@ -0,0 +1,38 @@
+package com.city.demo.security;
+
+import com.city.demo.utils.PemUtils;
+import io.jsonwebtoken.Claims;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.HashMap;
+import java.util.Map;
+
+public class JwtTest {
+
+ private final static String PRIVATE_KEY_PATH = "private.pem";
+ private final static String PUBLIC_KEY_PATH = "public.pem";
+
+ public static void main(String[] args) {
+ // 1. Generate a secret key
+ try {
+ RSAPublicKey publicKey = PemUtils.loadPublicKey(PUBLIC_KEY_PATH);
+ RSAPrivateKey privateKey = PemUtils.loadPrivateKey(PRIVATE_KEY_PATH);
+
+ // 2. Create an instance of JwtUtil
+ JwtUtil jwtUtil = new JwtUtil();
+
+ // 3. Generate a token
+ Map claims = new HashMap<>();
+ claims.put("username", "testuser");
+ claims.put("role", "admin");
+ String token = jwtUtil.generateAccessToken(claims);
+ System.out.println("Generated token: " + token);
+ } catch (Exception e) {
+ System.out.println(":C");
+ }
+
+ }
+}
diff --git a/backend/auth-service/src/main/java/com/city/demo/JwtUtil.java b/backend/auth-service/src/main/java/com/city/demo/security/JwtUtil.java
similarity index 51%
rename from backend/auth-service/src/main/java/com/city/demo/JwtUtil.java
rename to backend/auth-service/src/main/java/com/city/demo/security/JwtUtil.java
index 51040c7..8e93dd3 100644
--- a/backend/auth-service/src/main/java/com/city/demo/JwtUtil.java
+++ b/backend/auth-service/src/main/java/com/city/demo/security/JwtUtil.java
@@ -1,18 +1,32 @@
-package com.city.demo;
+package com.city.demo.security;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
+import com.city.demo.utils.PemUtils;
+import org.springframework.stereotype.Component;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
import java.util.Date;
import java.util.Map;
+@Component
public class JwtUtil {
private final Algorithm algorithm;
private final String issuer;
- public JwtUtil(String secretKey, String issuer) {
- this.algorithm = Algorithm.HMAC256(secretKey); // Шифруем секрет
- this.issuer = issuer; // Имя сервиса или приложения
+ private final RSAPublicKey PUBLIC_KEY;
+ private final RSAPrivateKey PRIVATE_KEY;
+
+ public JwtUtil() {
+ try {
+ PUBLIC_KEY = PemUtils.loadPublicKey("src/main/resources/public.pem");
+ PRIVATE_KEY = PemUtils.loadPrivateKey("src/main/resources/private.pem");
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to load RSA keys", e);
+ }
+ this.algorithm = Algorithm.RSA256(PUBLIC_KEY, PRIVATE_KEY); // Шифруем секрет
+ this.issuer = "auth"; // Имя сервиса или приложения
}
public Algorithm getAlgorithm() {
@@ -24,7 +38,7 @@ public String getIssuer() {
}
// Метод для генерации токена
- public String generateToken(Map claims) {
+ public String generateAccessToken(Map claims) {
var jwtBuilder = JWT.create().withIssuer(issuer);
claims.forEach(jwtBuilder::withClaim);
diff --git a/backend/auth-service/src/main/java/com/city/demo/service/AuthService.java b/backend/auth-service/src/main/java/com/city/demo/service/AuthService.java
new file mode 100644
index 0000000..30ac51c
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/service/AuthService.java
@@ -0,0 +1,10 @@
+package com.city.demo.service;
+
+import com.city.demo.service.dto.TokenResponse;
+import org.springframework.stereotype.Service;
+
+@Service
+public interface AuthService {
+ TokenResponse login(String email, String password);
+ TokenResponse refresh(String refreshToken);
+}
diff --git a/backend/auth-service/src/main/java/com/city/demo/LoginRequest.java b/backend/auth-service/src/main/java/com/city/demo/service/dto/LoginRequest.java
similarity index 93%
rename from backend/auth-service/src/main/java/com/city/demo/LoginRequest.java
rename to backend/auth-service/src/main/java/com/city/demo/service/dto/LoginRequest.java
index b552c9b..3cf1484 100644
--- a/backend/auth-service/src/main/java/com/city/demo/LoginRequest.java
+++ b/backend/auth-service/src/main/java/com/city/demo/service/dto/LoginRequest.java
@@ -1,4 +1,4 @@
-package com.city.demo;
+package com.city.demo.service.dto;
public class LoginRequest {
private String login;
diff --git a/backend/auth-service/src/main/java/com/city/demo/service/dto/RefreshRequest.java b/backend/auth-service/src/main/java/com/city/demo/service/dto/RefreshRequest.java
new file mode 100644
index 0000000..e0749b0
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/service/dto/RefreshRequest.java
@@ -0,0 +1,19 @@
+package com.city.demo.service.dto;
+
+public class RefreshRequest {
+ private String refreshToken;
+
+ public RefreshRequest(String refreshToken, String password) {
+ this.refreshToken = refreshToken;
+ }
+
+ // Getters and setters
+ public String getRefreshToken() {
+ return refreshToken;
+ }
+
+ public void setRefreshToken(String refreshToken) {
+ this.refreshToken = refreshToken;
+ }
+
+}
diff --git a/backend/auth-service/src/main/java/com/city/demo/TokenResponse.java b/backend/auth-service/src/main/java/com/city/demo/service/dto/TokenResponse.java
similarity index 95%
rename from backend/auth-service/src/main/java/com/city/demo/TokenResponse.java
rename to backend/auth-service/src/main/java/com/city/demo/service/dto/TokenResponse.java
index 8e29b13..98b704a 100644
--- a/backend/auth-service/src/main/java/com/city/demo/TokenResponse.java
+++ b/backend/auth-service/src/main/java/com/city/demo/service/dto/TokenResponse.java
@@ -1,4 +1,4 @@
-package com.city.demo;
+package com.city.demo.service.dto;
public class TokenResponse {
private String accessToken;
diff --git a/backend/auth-service/src/main/java/com/city/demo/service/impl/AuthServiceImpl.java b/backend/auth-service/src/main/java/com/city/demo/service/impl/AuthServiceImpl.java
new file mode 100644
index 0000000..25fbbc2
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/service/impl/AuthServiceImpl.java
@@ -0,0 +1,76 @@
+package com.city.demo.service.impl;
+
+import com.city.demo.security.HashPassword;
+import com.city.demo.security.JwtUtil;
+import com.city.demo.service.dto.TokenResponse;
+import com.city.demo.domain.User;
+import com.city.demo.repository.UserRepository;
+import com.city.demo.service.AuthService;
+import com.city.demo.utils.RefreshTokenUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+
+@Service
+public class AuthServiceImpl implements AuthService {
+ private final UserRepository userRepository;
+ private final JwtUtil jwtUtil;
+
+ @Autowired
+ public AuthServiceImpl(UserRepository userRepository, JwtUtil jwtUtil){
+ this.userRepository = userRepository;
+ this.jwtUtil = jwtUtil;
+ }
+
+ @Override
+ public TokenResponse login(String email, String password) {
+ // Временная реализация
+ User user = userRepository.findByEmail(email);
+ if (user == null) {
+ throw new RuntimeException("User not found");
+ }
+
+ String test = new String(user.getPassword(), StandardCharsets.UTF_8);
+
+ Boolean isValidPassword = HashPassword.verifyPassword(test, password);
+ if (!isValidPassword) {
+ throw new RuntimeException("Password is not valid");
+ }
+
+ Map claims = new HashMap<>();
+ claims.put("id", user.getId().toString());
+
+ String accessToken = jwtUtil.generateAccessToken(claims);
+
+ String refreshToken = RefreshTokenUtil.generateAndStoreRefreshToken(user.getId().toString());
+ return new TokenResponse(accessToken, refreshToken);
+ }
+
+ @Override
+ public TokenResponse refresh(String refreshToken) {
+ // Временная реализация
+ String userId = RefreshTokenUtil.getUserId(refreshToken);
+ if (userId == null) {
+ throw new RuntimeException("This token doesn't exist");
+ }
+ Optional user = userRepository.findById(UUID.fromString(userId));
+ if (user.isPresent()) {
+ throw new RuntimeException("User not found");
+ }
+
+ Map claims = new HashMap<>();
+ claims.put("id", user.get().getId().toString());
+
+ String newAccessToken = jwtUtil.generateAccessToken(claims);
+
+ RefreshTokenUtil.removeToken(refreshToken);
+ String newRefreshToken = RefreshTokenUtil.generateAndStoreRefreshToken(user.get().getId().toString());
+
+ return new TokenResponse(newAccessToken, newRefreshToken);
+ }
+}
diff --git a/backend/auth-service/src/main/java/com/city/demo/utils/PemUtils.java b/backend/auth-service/src/main/java/com/city/demo/utils/PemUtils.java
new file mode 100644
index 0000000..2616009
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/utils/PemUtils.java
@@ -0,0 +1,46 @@
+package com.city.demo.utils;
+
+import java.io.FileReader;
+import java.security.Security;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+
+public class PemUtils {
+ public static RSAPrivateKey loadPrivateKey(String filepath) throws Exception{
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+
+ try (PEMParser pemParser = new PEMParser(new FileReader(filepath))) {
+ Object object = pemParser.readObject();
+ JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
+
+ if (object instanceof PEMKeyPair) {
+ return (RSAPrivateKey) converter.getKeyPair((PEMKeyPair) object).getPrivate();
+ } else if (object instanceof PrivateKeyInfo) {
+ return (RSAPrivateKey) converter.getPrivateKey((PrivateKeyInfo) object);
+ } else {
+ throw new IllegalArgumentException("Unsupported private key format.");
+ }
+ }
+ }
+
+ public static RSAPublicKey loadPublicKey(String filepath) throws Exception{
+ Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
+
+ try (PEMParser pemParser = new PEMParser(new FileReader(filepath))) {
+ Object object = pemParser.readObject();
+ JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
+
+ if (object instanceof SubjectPublicKeyInfo) {
+ return (RSAPublicKey) converter.getPublicKey((SubjectPublicKeyInfo) object);
+ } else {
+ throw new IllegalArgumentException("Unsupported public key format.");
+ }
+ }
+ }
+}
diff --git a/backend/auth-service/src/main/java/com/city/demo/utils/RefreshTokenUtil.java b/backend/auth-service/src/main/java/com/city/demo/utils/RefreshTokenUtil.java
new file mode 100644
index 0000000..6c7ca5a
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/utils/RefreshTokenUtil.java
@@ -0,0 +1,38 @@
+package com.city.demo.utils;
+
+import java.util.UUID;
+
+public class RefreshTokenUtil {
+
+ private static final long TOKEN_EXPIRATION_SECONDS = 7 * 24 * 60 * 60; // 7 днів
+
+ // Генерація та збереження токена
+ public static String generateAndStoreRefreshToken(String userId) {
+ String refreshToken = UUID.randomUUID().toString();
+ valkeyclass.saveToken(userId, refreshToken, TOKEN_EXPIRATION_SECONDS);
+ System.out.println("🔐 Generated Refresh Token for " + userId + ": " + refreshToken);
+ return refreshToken;
+ }
+
+ // Отримання токена з кешу
+ public static String getUserId(String token) {
+ return valkeyclass.userId(token);
+ }
+
+ // Видалення токена вручну
+ public static void removeToken(String token) {
+ valkeyclass.deleteToken(token);
+ }
+
+ // Тестування
+ public static void main(String[] args) {
+ String userId = "user123";
+ String token = generateAndStoreRefreshToken(userId);
+
+ String cachedUserId = getUserId(token);
+ System.out.println("🧠 Cached userId: " + cachedUserId);
+
+ String mustBeNull = getUserId("invalidToken");
+ System.out.println("🧠 Cached userId for invalid token: " + mustBeNull);
+ }
+}
diff --git a/backend/auth-service/src/main/java/com/city/demo/utils/valkeyclass.java b/backend/auth-service/src/main/java/com/city/demo/utils/valkeyclass.java
new file mode 100644
index 0000000..aa035e2
--- /dev/null
+++ b/backend/auth-service/src/main/java/com/city/demo/utils/valkeyclass.java
@@ -0,0 +1,29 @@
+package com.city.demo.utils;
+
+
+import redis.clients.jedis.Jedis;
+
+public class valkeyclass {
+ private static final Jedis jedis;
+
+ static {
+ jedis = new Jedis("192.168.1.174", 6379); // заміни при потребі
+ jedis.select(4); // використовуємо БД №4
+ System.out.println("✅ Connected to Valkey (DB 4)");
+ }
+
+ // Зберегти токен з TTL
+ public static void saveToken(String userId, String token, long ttlSeconds) {
+ jedis.setex(token, (int) ttlSeconds, userId);
+ }
+
+ // Отримати токен
+ public static String userId(String token) {
+ return jedis.get(token);
+ }
+
+ // Видалити токен
+ public static void deleteToken(String token) {
+ jedis.del(token);
+ }
+}
diff --git a/backend/auth-service/src/main/resources/application.properties b/backend/auth-service/src/main/resources/application.properties
index 2109a44..dc4f731 100644
--- a/backend/auth-service/src/main/resources/application.properties
+++ b/backend/auth-service/src/main/resources/application.properties
@@ -1 +1,7 @@
spring.application.name=demo
+spring.datasource.url=jdbc:postgresql://192.168.1.174:5432/geo_db
+spring.datasource.username=postgres
+spring.datasource.password=sUpers3cRet
+spring.datasource.driver-class-name=org.postgresql.Driver
+spring.jpa.hibernate.ddl-auto=none
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
\ No newline at end of file
diff --git a/backend/auth-service/src/main/resources/private.pem b/backend/auth-service/src/main/resources/private.pem
new file mode 100644
index 0000000..a8c64c2
--- /dev/null
+++ b/backend/auth-service/src/main/resources/private.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA1Ytzek1ZpurQJ+wCb5q54IBg33MCA+QXbgQ3G1udE7f1EjRK
+lLmMOyedMVr5t5Ssyh6oQYMKjr/5akRfwoXmJU4JjkdmS6slqho/vYQQoOB1+HNz
+eOIY8jMMrbVMLYnwGBH3DNgOFJUFQiPPJ/o9+GSvDwCbQ4cz1hSNKpywwAuHNowe
+7umC/9AzbOjSUfZcrh+qf2BbJg2Am5cYtdZVh1PBXBiJCR+Bt+eEag8OUCSPBe4i
+OfyGttjPsLOHxObvlsuERuq6AowqvY0bam1LtbHs4bZ7Jo0k7X0dYI9CriaeaNHG
+s/lBWfueGGJNlbplj9tZz9Qe/fIkK4NLN3qijQIDAQABAoIBAHOPAFG50/8g6L4B
+2JuehmH39vRUn02NvCQcAdo7LHiEPwm35HgxJLLKUCVqTuJKgWrrRjAVgrwXJRid
+oZh5rz6WyVMGTlQ6bKptg/rBoeH9NbnaeC19ZPSELaM/DddC4vuTlTaNIKmArxTI
+ngzLPYfSrG0Y7eyQGLwL6QybOIbjeNOhQejxfbB6QiDYXeCmxS8L8s/PdGfvXMAX
+ZNB3VJijgz2COa5m1UtzsaGRhJWvO7YtCZS0B0F9Kf1WuB7Yq+SjKazUl3p6DVJ3
+GrTnPtjyFbBnvEHoqX8M82JH+IWKkoE6RhGga0XgPp8A0q/TxKWlZKSjuGJH20L3
+C9E8IZ0CgYEA9TqnomYeLGulVfPXeZpdVx9dE0QgZEWysKeSX5oW+DJ8KTjQJMBk
+IsNob6EgtCjNFARP4r0UWCMLhtD6b1ZynuOv4yZMom8lDNZmnQL9ah+nrZQlCgqE
+nIUATo6HB5zR0lBetQu1eDrg9Sr/aj6TtUmXLOVqIPbHRL31TChFAEcCgYEA3uyJ
+1YdC11kHEJXm0V14tqIXCiZdepDWtCbkgDqBBrJToO/XWbgWEnW0oGam1NBw3zV/
+nWwxJyOAoRJcsmGOfbCJq+ICtPjgASmt29/2dmnSGR087JjTSYCDqYdRvoLCn+fv
+sw+iwF0Mb6IYZ14BW9/HhrvaUxqXJQWMytx6pIsCgYEA2xekXXtOyfECrmBEyugX
+LJdg2K2tIukCVMHiDHfmvaidNVcE31DkJgrfa5xRU6SAVLnlhXXYj0YALlrW5FIf
+5cUWh6LYJOeX+ngzBWR+dCoD7RjGXso24IATXhwIl8yLyZL0ilwDrX8tLN1VhN6T
+qQwufHYa84iHT+tHFNuFilECgYAhDIhSs77hFj3J469Ykrb2vIkV5CIvrZFnG+z8
+ZbADQiTL00Ll9jive+vPa+iH3G8faFsHB6cx2j2To5lCiNwLHkUKynMESXFMOtWf
+p1ButbmEJ6WEnhOU7gyW8YzY5aHc+xG6RjMmuaTCWBGuIW2qJT77ZpYzg9msj2oU
+GSPFiQKBgCNvGH1q+GOWP1+Dw9FIbL89yQoeT95qcUb2VYluQ4O1xRpynu7F5GIl
+5wKTq5xWM4yKkEwHSDSPVGrMBk4FwVAxprab2ZQu1BTklDgWf1J6tgXc+y2TzmSE
+Nekr7RoWpNJ806TOQVvq8wESGpKTSoDINpd35L18Pv3lRrPjIU7r
+-----END RSA PRIVATE KEY-----
diff --git a/backend/auth-service/src/main/resources/public.pem b/backend/auth-service/src/main/resources/public.pem
new file mode 100644
index 0000000..bddff5a
--- /dev/null
+++ b/backend/auth-service/src/main/resources/public.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Ytzek1ZpurQJ+wCb5q5
+4IBg33MCA+QXbgQ3G1udE7f1EjRKlLmMOyedMVr5t5Ssyh6oQYMKjr/5akRfwoXm
+JU4JjkdmS6slqho/vYQQoOB1+HNzeOIY8jMMrbVMLYnwGBH3DNgOFJUFQiPPJ/o9
++GSvDwCbQ4cz1hSNKpywwAuHNowe7umC/9AzbOjSUfZcrh+qf2BbJg2Am5cYtdZV
+h1PBXBiJCR+Bt+eEag8OUCSPBe4iOfyGttjPsLOHxObvlsuERuq6AowqvY0bam1L
+tbHs4bZ7Jo0k7X0dYI9CriaeaNHGs/lBWfueGGJNlbplj9tZz9Qe/fIkK4NLN3qi
+jQIDAQAB
+-----END PUBLIC KEY-----