gonotes/internal/middleware/session.go

147 lines
3.6 KiB
Go
Raw Permalink Normal View History

2025-07-30 13:41:03 +01:00
// Package middleware to deal with sessions
2025-07-30 09:35:01 +01:00
package middleware
import (
"context"
2025-07-30 13:41:03 +01:00
"crypto/rand"
2025-12-10 20:40:41 +00:00
"log"
2025-07-30 09:35:01 +01:00
"net/http"
2025-12-10 20:40:41 +00:00
urls "forgejo.gwairfelin.com/max/gispatcho"
"forgejo.gwairfelin.com/max/gonotes/internal/auth"
"golang.org/x/oauth2"
2025-07-30 09:35:01 +01:00
)
2025-07-30 13:41:03 +01:00
type Session struct {
User string
}
type SessionStore struct {
sessions map[string]Session
2025-12-10 20:40:41 +00:00
oauth *oauth2.Config
Routes urls.URLs
2025-07-30 13:41:03 +01:00
}
type ContextKey string
2025-12-10 20:40:41 +00:00
func NewSessionStore(oauth *oauth2.Config, prefix string) SessionStore {
store := SessionStore{
sessions: make(map[string]Session, 10),
oauth: oauth,
}
store.Routes = urls.URLs{
Prefix: prefix,
URLs: map[string]urls.URL{
"login": {Path: "/login/", Protocol: "GET", Handler: store.LoginViewOAUTH},
"callback": {Path: "/callback/", Protocol: "GET", Handler: store.CallbackViewOAUTH},
2025-12-10 20:45:33 +00:00
"logout": {Path: "/logout/", Protocol: "GET", Handler: store.LogoutView},
2025-12-10 20:40:41 +00:00
},
}
return store
2025-07-30 13:41:03 +01:00
}
2025-12-10 20:45:33 +00:00
// Log a user in
2025-09-30 10:28:58 +01:00
func (s *SessionStore) Login(user string, w http.ResponseWriter) string {
2025-07-30 13:41:03 +01:00
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)
2025-09-30 10:28:58 +01:00
return sessionID
2025-07-30 13:41:03 +01:00
}
2025-12-10 20:45:33 +00:00
// View to logout a user
func (s *SessionStore) LogoutView(w http.ResponseWriter, r *http.Request) {
2025-07-30 13:41:03 +01:00
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)
2025-12-10 16:34:40 +00:00
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
2025-07-30 13:41:03 +01:00
}
2025-12-10 20:45:33 +00:00
// View to log in a user via oauth
2025-12-10 20:40:41 +00:00
func (s *SessionStore) LoginViewOAUTH(w http.ResponseWriter, r *http.Request) {
log.Printf("%+v", *s.oauth)
oauthState := auth.GenerateStateOAUTHCookie(w, s.Routes.Prefix)
url := s.oauth.AuthCodeURL(oauthState)
log.Printf("Redirecting to %s", url)
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}
2025-12-10 20:45:33 +00:00
// Oauth callback view
2025-12-10 20:40:41 +00:00
func (s *SessionStore) CallbackViewOAUTH(w http.ResponseWriter, r *http.Request) {
// Read oauthState from Cookie
oauthState, err := r.Cookie("oauthstate")
if err != nil {
log.Printf("An error occured during login: %s", err)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
log.Printf("%v", oauthState)
if r.FormValue("state") != oauthState.Value {
log.Println("invalid oauth state")
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
2025-12-10 20:56:17 +00:00
username, err := auth.GetUserFromForgejo(r.FormValue("code"), s.oauth)
2025-12-10 20:40:41 +00:00
if err != nil {
log.Println(err.Error())
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
2025-12-10 20:56:17 +00:00
s.Login(username, w)
2025-12-10 20:40:41 +00:00
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
}
2025-12-10 20:45:33 +00:00
// Turn the session store into a middleware.
// Sets the user on the context based on the available session cookie
2025-07-30 13:41:03 +01:00
func (s *SessionStore) AsMiddleware(next http.Handler) http.Handler {
2025-07-30 09:35:01 +01:00
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
sessionCookie, err := r.Cookie("id")
2025-12-10 16:34:40 +00:00
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
}
2025-07-30 09:35:01 +01:00
}
2025-12-10 16:34:40 +00:00
nextWithSessionContext(w, r, next, user, cookieVal)
2025-07-30 09:35:01 +01:00
})
}
2025-09-30 10:28:58 +01:00
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))
}