Skip to content

Commit 5cd96ae

Browse files
authored
Merge pull request #24 from CS3219-AY2425S1/merge/merge_account_creation_to_main
Authentication Feature
2 parents b99eb54 + 7754302 commit 5cd96ae

File tree

18 files changed

+735
-121
lines changed

18 files changed

+735
-121
lines changed

authentication-service/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.env.dev

authentication-service/Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Use the official Golang image as the base image
2+
FROM golang:1.23-alpine
3+
4+
# Set the working directory inside the container
5+
WORKDIR /app
6+
7+
# Copy the Go module files
8+
COPY go.mod go.sum ./
9+
10+
# Download the Go module dependencies
11+
RUN go mod download
12+
13+
# Copy the source code
14+
COPY . .
15+
16+
# Build the Go application
17+
RUN go build -o main ./main.go
18+
19+
# Set the entry point for the container
20+
ENTRYPOINT ["./main"]

authentication-service/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Account Creation Service
2+
3+
## Setup
4+
5+
Run the following command to install the dependencies
6+
7+
```bash
8+
docker build -t account-creation-service . --no-cache
9+
docker run -p 4040:4040 --env-file .env acc account-creation-service
10+
```
11+
12+
## Example Usage
13+
14+
1. Make sure your json struct has a similar structure:
15+
``` {
16+
"email": "[email protected]",
17+
"name": "exampleUser",
18+
"password": "securePassword123",
19+
"type": "User"
20+
}```
21+
22+
2. Make a POST request to http://localhost:4040/register
23+
24+
## Template code for Javascript
25+
26+
```const myHeaders = new Headers();
27+
myHeaders.append("Content-Type", "application/json");
28+
29+
const raw = JSON.stringify({
30+
"email": "[email protected]",
31+
"name": "exampleUser",
32+
"password": "securePassword123",
33+
"type": "User"
34+
});
35+
36+
const requestOptions = {
37+
method: "POST",
38+
headers: myHeaders,
39+
body: raw,
40+
redirect: "follow"
41+
};
42+
43+
fetch("http://localhost:4040/register", requestOptions)
44+
.then((response) => response.text())
45+
.then((result) => console.log(result))
46+
.catch((error) => console.error(error));```
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package controllers
2+
3+
import (
4+
"net/http"
5+
6+
"authentication-service/models"
7+
"authentication-service/services"
8+
"authentication-service/utils"
9+
"github.com/golang-jwt/jwt"
10+
"github.com/gin-gonic/gin"
11+
)
12+
13+
// Handles JWT token requests
14+
func TokenLogin(c *gin.Context) {
15+
// Get the token from the request query string
16+
var tokenString = c.Query("token")
17+
if tokenString == "" {
18+
c.JSON(http.StatusBadRequest, gin.H{"error": "Token not provided"})
19+
return
20+
}
21+
22+
// Verify the token using the utility function
23+
token, err := utils.VerifyToken(tokenString)
24+
if err != nil {
25+
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
26+
return
27+
}
28+
29+
// Extract claims from the token (e.g., the user's email)
30+
claims, ok := token.Claims.(jwt.MapClaims)
31+
if !ok || !token.Valid {
32+
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims"})
33+
return
34+
}
35+
36+
// Retrieve user details based on the email claim
37+
email, ok := claims["email"].(string)
38+
if !ok {
39+
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims"})
40+
return
41+
}
42+
43+
var user models.UserModel
44+
err = services.GetUserByEmail(c.Request.Context(), email, &user)
45+
if err != nil {
46+
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
47+
return
48+
}
49+
50+
// Return the user's account details
51+
c.JSON(http.StatusOK, gin.H{
52+
"email": user.Email,
53+
"name": user.Name,
54+
})
55+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package controllers
2+
3+
import (
4+
"context"
5+
"net/http"
6+
7+
"authentication-service/models"
8+
"authentication-service/services"
9+
"authentication-service/utils"
10+
"github.com/gin-gonic/gin"
11+
"golang.org/x/crypto/bcrypt"
12+
)
13+
14+
// LoginUser handles user login requests
15+
func LoginUser(c *gin.Context) {
16+
var loginRequest models.LoginRequest
17+
if err := c.ShouldBindJSON(&loginRequest); err != nil {
18+
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request data"})
19+
return
20+
}
21+
22+
// Retrieve user from the database
23+
var user models.UserModel
24+
err := services.GetUserByEmail(context.TODO(), loginRequest.Email, &user)
25+
if err != nil {
26+
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
27+
return
28+
}
29+
30+
// Compare the password with the stored password
31+
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(loginRequest.Password))
32+
if err != nil {
33+
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
34+
return
35+
}
36+
37+
// Successful login, generate JWT token
38+
token, err := utils.GenerateToken(user)
39+
if err != nil {
40+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"})
41+
return
42+
}
43+
44+
// Successful login
45+
c.JSON(http.StatusOK, gin.H{"message": "Login successful", "token": token})
46+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package controllers
2+
3+
import (
4+
"context"
5+
"log"
6+
"net/http"
7+
"time"
8+
9+
"authentication-service/models"
10+
"authentication-service/services"
11+
"authentication-service/utils"
12+
13+
"github.com/gin-gonic/gin"
14+
"go.mongodb.org/mongo-driver/bson"
15+
)
16+
17+
func RegisterUser(c *gin.Context) {
18+
var user models.RegistrationRequest
19+
20+
if err := c.ShouldBindJSON(&user); err != nil {
21+
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request data"})
22+
return
23+
}
24+
25+
if user.Email == "" || user.Name == "" || user.Password == "" {
26+
log.Printf("Error inserting user into database: %v", user.Type)
27+
c.JSON(http.StatusBadRequest, gin.H{"error": "Email, username, and password are required"})
28+
return
29+
}
30+
31+
var existingUser models.RegistrationRequest
32+
userCollection := services.GetUserCollection()
33+
err := userCollection.FindOne(context.TODO(), bson.M{"email": user.Email}).Decode(&existingUser)
34+
if err == nil {
35+
c.JSON(http.StatusBadRequest, gin.H{"error": "Email already exists"})
36+
return
37+
}
38+
39+
hashedPassword, err := utils.HashPassword(user.Password) // Use the new hash function from utils
40+
if err != nil {
41+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error hashing password"})
42+
return
43+
}
44+
45+
user.Password = hashedPassword
46+
user.CreatedAt = time.Now()
47+
user.Type = "User"
48+
49+
_, err = userCollection.InsertOne(context.TODO(), user)
50+
if err != nil {
51+
log.Printf("Error inserting user into database: %v", err)
52+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error inserting user into database"})
53+
return
54+
}
55+
56+
c.JSON(http.StatusCreated, gin.H{"message": "User registered successfully"})
57+
}

authentication-service/go.mod

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module authentication-service
2+
3+
go 1.23.1
4+
5+
require (
6+
github.com/gin-gonic/gin v1.10.0
7+
github.com/joho/godotenv v1.5.1
8+
go.mongodb.org/mongo-driver v1.17.0
9+
golang.org/x/crypto v0.27.0
10+
)
11+
12+
require (
13+
github.com/bytedance/sonic v1.11.6 // indirect
14+
github.com/bytedance/sonic/loader v0.1.1 // indirect
15+
github.com/cloudwego/base64x v0.1.4 // indirect
16+
github.com/cloudwego/iasm v0.2.0 // indirect
17+
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
18+
github.com/gin-contrib/sse v0.1.0 // indirect
19+
github.com/go-playground/locales v0.14.1 // indirect
20+
github.com/go-playground/universal-translator v0.18.1 // indirect
21+
github.com/go-playground/validator/v10 v10.20.0 // indirect
22+
github.com/goccy/go-json v0.10.2 // indirect
23+
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
24+
github.com/golang/snappy v0.0.4 // indirect
25+
github.com/json-iterator/go v1.1.12 // indirect
26+
github.com/klauspost/compress v1.13.6 // indirect
27+
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
28+
github.com/leodido/go-urn v1.4.0 // indirect
29+
github.com/mattn/go-isatty v0.0.20 // indirect
30+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
31+
github.com/modern-go/reflect2 v1.0.2 // indirect
32+
github.com/montanaflynn/stats v0.7.1 // indirect
33+
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
34+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
35+
github.com/ugorji/go/codec v1.2.12 // indirect
36+
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
37+
github.com/xdg-go/scram v1.1.2 // indirect
38+
github.com/xdg-go/stringprep v1.0.4 // indirect
39+
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
40+
golang.org/x/arch v0.8.0 // indirect
41+
golang.org/x/net v0.25.0 // indirect
42+
golang.org/x/sync v0.8.0 // indirect
43+
golang.org/x/sys v0.25.0 // indirect
44+
golang.org/x/text v0.18.0 // indirect
45+
google.golang.org/protobuf v1.34.1 // indirect
46+
gopkg.in/yaml.v3 v3.0.1 // indirect
47+
)

0 commit comments

Comments
 (0)