// Package middleware to deal with sessions package middleware import ( "context" "crypto/rand" "net/http" ) type Session struct { User string } type SessionStore struct { sessions map[string]Session } type ContextKey string func NewSessionStore() SessionStore { return SessionStore{sessions: make(map[string]Session, 10)} } func (s *SessionStore) Login(user string, w http.ResponseWriter) string { sessionID := rand.Text() s.sessions[sessionID] = Session{User: user} cookie := http.Cookie{ Name: "id", Value: sessionID, MaxAge: 3600, Secure: true, HttpOnly: true, Path: "/", } http.SetCookie(w, &cookie) return sessionID } func (s *SessionStore) Logout(w http.ResponseWriter, r *http.Request) { session := r.Context().Value(ContextKey("session")).(string) delete(s.sessions, session) cookie := http.Cookie{ Name: "id", Value: "", MaxAge: -1, Secure: true, HttpOnly: true, Path: "/", } http.SetCookie(w, &cookie) http.Redirect(w, r, "/", http.StatusTemporaryRedirect) } func (s *SessionStore) AsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sessionCookie, err := r.Cookie("id") user := "anon" var cookieVal string // Session exists if err == nil { session, ok := s.sessions[sessionCookie.Value] // Session not expired if ok { user = session.User cookieVal = sessionCookie.Value } } nextWithSessionContext(w, r, next, user, cookieVal) }) } func nextWithSessionContext(w http.ResponseWriter, r *http.Request, next http.Handler, user string, sessionID string) { ctx := r.Context() ctx = context.WithValue( context.WithValue( ctx, ContextKey("user"), user, ), ContextKey("session"), sessionID, ) next.ServeHTTP(w, r.WithContext(ctx)) }