package auth import ( "context" "encoding/json" "fmt" "net/http" "time" // "acoolname.co/backend/models" "acooldomain.co/backend/models" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "golang.org/x/crypto/bcrypt" ) var hmacSampleSecret []byte type Connection struct { connection *mongo.Client } type TokenInfo struct { Username string `json:"username"` Permissions models.Permission `json:"permissions"` } type AuthClaims struct { *jwt.StandardClaims TokenInfo } func signToken(token TokenInfo) (string, error) { t := jwt.New(jwt.GetSigningMethod("HS512")) t.Claims = &AuthClaims{ &jwt.StandardClaims{ ExpiresAt: time.Now().Add(time.Hour * 24 * 30).Unix(), }, token, } return t.SignedString(hmacSampleSecret) } func hashPassword(password string) (string, error) { bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) return string(bytes), err } func AuthorizedTo(requiredPermissions models.Permission, overwriters ...func(*gin.Context) bool) gin.HandlerFunc { return func(ctx *gin.Context) { authCookie, err := ctx.Request.Cookie("auth") if err != nil { ctx.AbortWithError(403, err) return } token, err := jwt.ParseWithClaims(authCookie.Value, &AuthClaims{}, func(token *jwt.Token) (interface{}, error) { // Don't forget to validate the alg is what you expect: if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key") return hmacSampleSecret, nil }) if err != nil { ctx.AbortWithError(403, err) return } if claims, ok := token.Claims.(*AuthClaims); ok && token.Valid { ctx.Set("claims", claims) if requiredPermissions&claims.Permissions != requiredPermissions && models.Admin&claims.Permissions != models.Admin { for _, overwrite := range overwriters { if overwrite(ctx) { return } } ctx.AbortWithStatus(403) return } } else { ctx.AbortWithStatus(500) } } } func (con Connection) signUp(c *gin.Context) { var token TokenInfo type SignUpRequest struct { token string username string password string } err := json.NewDecoder(c.Request.Body).Decode(&token) if err != nil { c.AbortWithError(500, err) } signedToken, err := signToken(token) if err != nil { c.AbortWithError(500, err) } c.SetCookie("auth", signedToken, -1, "", "", false, false) c.IndentedJSON(http.StatusOK, signedToken) } type SignInRequest struct { Username string `json:"username"` Password string `json:"password"` } func (con Connection) signIn(c *gin.Context) { var request SignInRequest err := json.NewDecoder(c.Request.Body).Decode(&request) if err != nil { c.AbortWithError(500, err) return } var userItem models.User err = con.connection.Database("Backend").Collection("Users").FindOne(context.TODO(), bson.D{{Key: "Username", Value: request.Username}}).Decode(&userItem) if err != nil { c.AbortWithError(403, err) return } if bcrypt.CompareHashAndPassword([]byte(userItem.HashedPass), []byte(request.Password)) != nil { c.AbortWithStatus(403) return } token := TokenInfo{ Username: userItem.Username, Permissions: userItem.Permissions, } signedToken, err := signToken(token) if err != nil { c.AbortWithError(500, err) return } c.SetCookie("auth", signedToken, -1, "", "", false, false) c.IndentedJSON(http.StatusOK, signedToken) } func (con Connection) test(c *gin.Context) { claims, exists := c.Get("claims") if !exists { fmt.Println("No Claims") c.AbortWithStatus(403) return } c.IndentedJSON(http.StatusOK, claims) } func LoadGroup(group *gin.RouterGroup, client *mongo.Client) { connection := Connection{connection: client} group.POST("/signin", connection.signIn) group.POST("/signup", AuthorizedTo(models.Admin), connection.signUp) group.GET("/test", AuthorizedTo(models.Admin), connection.test) }