Skip to content

Commit

Permalink
feat: first version
Browse files Browse the repository at this point in the history
  • Loading branch information
DevRuibin committed Jan 2, 2024
1 parent d8d259b commit 17fc359
Show file tree
Hide file tree
Showing 23 changed files with 595 additions and 80 deletions.
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Authentication Service Specification

## Overview

The Authentication Service is a critical component of the system, responsible for managing user registration, login, and token management. It provides a secure way for users to access the system by validating their credentials and issuing JWT tokens for authenticated sessions.

## Features

### User Registration

Endpoint: `POST /api/v1/auth/register`

This feature allows new users to register for an account. The user provides their details (such as username, password, email, etc.) and the service creates a new user account in the system. The password is hashed using bcrypt for secure storage.

### User Login

Endpoint: `POST /api/v1/auth/authenticate`

This feature allows users to log in to their account. The user provides their username and password, and the service validates these credentials. If the credentials are valid, the service generates a JWT token for the user's session and returns it in the response.

### Reset Password (To be implemented)

This feature will allow users to reset their password if they forget it. The user will provide their email, and the service will send a password reset link to that email. The user can then use this link to set a new password for their account.

### Refresh Token (To be implemented)

This feature will allow users to refresh their session token. The user will provide their current token, and the service will validate this token and issue a new one. This allows the user to maintain their session without needing to log in again.

## Security

The service uses JWT for session management. When a user logs in, they are issued a JWT token. This token is then used to authenticate the user's requests. The service validates the token with each request to ensure that it is valid and has not expired.

Passwords are hashed using bcrypt before they are stored. This ensures that even if the user data is compromised, the passwords cannot be easily recovered.

## API Documentation

The API documentation for the service is available at `http://localhost:8080/swagger-ui/index.html`. This provides detailed information about the API endpoints, including the request and response formats.

## Next version

The next version of the service will include the following features:

- Reset Password
- Refresh Token
- Login by Email and dynamic code
- Improve security by register and reset password by email

6 changes: 5 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
compileOnly 'org.projectlombok:lombok'
implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.2'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.amqp:spring-rabbit-test'
Expand Down
46 changes: 11 additions & 35 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,18 @@ services:
POSTGRES_PASSWORD: password
PGDATA: /data/postgres

# couchbase-db:
# container_name: db
# image: couchbase:community-7.2.2
# ports:
# - "8091-8097:8091-8097"
# - "9123:9123"
# - "11207:11207"
# - "11210:11210"
# - "11280:11280"
# - "18091-18097:18091-18097"
# volumes:
# - chatfy-data:/opt/couchbase/var
redis:
image: redis:latest
container_name: redis
ports:
- "6379:6379"


# contact-db-neo4j:
# container_name: contact-db-neo4j
# image: neo4j:bullseye
# ports:
# - "7474:7474"
# - "7687:7687"
# volumes:
# - chatfy-contact-db:/data


# redis:
# image: redis:latest
# container_name: redis
# ports:
# - "6379:6379"
# volumes:
# - chatfy-redis-data:/data

rabbitmq:
image: rabbitmq:3-management-alpine
container_name: "rabbitmq"
ports:
- "5672:5672"
- "15672:15672"


# volumes:
# chatfy-data:
# chatfy-auth-db:
# chatfy-contact-db:
# chatfy-redis-data:
2 changes: 0 additions & 2 deletions src/main/java/org/andy/chatfybackend/api/OpenApiConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package org.andy.chatfybackend.api;



import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,32 @@
package org.andy.chatfybackend.auth.auth;public class AuthenticationController {
package org.andy.chatfybackend.auth.auth;


import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
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;

@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor

public class AuthenticationController {
private final AuthenticationService authenticationService;
@PostMapping("/register")
// @PermitAll
public ResponseEntity<AuthenticationResponse> register(
@RequestBody RegisterRequest request
) {
return ResponseEntity.ok(authenticationService.register(request));
}

@PostMapping("/authenticate")
// @PermitAll
public ResponseEntity<AuthenticationResponse> register(
@RequestBody AuthenticationRequest request
) {
return ResponseEntity.ok(authenticationService.authenticate(request));
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,15 @@
package org.andy.chatfybackend.auth.auth;public class AuthenticationRequest {
package org.andy.chatfybackend.auth.auth;

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

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class AuthenticationRequest {
private String email;
private String password;
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
package org.andy.chatfybackend.auth.auth;public class AuthenticationResponse {
package org.andy.chatfybackend.auth.auth;

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

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class AuthenticationResponse {
private String jwt;
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,53 @@
package org.andy.chatfybackend.auth.auth;public class AuthenticationService {
package org.andy.chatfybackend.auth.auth;

import lombok.RequiredArgsConstructor;
import org.andy.chatfybackend.auth.basic_user.BasicUser;
import org.andy.chatfybackend.auth.basic_user.BasicUserRepository;
import org.andy.chatfybackend.auth.config.JwtService;
import org.andy.chatfybackend.auth.exceptions.DuplicateUserException;
import org.andy.chatfybackend.auth.exceptions.IncorrectPasswordException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class AuthenticationService {
private final BasicUserRepository basicUserRepository;
private final PasswordEncoder passwordEncoder;
private final JwtService jwtService;
private final AuthenticationManager authenticationManager;

public AuthenticationResponse register(RegisterRequest request) {
if(basicUserRepository.existsByEmail(request.getEmail())) {
throw new DuplicateUserException("Email already exists");
}
var user = BasicUser.builder()
.email(request.getEmail())
.password(passwordEncoder.encode(request.getPassword()))
.build();
basicUserRepository.save(user);
var jwt = jwtService.generateToken(user);
return AuthenticationResponse.builder()
.jwt(jwt)
.build();
}

public AuthenticationResponse authenticate(AuthenticationRequest request) {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
request.getEmail(),
request.getPassword()
));
} catch (Exception e) {
throw new IncorrectPasswordException("Incorrect password");
}
var user = basicUserRepository.findByEmail(request.getEmail())
.orElseThrow();
var jwt = jwtService.generateToken(user);
return AuthenticationResponse.builder()
.jwt(jwt)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,15 @@
package org.andy.chatfybackend.auth.auth;public class RegisterRequest {
package org.andy.chatfybackend.auth.auth;

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

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class RegisterRequest {
private String email;
private String password;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.andy.chatfybackend.auth;
package org.andy.chatfybackend.auth.basic_user;



Expand Down Expand Up @@ -28,11 +28,15 @@ public class BasicUser implements UserDetails {
private String email;
private String password;
@Enumerated(EnumType.STRING)
private Role role;

@Builder.Default
private Role role = Role.USER;
@Builder.Default
private boolean accountNonExpired=true;
@Builder.Default
private boolean accountNonLocked=true;
@Builder.Default
private boolean credentialsNonExpired=true;
@Builder.Default
private boolean enabled=true;


Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package org.andy.chatfybackend.auth;
package org.andy.chatfybackend.auth.basic_user;

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface BasicUserRepository extends JpaRepository<BasicUser, Long> {
Optional<BasicUser> findByEmail(String email);
boolean existsByEmail(String email);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.andy.chatfybackend.auth;
package org.andy.chatfybackend.auth.basic_user;

public enum Role {
ADMIN,
Expand Down
45 changes: 44 additions & 1 deletion src/main/java/org/andy/chatfybackend/auth/config/AuthConfig.java
Original file line number Diff line number Diff line change
@@ -1,2 +1,45 @@
package org.andy.chatfybackend.auth.config;public class AuthConfig {
package org.andy.chatfybackend.auth.config;

import lombok.RequiredArgsConstructor;
import org.andy.chatfybackend.auth.basic_user.BasicUserRepository;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@RequiredArgsConstructor
public class AuthConfig {
private final BasicUserRepository repository;
@Bean
public UserDetailsService userDetailsService() {
return username ->
repository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found")
);
}

@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService());
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
18 changes: 16 additions & 2 deletions src/main/java/org/andy/chatfybackend/auth/config/ErrorDetails.java
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
package org.andy.chatfybackend.auth.config;public class ErrorDetails {
}
package org.andy.chatfybackend.auth.config;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

@Builder
@AllArgsConstructor
@ToString
@Getter
public class ErrorDetails {
private String message;
@Builder.Default
private String details = "";
}
Loading

0 comments on commit 17fc359

Please sign in to comment.