package main import ( "github.com/gin-gonic/gin" "github.com/pquerna/otp/totp" "gorm.io/driver/sqlite" "gorm.io/gorm" ) var ( db *gorm.DB ) type User struct { gorm.Model Secret string Username string `gorm:"uniqueIndex"` Email string } func init() { var err error db, err = gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) if err != nil { panic("failed to connect database") } db.AutoMigrate(&User{}) } func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.String(200, "format string, values ...any") }) r.POST("/signup", signup) r.POST("/login", login) r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")} } type Signup struct { Username string `form:"username"` Email string `form:"email"` } func signup(c *gin.Context) { var s Signup c.Bind(&s) key, err := totp.Generate(totp.GenerateOpts{AccountName: s.Username, Issuer: "me"}) if err != nil { c.Status(502) return } var user User user.Email = s.Email user.Username = s.Username user.Secret = key.Secret() db.Create(&user) c.String(200, "%s", key.Secret()) } type Login struct { Username string `form:"username"` Code string `form:"code"` } func login(c *gin.Context) { var login Login var user User c.Bind(&login) if err := db.Where("username = ?", login.Username).First(&user).Error; err != nil { c.String(404, "something went wrong") return } correct := totp.Validate(login.Code, user.Secret) if correct { c.String(200, "success!") } else { c.String(404, "something went wrong") } }