diff --git a/internal/router/api/v1/account/login.go b/internal/router/api/v1/account/login.go new file mode 100644 index 0000000..0ef0f66 --- /dev/null +++ b/internal/router/api/v1/account/login.go @@ -0,0 +1,73 @@ +// Copyright 2024 perp (supernets) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package account + +import ( + "git.supernets.org/perp/gopay/internal/context" + "git.supernets.org/perp/gopay/internal/jwt" + v1 "git.supernets.org/perp/gopay/internal/models/v1" + "golang.org/x/crypto/bcrypt" +) + +// @summary Account login +// @description Login to an account +// @tags account +// @accept json +// @produce json +// @param register body v1.Register true "alice" "supersecretpassword" +// @success 200 {object} models.Token +// @failure 400 {object} models.Error "MissingBody | InvalidUsername | InvalidPassword" +// @failure 500 {object} models.Error "InternalServerError" +// @router /v1/account/login [post] +func Login(ctx *context.Context) { + // Store body + var body *v1.Register + + // Bind JSON + err := ctx.BindJSON(&body) + if err != nil { + ctx.JSON(400, ctx.Error("MissingBody")) + return + } + + // Select account by username + account, err := ctx.Db.Account.SelectByUsername(body.Username) + if err != nil { + ctx.JSON(500, ctx.Error("InternalServerError")) + return + } + + // Compare username + if account.Username != body.Username { + ctx.JSON(400, ctx.Error("InvalidUsername")) + return + } + + // Compare password + err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(body.Password)) + if err != nil { + ctx.JSON(400, ctx.Error("InvalidPassword")) + return + } + + // Generate token + token, err := jwt.Encode(account.ID) + if err != nil { + ctx.JSON(500, ctx.Error("InternalServerError")) + return + } + + ctx.JSON(200, ctx.Token(token)) +} diff --git a/internal/router/api/v1/v1.go b/internal/router/api/v1/v1.go index b2825fb..cc922c0 100644 --- a/internal/router/api/v1/v1.go +++ b/internal/router/api/v1/v1.go @@ -40,6 +40,7 @@ func Register(ctx *context.Context) { { a := v1.Group("account") + a.POST("login", ctx.API(account.Login)) a.POST("register", ctx.API(account.Register)) } }