Prototype commit
This commit is contained in:
commit
b2ffbe865e
29 changed files with 3438 additions and 0 deletions
97
server/auth.go
Normal file
97
server/auth.go
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
cookieName = "docent_session"
|
||||
cookieMaxAge = 30 * 24 * time.Hour
|
||||
)
|
||||
|
||||
type session struct {
|
||||
Exp int64 `json:"exp"`
|
||||
}
|
||||
|
||||
func (s *server) signCookieValue() (string, error) {
|
||||
payload := session{Exp: time.Now().Add(cookieMaxAge).Unix()}
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
enc := base64.RawURLEncoding.EncodeToString(data)
|
||||
mac := hmac.New(sha256.New, s.secret)
|
||||
mac.Write([]byte(enc))
|
||||
sig := base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
|
||||
return enc + "." + sig, nil
|
||||
}
|
||||
|
||||
func (s *server) verifyCookieValue(value string) bool {
|
||||
parts := strings.SplitN(value, ".", 2)
|
||||
if len(parts) != 2 {
|
||||
return false
|
||||
}
|
||||
mac := hmac.New(sha256.New, s.secret)
|
||||
mac.Write([]byte(parts[0]))
|
||||
expected := base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
|
||||
if !hmac.Equal([]byte(expected), []byte(parts[1])) {
|
||||
return false
|
||||
}
|
||||
data, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var sess session
|
||||
if err := json.Unmarshal(data, &sess); err != nil {
|
||||
return false
|
||||
}
|
||||
return time.Now().Unix() < sess.Exp
|
||||
}
|
||||
|
||||
func (s *server) setSessionCookie(w http.ResponseWriter) {
|
||||
value, err := s.signCookieValue()
|
||||
if err != nil {
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieName,
|
||||
Value: value,
|
||||
Path: "/",
|
||||
MaxAge: int(cookieMaxAge.Seconds()),
|
||||
HttpOnly: true,
|
||||
Secure: s.secure,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *server) clearSessionCookie(w http.ResponseWriter) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: cookieName,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
HttpOnly: true,
|
||||
Secure: s.secure,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *server) isAuthed(r *http.Request) bool {
|
||||
c, err := r.Cookie(cookieName)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return s.verifyCookieValue(c.Value)
|
||||
}
|
||||
|
||||
func constantTimeEqualString(a, b string) bool {
|
||||
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue