From b340958348c0fbb8464ec4f963722bac2c8394f5 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Mon, 2 Sep 2024 14:32:27 +0200 Subject: [PATCH] Add login route --- auth/.swaggo | 1 + auth/main.go | 8 +++++-- auth/session.go | 43 ++++++++++++++++++++++++++++++++++++++ auth/sql/queries/users.sql | 13 ++++++++++-- auth/users.go | 9 ++++---- 5 files changed, 66 insertions(+), 8 deletions(-) diff --git a/auth/.swaggo b/auth/.swaggo index df6d17f2..3372ddb7 100644 --- a/auth/.swaggo +++ b/auth/.swaggo @@ -1 +1,2 @@ replace jwt.MapClaims map[string]string +replace uuid.UUID string diff --git a/auth/main.go b/auth/main.go index 8f08f13d..e21ba402 100644 --- a/auth/main.go +++ b/auth/main.go @@ -83,7 +83,6 @@ func OpenDatabase() (*pgxpool.Pool, error) { fmt.Printf("Could not connect to database, check your env variables!") return nil, err } - defer db.Close() if schema != "disabled" { _, err = db.Exec(ctx, fmt.Sprintf("create schema if not exists %s", schema)) @@ -92,6 +91,7 @@ func OpenDatabase() (*pgxpool.Pool, error) { } } + fmt.Println("Migrating database") dbi := stdlib.OpenDBFromPool(db) defer dbi.Close() @@ -104,8 +104,9 @@ func OpenDatabase() (*pgxpool.Pool, error) { return nil, err } m.Up() + fmt.Println("Migrating finished") - return db, err + return db, nil } type Handler struct { @@ -149,6 +150,9 @@ func main() { e.GET("/users", h.ListUsers) e.POST("/users", h.Register) + + e.POST("/session", h.Login) + e.GET("/swagger/*", echoSwagger.WrapHandler) e.Logger.Fatal(e.Start(":4568")) diff --git a/auth/session.go b/auth/session.go index a0d539bd..ae064fe8 100644 --- a/auth/session.go +++ b/auth/session.go @@ -9,6 +9,7 @@ import ( "net/http" "time" + "github.com/alexedwards/argon2id" "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo/v4" "github.com/zoriya/kyoo/keibi/dbc" @@ -19,6 +20,48 @@ type LoginDto struct { Password string `json:"password" validate:"required"` } +// @Summary Login +// @Description Login to your account and open a session +// @Tags sessions +// @Accept json +// @Produce json +// @Param device query uuid false "The device the created session will be used on" +// @Param user body LoginDto false "Account informations" +// @Success 201 {object} dbc.Session +// @Failure 400 {object} problem.Problem "Invalid login body" +// @Failure 400 {object} problem.Problem "Invalid password" +// @Failure 404 {object} problem.Problem "Account does not exists" +// @Router /sessions [post] +func (h *Handler) Login(c echo.Context) error { + var req LoginDto + err := c.Bind(&req) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + if err = c.Validate(&req); err != nil { + return err + } + + dbuser, err := h.db.GetUserByLogin(context.Background(), req.Login) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "No account exists with the specified email or username.") + } + if dbuser.Password == nil { + return echo.NewHTTPError(http.StatusBadRequest, "Can't login with password, this account was created with OIDC.") + } + + match, err := argon2id.ComparePasswordAndHash(req.Password, *dbuser.Password) + if err != nil { + return err + } + if !match { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid password") + } + + user := MapDbUser(&dbuser) + return h.createSession(c, &user) +} + func (h *Handler) createSession(c echo.Context, user *User) error { ctx := context.Background() diff --git a/auth/sql/queries/users.sql b/auth/sql/queries/users.sql index 0d25d81b..ffdd37ef 100644 --- a/auth/sql/queries/users.sql +++ b/auth/sql/queries/users.sql @@ -18,7 +18,7 @@ order by id limit $1; --- name: GetUser :one +-- name: GetUser :many select sqlc.embed(u), sqlc.embed(h) @@ -26,7 +26,16 @@ from users as u left join oidc_handle as h on u.id = h.user_id where - u.id = $1 + u.id = $1; + +-- name: GetUserByLogin :one +select + * +from + users +where + email = sqlc.arg(login) + or username = sqlc.arg(login) limit 1; -- name: CreateUser :one diff --git a/auth/users.go b/auth/users.go index 75a07705..6035372f 100644 --- a/auth/users.go +++ b/auth/users.go @@ -29,7 +29,7 @@ type OidcHandle struct { } type RegisterDto struct { - Username string `json:"username" validate:"required"` + Username string `json:"username" validate:"required,excludes=@"` Email string `json:"email" validate:"required,email" format:"email"` Password string `json:"password" validate:"required"` } @@ -51,7 +51,7 @@ func MapDbUser(user *dbc.User) User { // @Tags users // @Accept json // @Produce json -// @Param afterId query uuid false "used for pagination." +// @Param afterId query string false "used for pagination." Format(uuid) // @Success 200 {object} User[] // @Failure 400 {object} problem.Problem // @Router /users [get] @@ -95,7 +95,8 @@ func (h *Handler) ListUsers(c echo.Context) error { // @Param device query uuid false "The device the created session will be used on" // @Param user body RegisterDto false "Registration informations" // @Success 201 {object} dbc.Session -// @Failure 400 {object} problem.Problem +// @Failure 400 {object} problem.Problem "Invalid register body" +// @Success 409 {object} problem.Problem "Duplicated email or username" // @Router /users [post] func (h *Handler) Register(c echo.Context) error { var req RegisterDto @@ -109,7 +110,7 @@ func (h *Handler) Register(c echo.Context) error { pass, err := argon2id.CreateHash(req.Password, argon2id.DefaultParams) if err != nil { - return echo.NewHTTPError(400, "Invalid password") + return err } duser, err := h.db.CreateUser(context.Background(), dbc.CreateUserParams{