package auth import ( "database/sql" "net/http" "os" "screenmark/screenmark/.gen/haystack/haystack/model" "screenmark/screenmark/middleware" "screenmark/screenmark/models" "github.com/charmbracelet/log" "github.com/go-chi/chi/v5" ) type AuthHandler struct { logger *log.Logger user models.UserModel auth Auth jwtManager *middleware.JwtManager } type loginBody struct { Email string `json:"email"` } type codeBody struct { Email string `json:"email"` Code string `json:"code"` } type codeReturn struct { Access string `json:"access"` Refresh string `json:"refresh"` } type refreshBody struct { Refresh string `json:"refresh"` } type refreshReturn struct { Access string `json:"access"` } func (h *AuthHandler) login(body loginBody, w http.ResponseWriter, r *http.Request) { err := h.auth.CreateCode(body.Email) if err != nil { middleware.WriteErrorInternal(h.logger, "could not create a code", w) return } w.WriteHeader(http.StatusOK) } func (h *AuthHandler) code(body codeBody, w http.ResponseWriter, r *http.Request) { if err := h.auth.UseCode(body.Email, body.Code); err != nil { middleware.WriteErrorBadRequest(h.logger, "email or code are incorrect", w) return } // TODO: we should only keep emails around for a little bit. // Time to first login should be less than 10 minutes. // So actually, they shouldn't be written to our database. if exists := h.user.DoesUserExist(r.Context(), body.Email); !exists { h.user.Save(r.Context(), model.Users{ Email: body.Email, }) } uuid, err := h.user.GetUserIdFromEmail(r.Context(), body.Email) if err != nil { middleware.WriteErrorBadRequest(h.logger, "failed to get user", w) return } refresh := h.jwtManager.CreateRefreshToken(uuid) access := h.jwtManager.CreateAccessToken(uuid) codeReturn := codeReturn{ Access: access, Refresh: refresh, } middleware.WriteJsonOrError(h.logger, codeReturn, w) } func (h *AuthHandler) refresh(body refreshBody, w http.ResponseWriter, r *http.Request) { h.logger.Info("token", "refresh", body.Refresh) userId, err := h.jwtManager.GetUserIdFromRefresh(body.Refresh) if err != nil { middleware.WriteErrorBadRequest(h.logger, "invalid refresh token: "+err.Error(), w) return } access := h.jwtManager.CreateAccessToken(userId) refreshReturn := refreshReturn{ Access: access, } middleware.WriteJsonOrError(h.logger, refreshReturn, w) } func (h *AuthHandler) CreateRoutes(r chi.Router) { h.logger.Info("Mounting auth router") r.Group(func(r chi.Router) { r.Use(middleware.SetJson) r.Post("/login", middleware.WithValidatedPost(h.login)) r.Post("/code", middleware.WithValidatedPost(h.code)) r.Post("/refresh", middleware.WithValidatedPost(h.refresh)) }) } func CreateAuthHandler(db *sql.DB, jwtManager *middleware.JwtManager) AuthHandler { userModel := models.NewUserModel(db) logger := log.New(os.Stdout).WithPrefix("Auth") mailer, err := CreateMailClient(logger) if err != nil { panic(err) } auth := CreateAuth(mailer) return AuthHandler{ logger: logger, user: userModel, auth: auth, jwtManager: jwtManager, } }