all repos — auth-boilerplate @ 9b74357216ccde9dafe9d4d3faa5e8ace72bab5b

A simple Go web-app boilerplate.

src/app/functions.go (view raw)

  1package app
  2
  3import (
  4	"context"
  5	"errors"
  6	"fmt"
  7	"log"
  8	"net/http"
  9	"os"
 10	"regexp"
 11	"strings"
 12	"time"
 13
 14	"github.com/birabittoh/auth-boilerplate/src/email"
 15)
 16
 17const (
 18	minUsernameLength = 3
 19	maxUsernameLength = 10
 20)
 21
 22var (
 23	validUsername = regexp.MustCompile(`(?i)^[a-z0-9._-]+$`)
 24	validEmail    = regexp.MustCompile(`^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$`)
 25)
 26
 27func getUserByName(username string, excluding uint) (user User, err error) {
 28	err = db.Model(&User{}).Where("upper(username) == upper(?) AND id != ?", username, excluding).First(&user).Error
 29	return
 30}
 31
 32func sanitizeUsername(username string) (string, error) {
 33	if !validUsername.MatchString(username) || len(username) < minUsernameLength || len(username) > maxUsernameLength {
 34		return "", errors.New("invalid username")
 35	}
 36
 37	return username, nil
 38}
 39
 40func sanitizeEmail(email string) (string, error) {
 41	email = strings.ToLower(email)
 42
 43	if !validEmail.MatchString(email) {
 44		return "", fmt.Errorf("invalid email")
 45	}
 46
 47	return email, nil
 48}
 49
 50func login(w http.ResponseWriter, userID uint, remember bool) {
 51	var duration time.Duration
 52	if remember {
 53		duration = durationWeek
 54	} else {
 55		duration = durationDay
 56	}
 57
 58	cookie, err := g.GenerateCookie(duration)
 59	if err != nil {
 60		http.Error(w, "Could not generate session cookie.", http.StatusInternalServerError)
 61	}
 62
 63	ks.Set("session:"+cookie.Value, userID, duration)
 64	http.SetCookie(w, cookie)
 65}
 66
 67func loadEmailConfig() *email.Client {
 68	address := os.Getenv("APP_SMTP_EMAIL")
 69	password := os.Getenv("APP_SMTP_PASSWORD")
 70	host := os.Getenv("APP_SMTP_HOST")
 71	port := os.Getenv("APP_SMTP_PORT")
 72
 73	if address == "" || password == "" || host == "" {
 74		log.Println("Missing email configuration.")
 75		return nil
 76	}
 77
 78	if port == "" {
 79		port = "587"
 80	}
 81
 82	return email.NewClient(address, password, host, port)
 83}
 84
 85func sendEmail(mail email.Email) error {
 86	if m == nil {
 87		return errors.New("email client is not initialized")
 88	}
 89	return m.Send(mail)
 90}
 91
 92func sendResetEmail(address, token string) {
 93	resetURL := fmt.Sprintf("%s/reset-password-confirm?token=%s", baseUrl, token)
 94	err := sendEmail(email.Email{
 95		To:      []string{address},
 96		Subject: "Reset password",
 97		Body:    fmt.Sprintf("Use the following link to reset your password:\n%s", resetURL),
 98	})
 99	if err != nil {
100		log.Printf("Could not send reset email for %s. Link: %s", address, resetURL)
101	}
102}
103
104func readSessionCookie(r *http.Request) (userID *uint, err error) {
105	cookie, err := r.Cookie("session_token")
106	if err != nil {
107		return
108	}
109	return ks.Get("session:" + cookie.Value)
110}
111
112// Middleware to check if the user is logged in
113func loginRequired(next http.HandlerFunc) http.HandlerFunc {
114	return func(w http.ResponseWriter, r *http.Request) {
115		userID, err := readSessionCookie(r)
116		if err != nil {
117			http.Redirect(w, r, "/login", http.StatusFound)
118			return
119		}
120
121		ctx := context.WithValue(r.Context(), userContextKey, *userID)
122		next(w, r.WithContext(ctx))
123	}
124}
125
126func getLoggedUser(r *http.Request) (user User, ok bool) {
127	userID, ok := r.Context().Value(userContextKey).(uint)
128	db.Find(&user, userID)
129	return user, ok
130}