package auth import ( "nixcn-cms/data" "nixcn-cms/internal/cryptography" "nixcn-cms/pkgs/authcode" "nixcn-cms/pkgs/email" "nixcn-cms/pkgs/turnstile" "github.com/google/uuid" log "github.com/sirupsen/logrus" "gorm.io/gorm" "github.com/gin-gonic/gin" "github.com/spf13/viper" ) type MagicRequest struct { ClientId string `json:"client_id"` RedirectUri string `json:"redirect_uri"` State string `json:"state"` Email string `json:"email"` TurnstileToken string `json:"turnstile_token"` } func Magic(c *gin.Context) { // Parse request var req MagicRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(400, gin.H{"error": "invalid request"}) return } // Cloudflare turnstile ok, err := turnstile.VerifyTurnstile(req.TurnstileToken, c.ClientIP()) if err != nil || !ok { c.JSON(403, gin.H{"error": "turnstile failed"}) return } code, err := authcode.NewAuthCode(req.Email) if err != nil { c.JSON(500, gin.H{"status": "code gen failed"}) } uri := viper.GetString("server.external_url") + "/api/v1/auth/redirect?" + "code=" + code + "&redirect_uri" + req.RedirectUri + "&state" + req.State debugMode := viper.GetString("server.debug_mode") if debugMode == "true" { log.Info("Magic link for " + req.Email + " : " + uri) } else { // Send email using resend resend, err := email.NewResendClient() if err != nil { log.Error(err) c.JSON(500, gin.H{"status": "invalid email config"}) return } resend.Send( req.Email, "NixCN CMS Email Verify", "

Click the link below to verify your email. This link will expire in 10 minutes.

"+uri+"", ) } c.JSON(200, gin.H{"status": "magic link sent"}) } func VerifyMagicLink(c *gin.Context) { // Get token from url magicToken := c.Query("token") if magicToken == "" { c.JSON(400, gin.H{"error": "missing token"}) return } // Verify email token email, ok := authcode.VerifyAuthCode(magicToken) if !ok { c.JSON(401, gin.H{"error": "invalid or expired token"}) return } // Verify if user exists userData := new(data.User) user, err := userData.GetByEmail(email) if err != nil { if err == gorm.ErrRecordNotFound { // Create user user.UUID = uuid.New() user.UserId = uuid.New() user.Email = email user.PermissionLevel = 10 if err := user.Create(); err != nil { c.JSON(500, gin.H{"status": "internal server error"}) return } } else { c.JSON(500, gin.H{"status": "internal server error"}) return } } // Generate jwt JwtTool := cryptography.Token{ Application: viper.GetString("server.application"), } accessToken, refreshToken, err := JwtTool.IssueTokens(user.UserId) if err != nil { c.JSON(500, gin.H{ "status": "error generating tokens", }) return } c.JSON(200, gin.H{ "access_token": accessToken, "refresh_token": refreshToken, }) }