refactor: migration auth api to v1

This commit is contained in:
Steven
2023-06-20 16:33:36 +08:00
parent 20884e9370
commit 6004a2f657
13 changed files with 428 additions and 251 deletions

110
api/v1/auth.go Normal file
View File

@ -0,0 +1,110 @@
package v1
import (
"encoding/json"
"fmt"
"net/http"
"github.com/boojack/shortify/api"
"github.com/boojack/shortify/server/auth"
"github.com/boojack/shortify/store"
"github.com/labstack/echo/v4"
"golang.org/x/crypto/bcrypt"
)
var (
userIDContextKey = "user-id"
)
func getUserIDContextKey() string {
return userIDContextKey
}
type SignUpRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type SignInRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) {
g.POST("/auth/signin", func(c echo.Context) error {
ctx := c.Request().Context()
signin := &SignInRequest{}
if err := json.NewDecoder(c.Request().Body).Decode(signin); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signin request").SetInternal(err)
}
user, err := s.Store.GetUserV1(ctx, &store.FindUser{
Username: &signin.Username,
})
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find user by username %s", signin.Username)).SetInternal(err)
}
if user == nil {
return echo.NewHTTPError(http.StatusUnauthorized, fmt.Sprintf("User not found with username %s", signin.Username))
} else if user.RowStatus == store.Archived {
return echo.NewHTTPError(http.StatusForbidden, fmt.Sprintf("User has been archived with username %s", signin.Username))
}
// Compare the stored hashed password, with the hashed version of the password that was received.
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(signin.Password)); err != nil {
// If the two passwords don't match, return a 401 status.
return echo.NewHTTPError(http.StatusUnauthorized, "Incorrect password").SetInternal(err)
}
if err := auth.GenerateTokensAndSetCookies(c, user, secret); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate tokens").SetInternal(err)
}
return c.JSON(http.StatusOK, user)
})
g.POST("/auth/signup", func(c echo.Context) error {
ctx := c.Request().Context()
signup := &SignUpRequest{}
if err := json.NewDecoder(c.Request().Body).Decode(signup); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted signup request").SetInternal(err)
}
user := &store.User{
Username: signup.Username,
Nickname: signup.Username,
}
passwordHash, err := bcrypt.GenerateFromPassword([]byte(signup.Password), bcrypt.DefaultCost)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate password hash").SetInternal(err)
}
user.PasswordHash = string(passwordHash)
existingUsers, err := s.Store.FindUserList(ctx, &api.UserFind{})
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find existing users").SetInternal(err)
}
// The first user to sign up is an admin by default.
if len(existingUsers) == 0 {
user.Role = store.RoleAdmin
} else {
user.Role = store.RoleUser
}
user, err = s.Store.CreateUserV1(ctx, user)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create user").SetInternal(err)
}
if err := auth.GenerateTokensAndSetCookies(c, user, secret); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to generate tokens").SetInternal(err)
}
return c.JSON(http.StatusOK, user)
})
g.POST("/auth/logout", func(c echo.Context) error {
auth.RemoveTokensAndCookies(c)
c.Response().WriteHeader(http.StatusOK)
return nil
})
}