package handler import ( "errors" "html/template" "log/slog" "net/http" "net/url" "time" "git.leonardobishop.net/instancer/pkg/auth" "git.leonardobishop.net/instancer/pkg/session" ) func GetAuth(tmpl *template.Template, authProvider *auth.OIDCAuthProvider) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { errMessage := r.URL.Query().Get("error") if errMessage != "" { w.Header().Add("HX-Redirect", "/auth?error="+url.QueryEscape(errMessage)) } else { w.Header().Add("HX-Redirect", "/auth") } tmpl.ExecuteTemplate(w, "auth.html", struct { Error string OidcIdPName string }{ Error: errMessage, OidcIdPName: authProvider.Name, }) } } func PostAuth(tmpl *template.Template, session *session.MemoryStore, authProvider *auth.OIDCAuthProvider) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { url, err := authProvider.StartJourney(r.RemoteAddr, r.UserAgent()) if err != nil { slog.Error("oidc start journey failed", "cause", err) tmpl.ExecuteTemplate(w, "auth.html", struct { Error string OidcIdPName string }{ Error: "Failed to start OIDC journey", OidcIdPName: authProvider.Name, }) return } http.Redirect(w, r, url, http.StatusFound) } } func GetAuthCallback(tmpl *template.Template, session *session.MemoryStore, authProvider *auth.OIDCAuthProvider) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { type pageParams struct { Error string OidcIdPName string } params := pageParams{OidcIdPName: authProvider.Name} query := r.URL.Query() s, err := session.Create() if err != nil { params.Error = "Failed to create session" slog.Error("session creation failed", "cause", err) w.Header().Add("HX-Push-Url", "/auth") tmpl.ExecuteTemplate(w, "auth.html", params) return } err = authProvider.CompleteJourney(r.Context(), query.Get("code"), query.Get("state"), r.RemoteAddr, r.UserAgent(), s) if err != nil { if errors.Is(err, auth.ErrNotAuthorised) { params.Error = "You are not authorised to use this service" goto done } else if errors.Is(err, auth.ErrInvalidState) { params.Error = "Invalid state" goto done } else if errors.Is(err, auth.ErrStateVerificationFailed) { params.Error = "State verification failed" goto done } else if errors.Is(err, auth.ErrInvalidIDToken) { params.Error = "Invalid ID token" goto done } params.Error = "Failed to complete OIDC journey" slog.Error("oidc complete journey failed", "cause", err) done: w.Header().Add("HX-Push-Url", "/auth") tmpl.ExecuteTemplate(w, "auth.html", params) session.Destroy(s.Token) return } http.SetCookie(w, &http.Cookie{ Name: "instancer-session", Value: s.Token, Path: "/", Secure: true, SameSite: http.SameSiteStrictMode, HttpOnly: true, }) http.Redirect(w, r, "/", http.StatusFound) } } //func PostAuth(tmpl *template.Template, session *session.MemoryStore) http.HandlerFunc { // return func(w http.ResponseWriter, r *http.Request) { // if err := r.ParseForm(); err != nil { // tmpl.ExecuteTemplate(w, "f_auth_error.html", struct { // Message string // }{ // Message: "Invalid form data", // }) // return // } // // team := r.FormValue("team") // if team == "" { // tmpl.ExecuteTemplate(w, "f_auth_error.html", struct { // Message string // }{ // Message: "No team entered", // }) // return // } // // if _, err := strconv.Atoi(team); err != nil { // tmpl.ExecuteTemplate(w, "f_auth_error.html", struct { // Message string // }{ // Message: "Team ID must be number", // }) // return // } // // session, err := session.Create(team) // if err != nil { // slog.Error("could not create session", "cause", err) // tmpl.ExecuteTemplate(w, "f_auth_error.html", struct { // Message string // }{ // Message: "Could not create session", // }) // return // } // // http.SetCookie(w, &http.Cookie{ // Name: "session", // Value: session.Token, // // Path: "/", // Secure: true, // SameSite: http.SameSiteStrictMode, // HttpOnly: true, // }) // w.Header().Add("HX-Redirect", "/") // } //} func GetLogout(session *session.MemoryStore) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { //TODO expire session here http.SetCookie(w, &http.Cookie{ Name: "instancer-session", Value: "", Expires: time.Unix(0, 0), Path: "/", Secure: true, SameSite: http.SameSiteStrictMode, HttpOnly: true, }) http.Redirect(w, r, "/auth", http.StatusFound) } }