Go server authenticates against Supabase GoTrue (youpc instance) using email+password. Login page with login/register tabs, domain restricted to @hoganlovells.com. Auth middleware protects all routes, refreshes expired tokens via refresh_token cookie. Lime green branding. - internal/auth: Supabase client (sign in, sign up, refresh, sign out), JWT expiry decode, auth middleware, cookie management - internal/handlers: login/register/logout handlers, per-page template parsing to avoid content block collisions - templates/login.html: tabbed login/register form - 30-day HTTP-only session cookies with SameSite=Lax - SUPABASE_URL and SUPABASE_ANON_KEY env vars in docker-compose
175 lines
4.1 KiB
Go
175 lines
4.1 KiB
Go
package handlers
|
|
|
|
import (
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"mgit.msbls.de/m/patholo/internal/auth"
|
|
)
|
|
|
|
type loginData struct {
|
|
Mode string
|
|
Error string
|
|
Success string
|
|
Email string
|
|
}
|
|
|
|
func handleLogin(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
handleLoginPage(w, r)
|
|
case http.MethodPost:
|
|
handleLoginSubmit(w, r)
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func handleLoginPage(w http.ResponseWriter, r *http.Request) {
|
|
if cookie, err := r.Cookie(auth.SessionCookieName); err == nil && cookie.Value != "" {
|
|
if exp, err := auth.DecodeJWTExpiry(cookie.Value); err == nil && time.Now().Before(exp) {
|
|
http.Redirect(w, r, "/", http.StatusFound)
|
|
return
|
|
}
|
|
}
|
|
|
|
data := loginData{
|
|
Mode: r.URL.Query().Get("mode"),
|
|
Error: r.URL.Query().Get("error"),
|
|
}
|
|
if data.Mode == "" {
|
|
data.Mode = "login"
|
|
}
|
|
renderPage(w, "login.html", data)
|
|
}
|
|
|
|
func handleLoginSubmit(w http.ResponseWriter, r *http.Request) {
|
|
email := strings.TrimSpace(r.FormValue("email"))
|
|
password := r.FormValue("password")
|
|
|
|
if email == "" || password == "" {
|
|
renderPage(w, "login.html", loginData{
|
|
Mode: "login",
|
|
Error: "Bitte E-Mail und Passwort eingeben.",
|
|
Email: email,
|
|
})
|
|
return
|
|
}
|
|
|
|
if !isHoganLovellsEmail(email) {
|
|
renderPage(w, "login.html", loginData{
|
|
Mode: "login",
|
|
Error: "Zugang nur für @hoganlovells.com E-Mail-Adressen.",
|
|
Email: email,
|
|
})
|
|
return
|
|
}
|
|
|
|
tokens, err := authClient.SignIn(email, password)
|
|
if err != nil {
|
|
log.Printf("sign in failed for %s: %v", email, err)
|
|
errMsg := "Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut."
|
|
if strings.Contains(err.Error(), "Invalid login credentials") {
|
|
errMsg = "Ungültige E-Mail-Adresse oder Passwort."
|
|
}
|
|
renderPage(w, "login.html", loginData{
|
|
Mode: "login",
|
|
Error: errMsg,
|
|
Email: email,
|
|
})
|
|
return
|
|
}
|
|
|
|
auth.SetAuthCookies(w, r, tokens)
|
|
http.Redirect(w, r, "/", http.StatusFound)
|
|
}
|
|
|
|
func handleRegister(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
email := strings.TrimSpace(r.FormValue("email"))
|
|
password := r.FormValue("password")
|
|
confirm := r.FormValue("confirm")
|
|
|
|
if email == "" || password == "" {
|
|
renderPage(w, "login.html", loginData{
|
|
Mode: "register",
|
|
Error: "Bitte alle Felder ausfüllen.",
|
|
Email: email,
|
|
})
|
|
return
|
|
}
|
|
|
|
if password != confirm {
|
|
renderPage(w, "login.html", loginData{
|
|
Mode: "register",
|
|
Error: "Passwörter stimmen nicht überein.",
|
|
Email: email,
|
|
})
|
|
return
|
|
}
|
|
|
|
if len(password) < 8 {
|
|
renderPage(w, "login.html", loginData{
|
|
Mode: "register",
|
|
Error: "Passwort muss mindestens 8 Zeichen lang sein.",
|
|
Email: email,
|
|
})
|
|
return
|
|
}
|
|
|
|
if !isHoganLovellsEmail(email) {
|
|
renderPage(w, "login.html", loginData{
|
|
Mode: "register",
|
|
Error: "Registrierung nur für @hoganlovells.com E-Mail-Adressen.",
|
|
Email: email,
|
|
})
|
|
return
|
|
}
|
|
|
|
tokens, err := authClient.SignUp(email, password)
|
|
if err != nil {
|
|
log.Printf("sign up failed for %s: %v", email, err)
|
|
errMsg := "Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut."
|
|
if strings.Contains(err.Error(), "already registered") || strings.Contains(err.Error(), "already been registered") {
|
|
errMsg = "Ein Account mit dieser E-Mail existiert bereits."
|
|
}
|
|
renderPage(w, "login.html", loginData{
|
|
Mode: "register",
|
|
Error: errMsg,
|
|
Email: email,
|
|
})
|
|
return
|
|
}
|
|
|
|
if tokens != nil {
|
|
auth.SetAuthCookies(w, r, tokens)
|
|
http.Redirect(w, r, "/", http.StatusFound)
|
|
return
|
|
}
|
|
|
|
renderPage(w, "login.html", loginData{
|
|
Mode: "login",
|
|
Success: "Account erstellt. Bitte melden Sie sich an.",
|
|
Email: email,
|
|
})
|
|
}
|
|
|
|
func handleLogout(w http.ResponseWriter, r *http.Request) {
|
|
if cookie, err := r.Cookie(auth.SessionCookieName); err == nil {
|
|
authClient.SignOut(cookie.Value)
|
|
}
|
|
auth.ClearAuthCookies(w)
|
|
http.Redirect(w, r, "/login", http.StatusFound)
|
|
}
|
|
|
|
func isHoganLovellsEmail(email string) bool {
|
|
parts := strings.SplitN(email, "@", 2)
|
|
return len(parts) == 2 && strings.EqualFold(parts[1], "hoganlovells.com")
|
|
}
|