diff --git a/Documents/JournalDeBord.md b/Documents/JournalDeBord.md
index 2d45fe139d0ef0b93eb503b2f106c8c08d0566a0..41579a32da8701c44b3d2e6a6542bbe859bbd6de 100644
--- a/Documents/JournalDeBord.md
+++ b/Documents/JournalDeBord.md
@@ -9,4 +9,5 @@
 | 07.11.2024 - 10.11.2024 | Recherche approfondie sur BDNS                       | - | -                      | -                                                              |                                                                       |
 | 14.11.2024 | Envoie email j+s pour plus d'info sur API                       | - | -                      | -   
 | 17.11.2024 | Début de base de données                       | - | -                      | -   
-| 21.11.2024 | Génération du projet et initialisation                       | - | -                      | -   
\ No newline at end of file
+| 21.11.2024 | Génération du projet et initialisation                       | - | -                      | -   
+| 23-24.11.2024 | Création d'utilisateur                       | - | -                      | -   
\ No newline at end of file
diff --git a/VolleyHub/backend/.gitattributes:Zone.Identifier b/VolleyHub/backend/.gitattributes:Zone.Identifier
deleted file mode 100644
index b646743bfaa9649edaf896e361737b00514e33c4..0000000000000000000000000000000000000000
--- a/VolleyHub/backend/.gitattributes:Zone.Identifier
+++ /dev/null
@@ -1,3 +0,0 @@
-[ZoneTransfer]
-ZoneId=3
-ReferrerUrl=C:\Users\titic\Downloads\volleyhub.zip
diff --git a/VolleyHub/backend/.gitignore:Zone.Identifier b/VolleyHub/backend/.gitignore:Zone.Identifier
deleted file mode 100644
index b646743bfaa9649edaf896e361737b00514e33c4..0000000000000000000000000000000000000000
--- a/VolleyHub/backend/.gitignore:Zone.Identifier
+++ /dev/null
@@ -1,3 +0,0 @@
-[ZoneTransfer]
-ZoneId=3
-ReferrerUrl=C:\Users\titic\Downloads\volleyhub.zip
diff --git a/VolleyHub/backend/HELP.md:Zone.Identifier b/VolleyHub/backend/HELP.md:Zone.Identifier
deleted file mode 100644
index b646743bfaa9649edaf896e361737b00514e33c4..0000000000000000000000000000000000000000
--- a/VolleyHub/backend/HELP.md:Zone.Identifier
+++ /dev/null
@@ -1,3 +0,0 @@
-[ZoneTransfer]
-ZoneId=3
-ReferrerUrl=C:\Users\titic\Downloads\volleyhub.zip
diff --git a/VolleyHub/backend/mvnw.cmd:Zone.Identifier b/VolleyHub/backend/mvnw.cmd:Zone.Identifier
deleted file mode 100644
index b646743bfaa9649edaf896e361737b00514e33c4..0000000000000000000000000000000000000000
--- a/VolleyHub/backend/mvnw.cmd:Zone.Identifier
+++ /dev/null
@@ -1,3 +0,0 @@
-[ZoneTransfer]
-ZoneId=3
-ReferrerUrl=C:\Users\titic\Downloads\volleyhub.zip
diff --git a/VolleyHub/backend/mvnw:Zone.Identifier b/VolleyHub/backend/mvnw:Zone.Identifier
deleted file mode 100644
index b646743bfaa9649edaf896e361737b00514e33c4..0000000000000000000000000000000000000000
--- a/VolleyHub/backend/mvnw:Zone.Identifier
+++ /dev/null
@@ -1,3 +0,0 @@
-[ZoneTransfer]
-ZoneId=3
-ReferrerUrl=C:\Users\titic\Downloads\volleyhub.zip
diff --git a/VolleyHub/backend/pom.xml b/VolleyHub/backend/pom.xml
index 2d07b6434b14e10ae3a5a24983e61a6e71706fe5..659f1379502768dff9b423710bec956d9a43b9ac 100644
--- a/VolleyHub/backend/pom.xml
+++ b/VolleyHub/backend/pom.xml
@@ -1,86 +1,115 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	<parent>
-		<groupId>org.springframework.boot</groupId>
-		<artifactId>spring-boot-starter-parent</artifactId>
-		<version>3.3.5</version>
-		<relativePath/> <!-- lookup parent from repository -->
-	</parent>
-	<groupId>com.hepia</groupId>
-	<artifactId>volleyhub</artifactId>
-	<version>0.0.1-SNAPSHOT</version>
-	<name>volleyhub</name>
-	<description>Backend for volleyhub</description>
-	<url/>
-	<licenses>
-		<license/>
-	</licenses>
-	<developers>
-		<developer/>
-	</developers>
-	<scm>
-		<connection/>
-		<developerConnection/>
-		<tag/>
-		<url/>
-	</scm>
-	<properties>
-		<java.version>17</java.version>
-	</properties>
-	<dependencies>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-data-jpa</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-security</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-web</artifactId>
-		</dependency>
-
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-devtools</artifactId>
-			<scope>runtime</scope>
-			<optional>true</optional>
-		</dependency>
-		<dependency>
-			<groupId>org.projectlombok</groupId>
-			<artifactId>lombok</artifactId>
-			<optional>true</optional>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-test</artifactId>
-			<scope>test</scope>
-		</dependency>
-		<dependency>
-			<groupId>org.springframework.security</groupId>
-			<artifactId>spring-security-test</artifactId>
-			<scope>test</scope>
-		</dependency>
-	</dependencies>
-
-	<build>
-		<plugins>
-			<plugin>
-				<groupId>org.springframework.boot</groupId>
-				<artifactId>spring-boot-maven-plugin</artifactId>
-				<configuration>
-					<excludes>
-						<exclude>
-							<groupId>org.projectlombok</groupId>
-							<artifactId>lombok</artifactId>
-						</exclude>
-					</excludes>
-				</configuration>
-			</plugin>
-		</plugins>
-	</build>
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.3.5</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.hepia</groupId>
+    <artifactId>volleyhub</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>volleyhub</name>
+    <description>Backend for volleyhub</description>
+    <url/>
+    <licenses>
+        <license/>
+    </licenses>
+    <developers>
+        <developer/>
+    </developers>
+    <scm>
+        <connection/>
+        <developerConnection/>
+        <tag/>
+        <url/>
+    </scm>
+    <properties>
+        <java.version>17</java.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.12.3</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.12.3</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.12.3</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+    </dependencies>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/VolleyHub/backend/pom.xml:Zone.Identifier b/VolleyHub/backend/pom.xml:Zone.Identifier
deleted file mode 100644
index b646743bfaa9649edaf896e361737b00514e33c4..0000000000000000000000000000000000000000
--- a/VolleyHub/backend/pom.xml:Zone.Identifier
+++ /dev/null
@@ -1,3 +0,0 @@
-[ZoneTransfer]
-ZoneId=3
-ReferrerUrl=C:\Users\titic\Downloads\volleyhub.zip
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/VolleyhubApplication.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/VolleyhubApplication.java
index 1ee71768ec5dc033d80caec3c6634f245d540a86..eea21d3315e41e087554329de51ad949dd85b0c9 100644
--- a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/VolleyhubApplication.java
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/VolleyhubApplication.java
@@ -1,13 +1,29 @@
 package com.hepia.volleyhub;
 
+import com.hepia.volleyhub.model.entity.Role;
+import com.hepia.volleyhub.repository.RoleRepository;
+import org.springframework.boot.CommandLineRunner;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.scheduling.annotation.EnableAsync;
 
 @SpringBootApplication
+@EnableAsync
+@EnableJpaAuditing
 public class VolleyhubApplication {
 
-	public static void main(String[] args) {
-		SpringApplication.run(VolleyhubApplication.class, args);
-	}
+    public static void main(String[] args) {
+        SpringApplication.run(VolleyhubApplication.class, args);
+    }
 
+    @Bean
+    public CommandLineRunner runner(RoleRepository roleRepository) {
+        return args -> {
+            if (roleRepository.findByName("USER").isEmpty()) {
+                roleRepository.save(Role.builder().name("USER").build());
+            }
+        };
+    }
 }
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/VolleyhubApplication.java:Zone.Identifier b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/VolleyhubApplication.java:Zone.Identifier
deleted file mode 100644
index b646743bfaa9649edaf896e361737b00514e33c4..0000000000000000000000000000000000000000
--- a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/VolleyhubApplication.java:Zone.Identifier
+++ /dev/null
@@ -1,3 +0,0 @@
-[ZoneTransfer]
-ZoneId=3
-ReferrerUrl=C:\Users\titic\Downloads\volleyhub.zip
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/config/BeansConfig.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/config/BeansConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..8db95cfc6f9f0002aa0811ca2ced7e272f15cbe8
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/config/BeansConfig.java
@@ -0,0 +1,69 @@
+package com.hepia.volleyhub.config;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.springframework.http.HttpHeaders.*;
+import static org.springframework.http.HttpHeaders.AUTHORIZATION;
+import static org.springframework.http.HttpMethod.*;
+
+@Configuration
+@RequiredArgsConstructor
+public class BeansConfig {
+    private final UserDetailsService userDetailsService;
+
+    @Bean
+    public AuthenticationProvider authenticationProvider() {
+        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+        provider.setUserDetailsService(userDetailsService);
+        provider.setPasswordEncoder(passwordEncoder());
+        return provider;
+    }
+
+    @Bean
+    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
+        return authenticationConfiguration.getAuthenticationManager();
+    }
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+
+    @Bean
+    public CorsFilter corsFilter() {
+        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        final CorsConfiguration corsConfiguration = new CorsConfiguration();
+        corsConfiguration.setAllowCredentials(true);
+        corsConfiguration.setAllowedOrigins(Collections.singletonList("http://localhost:4200"));
+        corsConfiguration.setAllowedHeaders(Arrays.asList(
+                ORIGIN,
+                CONTENT_TYPE,
+                ACCEPT,
+                AUTHORIZATION
+        ));
+        corsConfiguration.setAllowedMethods(Arrays.asList(
+                GET.name(),
+                POST.name(),
+                PUT.name(),
+                PATCH.name(),
+                DELETE.name()
+        ));
+        source.registerCorsConfiguration("/**", corsConfiguration);
+        return new CorsFilter(source);
+    }
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/config/OpenApiConfig.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/config/OpenApiConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..034c2499cbf4148f1a04f3fbd9ec06b61707995e
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/config/OpenApiConfig.java
@@ -0,0 +1,48 @@
+package com.hepia.volleyhub.config;
+
+import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
+import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
+import io.swagger.v3.oas.annotations.info.Contact;
+import io.swagger.v3.oas.annotations.info.Info;
+import io.swagger.v3.oas.annotations.info.License;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.security.SecurityScheme;
+import io.swagger.v3.oas.annotations.servers.Server;
+
+@OpenAPIDefinition(
+        info = @Info(
+                contact = @Contact(
+                        name = "Thibault Capt",
+                        email = "thibault.capt@etu.hesge.ch",
+                        url = "https://gitedu.hesge.ch/niklaus.eggenber/capt-24-25"
+                ),
+                description = "Documentation for the VolleyHub API",
+                title = "OpenAPI VolleyHub specification",
+                version = "1.0.0",
+                license = @License(
+                        name = "HES-SO/HEPIA",
+                        url = "https://www.hesge.ch/hepia"
+                ),
+                termsOfService = "https://www.hesge.ch/hepia/mentions-legales"
+        ),
+        servers = @Server(
+                url = "http://localhost:8088/api/v1",
+                description = "Local environment"
+        ),
+        security = {
+                @SecurityRequirement(
+                        name = "bearerAuth"
+                )
+        }
+)
+@SecurityScheme(
+        name = "bearerAuth",
+        description = "JWT auth token",
+        scheme = "bearer",
+        type = SecuritySchemeType.HTTP,
+        bearerFormat = "JWT",
+        in = SecuritySchemeIn.HEADER
+)
+public class OpenApiConfig {
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/config/SecurityConfig.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/config/SecurityConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..17db386137128864c619b7c1c9b9c435089a54fa
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/config/SecurityConfig.java
@@ -0,0 +1,57 @@
+package com.hepia.volleyhub.config;
+
+
+import com.hepia.volleyhub.filter.JwtAuthFilter;
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import static org.springframework.security.config.Customizer.withDefaults;
+import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
+
+@Configuration
+@EnableWebSecurity
+@RequiredArgsConstructor
+@EnableMethodSecurity(securedEnabled = true)
+public class SecurityConfig {
+    private final JwtAuthFilter jwtAuthFilter;
+    private final AuthenticationProvider authenticationProvider;
+
+    @Bean
+    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+        // With new Spring Security 5.4, we can use the following code to disable CSRF
+        http
+                .cors(withDefaults())
+                .csrf(AbstractHttpConfigurer::disable)
+                .authorizeHttpRequests(req ->
+                        req.requestMatchers(
+                                        "/auth/**",
+                                        "/v2/api-docs",
+                                        "/v3/api-docs",
+                                        "/v3/api-docs/**",
+                                        "/swagger-resources",
+                                        "/swagger-resources/**",
+                                        "/configuration/ui",
+                                        "/configuration/security",
+                                        "/swagger-ui/**",
+                                        "/webjars/**",
+                                        "/swagger-ui.html"
+                                ).permitAll()
+                                .anyRequest()
+                                .authenticated()
+                )
+                .sessionManagement(session ->
+                        session.sessionCreationPolicy(STATELESS)
+                )
+                .authenticationProvider(authenticationProvider)
+                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
+        return http.build();
+    }
+}
\ No newline at end of file
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/controller/AuthenticationController.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/controller/AuthenticationController.java
new file mode 100644
index 0000000000000000000000000000000000000000..16b935f3d40a8eed347d5f98503457dadd39182f
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/controller/AuthenticationController.java
@@ -0,0 +1,44 @@
+package com.hepia.volleyhub.controller;
+
+import com.hepia.volleyhub.model.dto.AuthenticationRequest;
+import com.hepia.volleyhub.model.dto.AuthenticationResponse;
+import com.hepia.volleyhub.model.dto.RegistrationRequest;
+import com.hepia.volleyhub.service.AuthenticationService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.mail.MessagingException;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("auth")
+@RequiredArgsConstructor
+@Tag(name = "Authentication")
+public class AuthenticationController {
+    private final AuthenticationService authenticationService;
+
+    @PostMapping("/register")
+    @ResponseStatus(HttpStatus.ACCEPTED)
+    public ResponseEntity<?> register(
+            @RequestBody @Valid RegistrationRequest request
+    ) throws MessagingException {
+        authenticationService.register(request);
+        return ResponseEntity.accepted().build();
+    }
+
+    @PostMapping("/authenticate")
+    public ResponseEntity<AuthenticationResponse> authenticate(
+            @RequestBody @Valid AuthenticationRequest request
+    ) {
+        return ResponseEntity.ok(authenticationService.authenticate(request));
+    }
+
+    @GetMapping("/activate-account")
+    public void confirmAccount(
+            @RequestParam("token") String token
+    ) throws MessagingException {
+        authenticationService.activateAccount(token);
+    }
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/filter/JwtAuthFilter.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/filter/JwtAuthFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..899cf805ed947185ba565abd9c04223ee553334d
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/filter/JwtAuthFilter.java
@@ -0,0 +1,71 @@
+package com.hepia.volleyhub.filter;
+
+
+import com.hepia.volleyhub.service.JwtService;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.RequiredArgsConstructor;
+import org.springframework.lang.NonNull;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Service;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+import static org.springframework.http.HttpHeaders.AUTHORIZATION;
+
+@Service
+@RequiredArgsConstructor
+public class JwtAuthFilter extends OncePerRequestFilter {
+    private final JwtService jwtService;
+    private final UserDetailsService userDetailsService;
+
+    @Override
+    protected void doFilterInternal(
+            @NonNull HttpServletRequest request,
+            @NonNull HttpServletResponse response,
+            @NonNull FilterChain filterChain
+    ) throws ServletException, IOException {
+        // Every request will pass through this filter
+        if (request.getServletPath().contains("/api/v1/auth")) {
+            // If the request is for the authentication endpoint, we don't need to check for the token
+            filterChain.doFilter(request, response);
+            return;
+        }
+
+        final String authorizationHeader = request.getHeader(AUTHORIZATION);
+        final String jwt;
+        final String email;
+        if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
+            // If the request is for the authentication endpoint, we don't need to check for the token
+            filterChain.doFilter(request, response);
+            return;
+        }
+
+        jwt = authorizationHeader.substring(7); // "bearer " is 7 characters long
+        email = jwtService.extractUsername(jwt);
+        if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+            UserDetails userDetails = userDetailsService.loadUserByUsername(email);
+            if (jwtService.isTokenValid(jwt, userDetails)) {
+                // If the token is valid, we authenticate the user
+                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
+                        userDetails,
+                        null,
+                        userDetails.getAuthorities()
+                );
+                // We set the details of the authentication
+                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+                // We set the authentication in the context
+                SecurityContextHolder.getContext().setAuthentication(authToken);
+            }
+        }
+        // We continue with the filter chain
+        filterChain.doFilter(request, response);
+    }
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/handler/GlobalExceptionHandler.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/handler/GlobalExceptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..477f5771d6db86a3fc1a2cfb3dfd725c1afb4131
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/handler/GlobalExceptionHandler.java
@@ -0,0 +1,99 @@
+package com.hepia.volleyhub.handler;
+
+
+import com.hepia.volleyhub.model.dto.ExceptionResponse;
+import jakarta.mail.MessagingException;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.authentication.LockedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.hepia.volleyhub.model.enums.BusinessErrorCode.*;
+import static org.springframework.http.HttpStatus.*;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+    @ExceptionHandler(LockedException.class)
+    public ResponseEntity<ExceptionResponse> handleException(LockedException e) {
+        return ResponseEntity
+                .status(UNAUTHORIZED)
+                .body(
+                        ExceptionResponse.builder()
+                                .businessErrorCode(ACCOUNT_LOCKED.getCode())
+                                .businessExceptionDescription(ACCOUNT_LOCKED.getDescription())
+                                .error(e.getMessage())
+                                .build()
+                );
+    }
+
+    @ExceptionHandler(DisabledException.class)
+    public ResponseEntity<ExceptionResponse> handleException(DisabledException e) {
+        return ResponseEntity
+                .status(UNAUTHORIZED)
+                .body(
+                        ExceptionResponse.builder()
+                                .businessErrorCode(ACCOUNT_DISABLED.getCode())
+                                .businessExceptionDescription(ACCOUNT_DISABLED.getDescription())
+                                .error(e.getMessage())
+                                .build()
+                );
+    }
+
+    @ExceptionHandler(BadCredentialsException.class)
+    public ResponseEntity<ExceptionResponse> handleException(BadCredentialsException e) {
+        return ResponseEntity
+                .status(UNAUTHORIZED)
+                .body(
+                        ExceptionResponse.builder()
+                                .businessErrorCode(BAD_CREDENTIALS.getCode())
+                                .businessExceptionDescription(BAD_CREDENTIALS.getDescription())
+                                .error(BAD_CREDENTIALS.getDescription())
+                                .build()
+                );
+    }
+
+    @ExceptionHandler(MessagingException.class)
+    public ResponseEntity<ExceptionResponse> handleException(MessagingException e) {
+        return ResponseEntity
+                .status(INTERNAL_SERVER_ERROR)
+                .body(
+                        ExceptionResponse.builder()
+                                .error(e.getMessage())
+                                .build()
+                );
+    }
+
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public ResponseEntity<ExceptionResponse> handleException(MethodArgumentNotValidException e) {
+        Set<String> errors = new HashSet<>();
+        e.getBindingResult().getAllErrors().forEach(error -> errors.add(error.getDefaultMessage()));
+        return ResponseEntity
+                .status(BAD_REQUEST)
+                .body(
+                        ExceptionResponse.builder()
+                                .validationErrors(errors)
+                                .build()
+                );
+    }
+
+    @ExceptionHandler(Exception.class)
+    public ResponseEntity<ExceptionResponse> handleException(Exception e) {
+        // Log the exception
+        // TODO: Replace with a logger
+        e.printStackTrace();
+        return ResponseEntity
+                .status(INTERNAL_SERVER_ERROR)
+                .body(
+                        ExceptionResponse.builder()
+                                .businessExceptionDescription("Internal server error, please contact the administrator")
+                                .error(e.getMessage())
+                                .build()
+                );
+    }
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/AuthenticationRequest.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/AuthenticationRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8837f24af5a65128be1d3eb1242cad91c2299f28
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/AuthenticationRequest.java
@@ -0,0 +1,21 @@
+package com.hepia.volleyhub.model.dto;
+
+import jakarta.validation.constraints.*;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+public class AuthenticationRequest {
+    @Email(message = "Email is not valid")
+    @NotEmpty(message = "Email is required")
+    @NotBlank(message = "Email is required")
+    private String email;
+
+    @NotEmpty(message = "Password is required")
+    @NotNull(message = "Password is required")
+    @Size(min = 8, message = "Password must be at least 8 characters long")
+    private String password;
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/AuthenticationResponse.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/AuthenticationResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..47f45662f1b42bb391438ef69719a924692a71ca
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/AuthenticationResponse.java
@@ -0,0 +1,13 @@
+package com.hepia.volleyhub.model.dto;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+public class AuthenticationResponse {
+    private String token;
+}
+
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/ExceptionResponse.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/ExceptionResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..51c26d86d89c945bf5bdbdf21c08be2393bfca4e
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/ExceptionResponse.java
@@ -0,0 +1,21 @@
+package com.hepia.volleyhub.model.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.*;
+
+import java.util.Map;
+import java.util.Set;
+
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+public class ExceptionResponse {
+    private int businessErrorCode;
+    private String businessExceptionDescription;
+    private String error;
+    private Set<String> validationErrors;
+    private Map<String, String> errors;
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/RegistrationRequest.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/RegistrationRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cc737ce9ef074a795688a077ad2df0242852cc95
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/dto/RegistrationRequest.java
@@ -0,0 +1,21 @@
+package com.hepia.volleyhub.model.dto;
+
+import jakarta.validation.constraints.*;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@Builder
+public class RegistrationRequest {
+    @Email(message = "Email is not valid")
+    @NotEmpty(message = "Email is required")
+    @NotBlank(message = "Email is required")
+    private String email;
+
+    @NotEmpty(message = "Password is required")
+    @NotNull(message = "Password is required")
+    @Size(min = 8, message = "Password must be at least 8 characters long")
+    private String password;
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/entity/Role.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/entity/Role.java
new file mode 100644
index 0000000000000000000000000000000000000000..71b4da3ed65c9bc7d6d18937fdca5a896c906d5b
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/entity/Role.java
@@ -0,0 +1,38 @@
+package com.hepia.volleyhub.model.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.*;
+import lombok.*;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@EntityListeners(AuditingEntityListener.class)
+public class Role {
+    @Id
+    @GeneratedValue
+    private long id;
+    @Column(unique = true)
+    private String name;
+
+    @ManyToMany(mappedBy = "roles")
+    @JsonIgnore // Prevent infinite recursion
+    private List<User> users;
+
+    @CreatedDate
+    @Column(updatable = false, nullable = false)
+    private LocalDate createdAt;
+
+    @LastModifiedDate
+    @Column(insertable = false)
+    private LocalDate lastModifiedAt;
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/entity/Token.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/entity/Token.java
new file mode 100644
index 0000000000000000000000000000000000000000..544b92ce943a9e90d063ce80169758adbc194dd3
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/entity/Token.java
@@ -0,0 +1,27 @@
+package com.hepia.volleyhub.model.entity;
+
+import jakarta.persistence.*;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+public class Token {
+    @Id
+    @GeneratedValue
+    private long id;
+    private String token;
+    private LocalDateTime createdAt;
+    private LocalDateTime expiresAt;
+    private LocalDateTime validatedAt;
+
+    @ManyToOne
+    @JoinColumn(name = "userId", nullable = false)
+    private User user;
+
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/entity/User.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/entity/User.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d28ceed192b77aae00c9fb68be9a9f4df003f24
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/entity/User.java
@@ -0,0 +1,89 @@
+package com.hepia.volleyhub.model.entity;
+
+import jakarta.persistence.*;
+import lombok.*;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.security.Principal;
+import java.time.LocalDate;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Getter
+@Setter
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Table(name = "_user")
+@EntityListeners(AuditingEntityListener.class)
+public class User implements UserDetails, Principal {
+    @Id
+    @GeneratedValue
+    private long id;
+    @Column(unique = true)
+    private String email;
+    private String password;
+    private boolean accountLocked;
+    private boolean enabled;
+
+    @ManyToMany(fetch = FetchType.EAGER)
+    private List<Role> roles;
+
+    @CreatedDate
+    @Column(updatable = false, nullable = false)
+    private LocalDate createdAt;
+
+    @LastModifiedDate
+    @Column(insertable = false)
+    private LocalDate lastModifiedAt;
+
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return roles
+                .stream()
+                .map(r -> new SimpleGrantedAuthority(r.getName()))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public String getName() {
+        return email;
+    }
+
+    @Override
+    public String getUsername() {
+        return email;
+    }
+
+    @Override
+    public String getPassword() {
+        return password;
+    }
+
+    @Override
+    public boolean isAccountNonExpired() {
+        return UserDetails.super.isAccountNonExpired(); // true
+    }
+
+    @Override
+    public boolean isAccountNonLocked() {
+        return !accountLocked;
+    }
+
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return UserDetails.super.isCredentialsNonExpired(); // true
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return enabled;
+    }
+}
\ No newline at end of file
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/enums/BusinessErrorCode.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/enums/BusinessErrorCode.java
new file mode 100644
index 0000000000000000000000000000000000000000..4586603edf7fe4191680632631e4c8bc3577207b
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/enums/BusinessErrorCode.java
@@ -0,0 +1,23 @@
+package com.hepia.volleyhub.model.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+
+import static org.springframework.http.HttpStatus.NOT_IMPLEMENTED;
+
+@Getter
+@AllArgsConstructor
+public enum BusinessErrorCode {
+    NO_CODE(0, NOT_IMPLEMENTED, "No code"),
+    INCORRECT_CURRENT_PASSWORD(300, HttpStatus.BAD_REQUEST, "Current password is incorrect"),
+    NEW_PASSWORD_DOES_NOT_MATCH(301, HttpStatus.BAD_REQUEST, "New password does not match"),
+    ACCOUNT_LOCKED(302, HttpStatus.LOCKED, "User account is locked"),
+    ACCOUNT_DISABLED(303, HttpStatus.LOCKED, "User account is disabled"),
+    BAD_CREDENTIALS(304, HttpStatus.LOCKED, "email and / or password are incorrect"),
+    ;
+    private final int code;
+    private final HttpStatus httpStatus;
+    private final String description;
+}
+
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/enums/EmailTemplateName.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/enums/EmailTemplateName.java
new file mode 100644
index 0000000000000000000000000000000000000000..781ad6602ad71e19cbadcf86dc0f580145fe3c93
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/model/enums/EmailTemplateName.java
@@ -0,0 +1,13 @@
+package com.hepia.volleyhub.model.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum EmailTemplateName {
+    ACTIVATION("activate_account");
+
+    private final String name;
+}
+
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/repository/RoleRepository.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/repository/RoleRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..4dedb93fc806b3fea0a0915a5bee48af21433e35
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/repository/RoleRepository.java
@@ -0,0 +1,10 @@
+package com.hepia.volleyhub.repository;
+
+import com.hepia.volleyhub.model.entity.Role;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface RoleRepository extends JpaRepository<Role, Long> {
+    Optional<Role> findByName(String name);
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/repository/TokenRepository.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/repository/TokenRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5638fd32c9894cb326af48b5f18da683fdf8977
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/repository/TokenRepository.java
@@ -0,0 +1,10 @@
+package com.hepia.volleyhub.repository;
+
+import com.hepia.volleyhub.model.entity.Token;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface TokenRepository extends JpaRepository<Token, Long> {
+    Optional<Token> findByToken(String token);
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/repository/UserRepository.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/repository/UserRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..925855094dbabf43b0a248afb82b064f4c234109
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/repository/UserRepository.java
@@ -0,0 +1,10 @@
+package com.hepia.volleyhub.repository;
+
+import com.hepia.volleyhub.model.entity.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface UserRepository extends JpaRepository<User, Long> {
+    Optional<User> findByEmail(String email);
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/AuthenticationService.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/AuthenticationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ceff2ca0c0a915ec15cfe3ee6904fd567e05669
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/AuthenticationService.java
@@ -0,0 +1,131 @@
+package com.hepia.volleyhub.service;
+
+import com.hepia.volleyhub.model.dto.AuthenticationRequest;
+import com.hepia.volleyhub.model.dto.AuthenticationResponse;
+import com.hepia.volleyhub.model.dto.RegistrationRequest;
+import com.hepia.volleyhub.model.entity.Token;
+import com.hepia.volleyhub.model.entity.User;
+import com.hepia.volleyhub.repository.RoleRepository;
+import com.hepia.volleyhub.repository.TokenRepository;
+import com.hepia.volleyhub.repository.UserRepository;
+import jakarta.mail.MessagingException;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+import java.security.SecureRandom;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+
+import static com.hepia.volleyhub.model.enums.EmailTemplateName.ACTIVATION;
+
+
+@Service
+@RequiredArgsConstructor
+public class AuthenticationService {
+    private final RoleRepository roleRepository;
+    private final PasswordEncoder passwordEncoder;
+    private final UserRepository userRepository;
+    private final TokenRepository tokenRepository;
+    private final EmailService emailService;
+    private final AuthenticationManager authenticationManager;
+    private final JwtService jwtService;
+
+    @Value("${application.mailing.frontend.activation-url}")
+    String activationUrl;
+
+    public void register(RegistrationRequest request) throws MessagingException {
+        var userRole = roleRepository.findByName("USER")
+                // TODO: better exception handling
+                .orElseThrow(() -> new IllegalStateException("Error: Role user is not found."));
+        var user = User.builder()
+                .email(request.getEmail())
+                .password(passwordEncoder.encode(request.getPassword()))
+                .accountLocked(false)
+                .enabled(false)
+                .roles(List.of(userRole))
+                .build();
+
+        userRepository.save(user);
+        sendValidationEmail(user);
+    }
+
+    private void sendValidationEmail(User user) throws MessagingException {
+        String newToken = generateAndSaveActivationToken(user);
+        // Send email with token
+        emailService.sendEmail(
+                user.getEmail(),
+                user.getEmail(),
+                ACTIVATION,
+                activationUrl,
+                newToken,
+                "Activate your account"
+        );
+
+    }
+
+    private String generateAndSaveActivationToken(User user) {
+        // Generate a token
+        String generatedToken = generateActivationCode(6);
+        var token = Token
+                .builder()
+                .token(generatedToken)
+                .createdAt(LocalDateTime.now())
+                .expiresAt(LocalDateTime.now().plusMinutes(15))
+                .user(user)
+                .build();
+        tokenRepository.save(token);
+        return generatedToken;
+    }
+
+    private String generateActivationCode(int length) {
+        String characters = "0123456789";
+        StringBuilder code = new StringBuilder();
+        SecureRandom random = new SecureRandom();
+        for (int i = 0; i < length; i++) {
+            // Generate a random index
+            int randomId = random.nextInt(characters.length());
+            // Append the character at the random index
+            code.append(characters.charAt(randomId));
+        }
+
+        return code.toString();
+    }
+
+    public AuthenticationResponse authenticate(AuthenticationRequest request) {
+        var auth = authenticationManager.authenticate(
+                new UsernamePasswordAuthenticationToken(
+                        request.getEmail(),
+                        request.getPassword()
+                )
+        );
+        var claims = new HashMap<String, Object>();
+        var user = ((User) auth.getPrincipal());
+        var jwtToken = jwtService.generateToken(claims, user);
+        return AuthenticationResponse.builder()
+                .token(jwtToken)
+                .build();
+    }
+
+    public void activateAccount(String token) throws MessagingException {
+        Token savedToken = tokenRepository.findByToken(token)
+                // TODO: Exception need to be defined
+                .orElseThrow(() -> new RuntimeException("Error: Invalid token"));
+        if (LocalDateTime.now().isAfter(savedToken.getExpiresAt())) {
+            sendValidationEmail(savedToken.getUser());
+            throw new RuntimeException("Activation token expired. A new token has been sent to your email.");
+        }
+        User user = userRepository.findById(savedToken.getUser().getId())
+                .orElseThrow(() -> new UsernameNotFoundException("Error: User not found"));
+        user.setEnabled(true);
+        userRepository.save(user);
+        savedToken.setValidatedAt(LocalDateTime.now());
+        tokenRepository.save(savedToken);
+    }
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/EmailService.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/EmailService.java
new file mode 100644
index 0000000000000000000000000000000000000000..1583147227298e50dddf7cfe0943a0acf90b2584
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/EmailService.java
@@ -0,0 +1,66 @@
+package com.hepia.volleyhub.service;
+
+
+import com.hepia.volleyhub.model.enums.EmailTemplateName;
+import jakarta.mail.MessagingException;
+import jakarta.mail.internet.MimeMessage;
+import lombok.RequiredArgsConstructor;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.thymeleaf.context.Context;
+import org.thymeleaf.spring6.SpringTemplateEngine;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.springframework.mail.javamail.MimeMessageHelper.MULTIPART_MODE_MIXED;
+
+@Service
+@RequiredArgsConstructor
+public class EmailService {
+    private final JavaMailSender mailSender;
+    private final SpringTemplateEngine templateEngine;
+
+    @Async
+    public void sendEmail(
+            String to,
+            String username,
+            EmailTemplateName template,
+            String confirmationUrl,
+            String activationCode,
+            String subject
+    ) throws MessagingException {
+        String templateName;
+        if (template == null) {
+            templateName = "default";
+        } else {
+            templateName = template.getName();
+        }
+
+        MimeMessage mimeMessage = mailSender.createMimeMessage();
+        MimeMessageHelper helper = new MimeMessageHelper(
+                mimeMessage,
+                MULTIPART_MODE_MIXED,
+                UTF_8.name()
+        );
+
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("username", username);
+        properties.put("confirmationUrl", confirmationUrl);
+        properties.put("activationCode", activationCode);
+
+        Context context = new Context();
+        context.setVariables(properties);
+
+        helper.setFrom("contact@volleyhub.com");
+        helper.setTo(to);
+        helper.setSubject(subject);
+
+        String templateHTML = templateEngine.process(templateName, context);
+        helper.setText(templateHTML, true);
+        mailSender.send(mimeMessage);
+    }
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/JwtService.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/JwtService.java
new file mode 100644
index 0000000000000000000000000000000000000000..b618cdcb0358dc1f184d2c2c631d04416fee536b
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/JwtService.java
@@ -0,0 +1,79 @@
+package com.hepia.volleyhub.service;
+
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.io.Decoders;
+import io.jsonwebtoken.security.Keys;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Service;
+
+import javax.crypto.SecretKey;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.function.Function;
+
+@Service
+public class JwtService {
+    @Value("${application.security.jwt.expiration}")
+    private long jwtExpiration;
+    @Value("${application.security.jwt.secret}")
+    private CharSequence secretKey;
+
+    public String extractUsername(String token) {
+        return extractClaim(token, Claims::getSubject);
+    }
+
+    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
+        final Claims claims = extractAllClaims(token);
+        return claimsResolver.apply(claims);
+    }
+
+    private Claims extractAllClaims(String token) {
+        return Jwts
+                .parser()
+                .verifyWith(getSignInKey())
+                .build()
+                .parseSignedClaims(token)
+                .getPayload();
+    }
+
+    public String generateToken(HashMap<String, Object> extraClaims, UserDetails userDetails) {
+        return buildToken(extraClaims, userDetails, jwtExpiration);
+    }
+
+    private String buildToken(HashMap<String, Object> extraClaims, UserDetails userDetails, long jwtExpiration) {
+        var authorities = userDetails.getAuthorities()
+                .stream()
+                .map(GrantedAuthority::getAuthority)
+                .toList();
+        return Jwts.builder()
+                .claims(extraClaims)
+                .subject(userDetails.getUsername())
+                .issuedAt(new Date(System.currentTimeMillis()))
+                .expiration(new Date(System.currentTimeMillis() + jwtExpiration))
+                .claim("authorities", authorities)
+                .signWith(getSignInKey())
+                .compact();
+    }
+
+    public boolean isTokenValid(String token, UserDetails userDetails) {
+        return extractUsername(token).equals(userDetails.getUsername()) && !isTokenExpired(token);
+    }
+
+    private boolean isTokenExpired(String token) {
+        return extractExpiration(token).before(new Date());
+    }
+
+    private Date extractExpiration(String token) {
+        return extractClaim(token, Claims::getExpiration);
+    }
+
+    private SecretKey getSignInKey() {
+        byte[] keyBytes = Decoders.BASE64.decode(secretKey);
+        return Keys.hmacShaKeyFor(keyBytes);
+    }
+}
diff --git a/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/UserDetailsServiceImpl.java b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/UserDetailsServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..2840486be18a3c4b62eb1fde103ea0812e997dfc
--- /dev/null
+++ b/VolleyHub/backend/src/main/java/com/hepia/volleyhub/service/UserDetailsServiceImpl.java
@@ -0,0 +1,23 @@
+package com.hepia.volleyhub.service;
+
+import com.hepia.volleyhub.repository.UserRepository;
+import jakarta.transaction.Transactional;
+import lombok.RequiredArgsConstructor;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class UserDetailsServiceImpl implements UserDetailsService {
+
+    private final UserRepository userRepository;
+
+    @Override
+    @Transactional
+    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
+        return userRepository.findByEmail(email)
+                .orElseThrow(() -> new UsernameNotFoundException("User Not Found"));
+    }
+}
diff --git a/VolleyHub/backend/src/main/resources/application-dev.yml b/VolleyHub/backend/src/main/resources/application-dev.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b7e8b92074c4e04e89ea0dbba14fe490dffcdd28
--- /dev/null
+++ b/VolleyHub/backend/src/main/resources/application-dev.yml
@@ -0,0 +1,41 @@
+spring:
+  datasource:
+    url: jdbc:postgresql://localhost:5432/db_volleyhub
+    username: username # TODO: Change username
+    password: password # TODO: Change password
+    driver-class-name: org.postgresql.Driver
+  jpa:
+    hibernate:
+      ddl-auto: update
+    show-sql: false
+    properties:
+      hibernate:
+        format_sql: true
+    database: postgresql
+    database-platform: org.hibernate.dialect.PostgreSQLDialect
+  mail: # Mail-dev
+    host: localhost
+    port: 1025
+    username: mail # TODO: Change username
+    password: password # TODO: Change password
+    properties:
+      mail:
+        smtp:
+          trust: "*"
+        auth: true
+        starttls: # security layer
+          enable: true
+        connectiontimeout: 5000
+        timeout: 3000
+        writetimeout: 5000
+
+application:
+  security:
+    jwt:
+      secret: 12569932e2f248ce091c43c099a36ca858c2ec703f171560ddea9afe0197ec8c
+      expiration: 86400000 # 1 day
+  mailing:
+    frontend:
+      activation-url: http://localhost:4200/activate-account
+server:
+  port: 8088
\ No newline at end of file
diff --git a/VolleyHub/backend/src/main/resources/application.properties b/VolleyHub/backend/src/main/resources/application.properties
deleted file mode 100644
index 3c11d9e9451027b65e31d2cc4af3f7bb5cf153b6..0000000000000000000000000000000000000000
--- a/VolleyHub/backend/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-spring.application.name=volleyhub
diff --git a/VolleyHub/backend/src/main/resources/application.properties:Zone.Identifier b/VolleyHub/backend/src/main/resources/application.properties:Zone.Identifier
deleted file mode 100644
index b646743bfaa9649edaf896e361737b00514e33c4..0000000000000000000000000000000000000000
--- a/VolleyHub/backend/src/main/resources/application.properties:Zone.Identifier
+++ /dev/null
@@ -1,3 +0,0 @@
-[ZoneTransfer]
-ZoneId=3
-ReferrerUrl=C:\Users\titic\Downloads\volleyhub.zip
diff --git a/VolleyHub/backend/src/main/resources/application.yml b/VolleyHub/backend/src/main/resources/application.yml
new file mode 100644
index 0000000000000000000000000000000000000000..20243438fc9b9b5d64b309542b427e32f1175436
--- /dev/null
+++ b/VolleyHub/backend/src/main/resources/application.yml
@@ -0,0 +1,12 @@
+spring:
+  profiles:
+    active: dev
+  servlet:
+    multipart:
+      max-file-size: 50MB
+springdoc:
+  default-produces-media-type: application/json
+
+server:
+  servlet:
+    context-path: /api/v1/
\ No newline at end of file
diff --git a/VolleyHub/backend/src/main/resources/templates/activate_account.html b/VolleyHub/backend/src/main/resources/templates/activate_account.html
new file mode 100644
index 0000000000000000000000000000000000000000..ffe7ba2527b3dec8ceadcb9073957d157c284e62
--- /dev/null
+++ b/VolleyHub/backend/src/main/resources/templates/activate_account.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Activate Account</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            background-color: #f4f4f4;
+            margin: 0;
+            padding: 0;
+        }
+
+        .container {
+            max-width: 600px;
+            margin: 10px auto;
+            padding: 20px;
+            background-color: #fff;
+            border-radius: 5px;
+            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+        }
+
+        .activation-code {
+            font-size: 1.5em;
+            font-weight: bold;
+            text-align: center;
+            margin-bottom: 20px;
+        }
+
+        .activation-link {
+            display: block;
+            text-align: center;
+            margin-top: 20px;
+        }
+
+        .activation-link a {
+            display: inline-block;
+            padding: 10px 20px;
+            background-color: #007bff;
+            color: #fff;
+            text-decoration: none;
+            border-radius: 5px;
+        }
+    </style>
+</head>
+<body>
+<div class="container">
+    <h1>Activate your account</h1>
+    <p class="greeting" th:text="'Hello ' + ${username} + ','"></p>
+    <p>Please use the following activation code to activate your account.</p>
+    <div class="activation-code">
+        <span th:text="${activationCode}"></span>
+    </div>
+    <div class="activation-link">
+        <a th:href="${confirmationUrl}" target="_blank">Activate my account</a>
+    </div>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/VolleyHub/backend/src/test/java/com/hepia/volleyhub/VolleyhubApplicationTests.java:Zone.Identifier b/VolleyHub/backend/src/test/java/com/hepia/volleyhub/VolleyhubApplicationTests.java:Zone.Identifier
deleted file mode 100644
index b646743bfaa9649edaf896e361737b00514e33c4..0000000000000000000000000000000000000000
--- a/VolleyHub/backend/src/test/java/com/hepia/volleyhub/VolleyhubApplicationTests.java:Zone.Identifier
+++ /dev/null
@@ -1,3 +0,0 @@
-[ZoneTransfer]
-ZoneId=3
-ReferrerUrl=C:\Users\titic\Downloads\volleyhub.zip
diff --git a/VolleyHub/docker-compose.yml b/VolleyHub/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cfcdc8d9dfad48f48517babde917acec852a808f
--- /dev/null
+++ b/VolleyHub/docker-compose.yml
@@ -0,0 +1,30 @@
+services:
+    postgres:
+        container_name: postgres-sql-vh
+        image: postgres
+        environment:
+            POSTGRES_USER: username
+            POSTGRES_PASSWORD: password
+            PGDATA: /var/lib/postgresql/data
+            POSTGRES_DB: db_volleyhub
+        volumes:
+            - postgres:/data/postgres
+        ports:
+            - 5432:5432
+        networks:
+            - network_vh
+        restart: unless-stopped
+    mail-dev:
+        container_name: mail-dev-vh
+        image: maildev/maildev
+        ports:
+            - 1080:1080 # Webapp
+            - 1025:1025 # Dev
+
+networks:
+    network_vh:
+        driver: bridge
+
+volumes:
+    postgres:
+        driver: local
\ No newline at end of file