diff options
Diffstat (limited to 'auth/:w')
-rw-r--r-- | auth/:w | 182 |
1 files changed, 182 insertions, 0 deletions
@@ -0,0 +1,182 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package auth + +import ( + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" + "github.com/spf13/viper" + "golang.org/x/crypto/bcrypt" + "vidhukant.com/openbills/user" + "net/http" + "time" + "vidhukant.com/openbills/errors" +) + +var ( + COST int + AUTH_KEY, REFRESH_KEY []byte +) +func init() { + COST = viper.GetInt("cryptography.password_hashing_cost") + AUTH_KEY = []byte(viper.GetString("cryptography.auth_key")) + REFRESH_KEY = []byte(viper.GetString("cryptography.refresh_key")) +} + +func handleSignUp (ctx *gin.Context) { + var user user.User + ctx.Bind(&user) + + var err error + + // hash password + var bytes []byte + bytes, err = bcrypt.GenerateFromPassword([]byte(user.Password), 14) + if err != nil { + // TODO: handle potential errors + ctx.Error(err) + ctx.Abort() + return + } + user.Password = string(bytes) + + err = user.Create() + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + // remove password hash from response + user.Password = "" + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": user, + }) +} + +func handleSignIn (ctx *gin.Context) { + var req LoginReq + ctx.Bind(&req) + + var err error + var u user.User + + err = user.CheckPassword(&u, req.AccountName, req.Method, req.Password) + if err != nil { + // TODO: handle potential errors + ctx.Error(err) + ctx.Abort() + return + } + + authToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, + AuthClaims { + jwt.RegisteredClaims { + IssuedAt: jwt.NewNumericDate(time.Now()), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 2)), + }, + u.ID, + }, + ).SignedString(AUTH_KEY) + if err != nil { + // TODO: handle potential errors + ctx.Error(err) + ctx.Abort() + return + } + + refreshToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, + AuthClaims { + jwt.RegisteredClaims { + IssuedAt: jwt.NewNumericDate(time.Now()), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 6)), + }, + u.ID, + }, + ).SignedString(REFRESH_KEY) + if err != nil { + // TODO: handle potential errors + ctx.Error(err) + ctx.Abort() + return + } + + // remove password hash from response + u.Password = "" + + ctx.JSON(http.StatusOK, gin.H{ + "auth_token": authToken, + "refresh_token": refreshToken, + "message": "success", + "data": u, + }) +} + +func handleRefresh (ctx *gin.Context) { + var req RefreshReq + ctx.Bind(&req) + + tk, _ := jwt.ParseWithClaims(req.RefreshToken, &AuthClaims{}, func (token *jwt.Token) (interface{}, error) { + return []byte(REFRESH_KEY), nil + }) + + claims, ok := tk.Claims.(*AuthClaims) + if !ok { + ctx.Error(errors.ErrUnauthorized) + ctx.Abort() + return + } + + if !tk.Valid { + eat := claims.ExpiresAt.Unix() + if eat != 0 && eat < time.Now().Unix() { + ctx.Error(errors.ErrSessionExpired) + } else { + ctx.Error(errors.ErrUnauthorized) + } + + ctx.Abort() + return + } + + // TODO: if token is valid, check if user even exists before generating authToken + + authToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, + AuthClaims { + jwt.RegisteredClaims { + IssuedAt: jwt.NewNumericDate(time.Now()), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 2)), + }, + claims.UserID, + }, + ).SignedString(AUTH_KEY) + if err != nil { + // TODO: handle potential errors + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "auth_token": authToken, + "message": "success", + //"data": u, + }) +} |