Sfoglia il codice sorgente

Started work on Me endpoint and JWT.

Aaro Saila 2 settimane fa
parent
commit
b92601ae95

+ 82 - 7
src/auth-server/controllers/user.go

@@ -2,19 +2,36 @@ package controllers
 
 import (
 	"context"
-	"fmt"
 	"net/http"
 	"pong-auth/db"
 	"pong-auth/models"
+	"strings"
 
 	"github.com/gin-gonic/gin"
+	"github.com/golang-jwt/jwt/v5"
+	"golang.org/x/crypto/bcrypt"
 )
 
+var jwtSecret []byte = []byte("secret")
+
 func UserSignup(c *gin.Context) {
 	dto := models.UserSignupDto{}
 	err := c.ShouldBindJSON(&dto)
 	if err != nil {
-		fmt.Println("bind")
+		c.JSON(http.StatusInternalServerError, err.Error())
+		return
+	}
+
+	dto.Username = strings.TrimSpace(dto.Username)
+	if dto.Username == "" {
+		c.JSON(http.StatusBadRequest, gin.H{
+			"err": "Username was empty",
+		})
+		return
+	}
+
+	hashed, err := bcrypt.GenerateFromPassword([]byte(dto.Password), bcrypt.MinCost)
+	if err != nil {
 		c.JSON(http.StatusInternalServerError, err.Error())
 		return
 	}
@@ -27,15 +44,14 @@ func UserSignup(c *gin.Context) {
 	defer conn.Close(context.Background())
 	rows := conn.QueryRow(
 		context.Background(),
-		"INSERT INTO users (email, password) VALUES ($1, $2) RETURNING *",
-		dto.Email,
-		dto.Password,
+		"INSERT INTO users (username, password) VALUES ($1, $2) RETURNING *",
+		dto.Username,
+		string(hashed),
 	)
 
 	user := models.User{}
-	err = rows.Scan(&user.Id, &user.Email, &user.Password)
+	err = rows.Scan(&user.Id, &user.Username, &user.Password)
 	if err != nil {
-		fmt.Printf("scan: %+v\n", err)
 		c.JSON(http.StatusInternalServerError, gin.H{
 			"err": err.Error(),
 		})
@@ -46,5 +62,64 @@ func UserSignup(c *gin.Context) {
 }
 
 func UserLogin(c *gin.Context) {
+	dto := models.UserSignupDto{}
+	err := c.ShouldBindJSON(&dto)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, err.Error())
+		return
+	}
+
+	conn, err := db.Connect()
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, err.Error())
+		return
+	}
+	defer conn.Close(context.Background())
+
+	row := conn.QueryRow(
+		context.Background(),
+		"SELECT * FROM users WHERE username = $1",
+		dto.Username,
+	)
+
+	user := models.User{}
+	err = row.Scan(&user.Id, &user.Username, &user.Password)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, err.Error())
+		return
+	}
+
+	err = bcrypt.CompareHashAndPassword(
+		[]byte(user.Password),
+		[]byte(dto.Password),
+	)
+	if err != nil {
+		c.JSON(http.StatusUnauthorized, gin.H{
+			"err": "Wrong password",
+		})
+		return
+	}
+
+	claims := jwt.RegisteredClaims{
+		Issuer:  "pong-auth-server",
+		Subject: user.Id,
+	}
+	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+	tokenStr, err := token.SignedString(jwtSecret)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, err.Error())
+		return
+	}
+
+	c.JSON(http.StatusOK, models.UserLoginOutDto{
+		Me: models.UserMeDto{
+			Id:       user.Id,
+			Username: user.Username,
+		},
+		Jwt: tokenStr,
+	})
+}
+
+func UserMe(c *gin.Context) {
 
 }

+ 5 - 4
src/auth-server/go.mod

@@ -15,6 +15,7 @@ require (
 	github.com/go-playground/validator/v10 v10.30.1 // indirect
 	github.com/goccy/go-json v0.10.5 // indirect
 	github.com/goccy/go-yaml v1.19.2 // indirect
+	github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
 	github.com/jackc/pgpassfile v1.0.0 // indirect
 	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
 	github.com/jackc/pgx/v5 v5.11.0 // indirect
@@ -33,9 +34,9 @@ require (
 	github.com/ugorji/go/codec v1.3.1 // indirect
 	go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
 	golang.org/x/arch v0.22.0 // indirect
-	golang.org/x/crypto v0.48.0 // indirect
-	golang.org/x/net v0.51.0 // indirect
-	golang.org/x/sys v0.41.0 // indirect
-	golang.org/x/text v0.34.0 // indirect
+	golang.org/x/crypto v0.58.0 // indirect
+	golang.org/x/net v0.60.0 // indirect
+	golang.org/x/sys v0.49.0 // indirect
+	golang.org/x/text v0.43.0 // indirect
 	google.golang.org/protobuf v1.36.10 // indirect
 )

+ 10 - 0
src/auth-server/go.sum

@@ -24,6 +24,8 @@ github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
 github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
 github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
+github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
+github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
 github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
@@ -75,13 +77,21 @@ golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
 golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
 golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
 golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
+golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54=
+golang.org/x/crypto v0.58.0/go.mod h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w=
 golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
 golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
+golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU=
+golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
 golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA=
+golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
 golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
 golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
+golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
+golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
 google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
 google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

+ 25 - 0
src/auth-server/middleware/auth.go

@@ -0,0 +1,25 @@
+package middleware
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/gin-gonic/gin"
+	"github.com/golang-jwt/jwt/v5"
+)
+
+func Auth() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		authHeader := strings.TrimSpace(c.GetHeader("Authorization"))
+		authHeaderSplit := strings.Split(authHeader, "Bearer")
+		if len(authHeaderSplit) != 2 {
+			c.JSON(http.StatusBadRequest, gin.H{
+				"err": "Invalid Authorization header",
+			})
+			c.Abort()
+			return
+		}
+
+		token := authHeaderSplit[1]
+	}
+}

+ 12 - 2
src/auth-server/models/models.go

@@ -2,15 +2,25 @@ package models
 
 type User struct {
 	Id       string
-	Email    string
+	Username string
 	Password string
 }
 
 type UserSignupDto struct {
-	Email    string `json:"email"`
+	Username string `json:"username"`
 	Password string `json:"password"`
 }
 
+type UserMeDto struct {
+	Id       string `json:"id"`
+	Username string `json:"username"`
+}
+
+type UserLoginOutDto struct {
+	Me  UserMeDto `json:"me"`
+	Jwt string    `json:"jwt"`
+}
+
 type Match struct {
 	Id           string
 	Player1Id    string

+ 2 - 2
src/auth-server/res/index.html

@@ -6,8 +6,8 @@
     </head>
     <body>
         <h1>Signup</h1>
-        <label>Email</label>
-        <input id="input-signup-email" type=email></input>
+        <label>Username</label>
+        <input id="input-signup-username" type=text></input>
         <label>Password</label>
         <input id="input-signup-password" type=password></input>
         <button id="button-signup" type=button>Send</button>

+ 6 - 4
src/auth-server/res/index.js

@@ -1,11 +1,13 @@
-const inputEmail = document.getElementById("input-signup-email");
+const inputUsername = document.getElementById("input-signup-username");
 const inputPassword = document.getElementById("input-signup-password");
 const buttonSignup = document.getElementById("button-signup");
 const resultTxt = document.getElementById("result-text");
 
 buttonSignup.onclick = async () => {
-    const email = inputEmail.textContent;
-    const password = inputPassword.textContent;
+    const username = inputUsername.value;
+    const password = inputPassword.value;
+    // console.log(username);
+    // console.log(password);
 
     const res = await fetch("http://127.0.0.1:8080/api/user/signup", {
         method: "POST",
@@ -13,7 +15,7 @@ buttonSignup.onclick = async () => {
             "Content-Type": "application/json"
         },
         body: JSON.stringify({
-            "email": email,
+            "username": username,
             "password": password
         })
     });

+ 1 - 0
src/auth-server/routes.go

@@ -11,6 +11,7 @@ func UserRoutes(r *gin.RouterGroup) {
 	g := r.Group("user")
 
 	g.POST("/signup", controllers.UserSignup)
+	g.POST("/login", controllers.UserLogin)
 }
 
 func RoutesSet(r *gin.Engine) {

+ 1 - 1
src/auth-server/sql/schema.sql

@@ -1,6 +1,6 @@
 CREATE TABLE IF NOT EXISTS users (
     id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
-    email text UNIQUE NOT NULL,
+    username text UNIQUE NOT NULL,
     password text NOT NULL
 );