diff options
| author | Leonardo Bishop <me@leonardobishop.net> | 2026-01-16 17:19:27 +0000 |
|---|---|---|
| committer | Leonardo Bishop <me@leonardobishop.net> | 2026-01-16 17:19:27 +0000 |
| commit | e6cbb8415490524034561102b6c9f03e92e4dae7 (patch) | |
| tree | 2012f04c11adf636bdd06ae37f5ef3efd7a645a0 /web/handler/auth.go | |
| parent | 8fc52adfdc705a1b05d3a0aef4d6e63f8ec0308d (diff) | |
Add OIDC auth
Diffstat (limited to 'web/handler/auth.go')
| -rw-r--r-- | web/handler/auth.go | 149 |
1 files changed, 116 insertions, 33 deletions
diff --git a/web/handler/auth.go b/web/handler/auth.go index 38b87b6..29bd47c 100644 --- a/web/handler/auth.go +++ b/web/handler/auth.go @@ -1,81 +1,164 @@ package handler import ( + "errors" "html/template" "log/slog" "net/http" - "strconv" "time" + "git.leonardobishop.net/instancer/pkg/auth" "git.leonardobishop.net/instancer/pkg/session" ) -func GetAuth(tmpl *template.Template) http.HandlerFunc { +func GetAuth(tmpl *template.Template, authProvider *auth.OIDCAuthProvider) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - tmpl.ExecuteTemplate(w, "auth.html", nil) + w.Header().Add("HX-Redirect", "/auth") + tmpl.ExecuteTemplate(w, "auth.html", struct { + Error string + OidcIdPName string + }{ + Error: "", + OidcIdPName: authProvider.Name, + }) } } -func PostAuth(tmpl *template.Template, session *session.MemoryStore) http.HandlerFunc { +func PostAuth(tmpl *template.Template, session *session.MemoryStore, authProvider *auth.OIDCAuthProvider) 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 + 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 }{ - Message: "Invalid form data", + Error: "Failed to start OIDC journey", + OidcIdPName: authProvider.Name, }) return } - team := r.FormValue("team") - if team == "" { - tmpl.ExecuteTemplate(w, "f_auth_error.html", struct { - Message string - }{ - Message: "No team entered", - }) - 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() - if _, err := strconv.Atoi(team); err != nil { - tmpl.ExecuteTemplate(w, "f_auth_error.html", struct { - Message string - }{ - Message: "Team ID must be number", - }) + 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 } - session, err := session.Create(team) + err = authProvider.CompleteJourney(r.Context(), query.Get("code"), query.Get("state"), r.RemoteAddr, r.UserAgent(), s) + 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", - }) + 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: "session", - Value: session.Token, + Name: "instancer-session", + Value: s.Token, Path: "/", Secure: true, SameSite: http.SameSiteStrictMode, HttpOnly: true, }) - w.Header().Add("HX-Redirect", "/") + 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: "session", + Name: "instancer-session", Value: "", Expires: time.Unix(0, 0), |
