add login handler

This commit is contained in:
Branden J Brown 2024-02-01 20:40:59 -06:00
parent 570fc6a298
commit 6aa6996f66
4 changed files with 52 additions and 38 deletions

View File

@ -26,6 +26,7 @@ func main() {
panic(err)
}
r := chi.NewRouter()
r.Post("/user/login", s.Login)
r.With(serve.WithPlayerID(sessions)).Get("/queue", s.Queue)
http.ListenAndServe(":8080", r)
}

View File

@ -1,38 +0,0 @@
package serve
import (
"net/http"
"time"
)
const cookieName = "__Host-consent-v1"
// SetConsent registers a consent cookie on the response.
func SetConsent(w http.ResponseWriter) {
http.SetCookie(w, &http.Cookie{
Name: cookieName,
Value: "given",
Expires: time.Now().Add(20 * 365 * 24 * time.Hour),
Path: "/",
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
})
}
// NeedsConsent is a middleware that immediately responds with a 403 if the
// request does not bear a consent cookie.
func NeedsConsent(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if _, err := r.Cookie(cookieName); err != nil {
http.Error(w, cookieFailed, http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
const cookieFailed = `
The requested resource requires consent to processing identifying information and storing necessary cookies.
I'm just a lil guy. The information is used solely for providing the service's functionality.
`

View File

@ -21,6 +21,19 @@ func with[T any](ctx context.Context, v T) context.Context {
const sessionCookie = "__Host-id-v1"
// SetSession sets a cookie carrying a session token on a response.
func SetSession(w http.ResponseWriter, s player.Session) {
http.SetCookie(w, &http.Cookie{
Name: sessionCookie,
Value: s.String(),
Path: "/",
Expires: time.Now().Add(365 * 24 * time.Hour),
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
})
}
// WithPlayerID is a middleware that adds a player ID to the request context
// based on the session cookie content. If there is no such cookie, or its
// value is invalid, the request fails with a 403 error.

View File

@ -3,6 +3,7 @@ package main
import (
"context"
"errors"
"io"
"log/slog"
"net/http"
"sync"
@ -20,6 +21,9 @@ import (
type Server struct {
l *lobby.Lobby
creds player.RowQuerier
sessions player.Execer
mu sync.Mutex
pp map[player.ID]*websocket.Conn
}
@ -62,6 +66,40 @@ func (s *Server) left(p player.ID) {
}
}
func (s *Server) Login(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
slog := slog.With(
slog.String("remote", r.RemoteAddr),
slog.String("forwarded-for", r.Header.Get("X-Forwarded-For")),
slog.String("user-agent", r.UserAgent()),
)
if err := r.ParseForm(); err != nil {
slog.WarnContext(ctx, "error parsing form on login", "err", err.Error())
http.Error(w, "what", http.StatusBadRequest)
return
}
user, pass := r.PostFormValue("user"), r.PostFormValue("pass")
if user == "" || pass == "" {
slog.WarnContext(ctx, "missing user or pass on login")
http.Error(w, "missing credentials", http.StatusBadRequest)
return
}
p, err := player.Login(ctx, s.creds, user, pass)
if err != nil {
slog.ErrorContext(ctx, "login failed", "err", err.Error())
http.Error(w, "no", http.StatusUnauthorized)
return
}
id, err := player.StartSession(ctx, s.sessions, p, time.Now())
if err != nil {
slog.ErrorContext(ctx, "failed to create session", "player", p, "err", err.Error())
http.Error(w, "something went wrong", http.StatusInternalServerError)
return
}
serve.SetSession(w, id)
io.WriteString(w, id.String())
}
// Queue connects players to games. The connection immediately upgrades.
// This handler MUST be wrapped in [serve.WithPlayerID].
func (s *Server) Queue(w http.ResponseWriter, r *http.Request) {