all repos — auth-boilerplate @ 9b74357216ccde9dafe9d4d3faa5e8ace72bab5b

A simple Go web-app boilerplate.

src/auth/auth.go (view raw)

  1package auth
  2
  3import (
  4	"crypto/rand"
  5	"encoding/hex"
  6	"errors"
  7	"net/http"
  8	"time"
  9	"unicode"
 10
 11	"golang.org/x/crypto/bcrypt"
 12)
 13
 14type Auth struct {
 15	Pepper       string
 16	spicesLength int
 17}
 18
 19const (
 20	minPasswordLength        = 6
 21	maxHashLength            = 72
 22	DefaultMaxPasswordLength = 56 // leaves 16 bytes for salt and pepper
 23)
 24
 25func NewAuth(pepper string, maxPasswordLength uint) *Auth {
 26	if maxPasswordLength > 70 {
 27		return nil
 28	}
 29
 30	hash, err := bcrypt.GenerateFromPassword([]byte(pepper), bcrypt.DefaultCost)
 31	if err != nil {
 32		return nil
 33	}
 34
 35	spicesLength := int(maxHashLength-maxPasswordLength) / 2
 36	return &Auth{
 37		Pepper:       hex.EncodeToString(hash)[:spicesLength],
 38		spicesLength: spicesLength,
 39	}
 40}
 41
 42func (g Auth) HashPassword(password string) (hashedPassword, salt string, err error) {
 43	if !isASCII(password) || len(password) < minPasswordLength {
 44		err = errors.New("invalid password")
 45		return
 46	}
 47
 48	salt, err = g.GenerateRandomToken(g.spicesLength)
 49	if err != nil {
 50		return
 51	}
 52	salt = salt[:g.spicesLength]
 53
 54	bytesPassword, err := bcrypt.GenerateFromPassword([]byte(password+salt+g.Pepper), bcrypt.DefaultCost)
 55	if err != nil {
 56		return
 57	}
 58
 59	hashedPassword = string(bytesPassword)
 60	return
 61}
 62
 63func (g Auth) CheckPassword(password, salt, hash string) bool {
 64	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password+salt+g.Pepper))
 65	return err == nil
 66}
 67
 68func (g Auth) GenerateRandomToken(n int) (string, error) {
 69	token := make([]byte, n)
 70	_, err := rand.Read(token)
 71	if err != nil {
 72		return "", err
 73	}
 74	return hex.EncodeToString(token), nil
 75}
 76
 77func (g Auth) GenerateCookie(duration time.Duration) (*http.Cookie, error) {
 78	sessionToken, err := g.GenerateRandomToken(32)
 79	if err != nil {
 80		return nil, err
 81	}
 82
 83	return &http.Cookie{
 84		Name:     "session_token",
 85		Value:    sessionToken,
 86		Expires:  time.Now().Add(duration),
 87		Path:     "/",
 88		HttpOnly: true,
 89		Secure:   true,
 90	}, nil
 91}
 92
 93func (g Auth) GenerateEmptyCookie() *http.Cookie {
 94	return &http.Cookie{
 95		Name:    "session_token",
 96		Value:   "",
 97		Expires: time.Now().Add(-1 * time.Hour),
 98		Path:    "/",
 99	}
100}
101
102func isASCII(s string) bool {
103	for i := 0; i < len(s); i++ {
104		if s[i] > unicode.MaxASCII {
105			return false
106		}
107	}
108	return true
109}