summaryrefslogtreecommitdiffstats
path: root/web/handler
diff options
context:
space:
mode:
authorLeonardo Bishop <me@leonardobishop.net>2026-01-16 17:19:27 +0000
committerLeonardo Bishop <me@leonardobishop.net>2026-01-16 17:19:27 +0000
commite6cbb8415490524034561102b6c9f03e92e4dae7 (patch)
tree2012f04c11adf636bdd06ae37f5ef3efd7a645a0 /web/handler
parent8fc52adfdc705a1b05d3a0aef4d6e63f8ec0308d (diff)
Add OIDC auth
Diffstat (limited to 'web/handler')
-rw-r--r--web/handler/auth.go149
-rw-r--r--web/handler/deploy.go2
-rw-r--r--web/handler/index.go4
-rw-r--r--web/handler/instance.go6
4 files changed, 123 insertions, 38 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),
diff --git a/web/handler/deploy.go b/web/handler/deploy.go
index 24e0f95..f0c40fc 100644
--- a/web/handler/deploy.go
+++ b/web/handler/deploy.go
@@ -24,7 +24,7 @@ func PostDeploy(tmpl *template.Template, dockerDeployer *deployer.DockerDeployer
}
session := r.Context().Value("session").(*session.UserSession)
- deployKey := dockerDeployer.StartDeploy(challenge, session.Team)
+ deployKey := dockerDeployer.StartDeploy(challenge, session.TeamID)
tmpl.ExecuteTemplate(w, "f_deploy.html", struct {
DeployKey string
diff --git a/web/handler/index.go b/web/handler/index.go
index 5cf44cf..9279151 100644
--- a/web/handler/index.go
+++ b/web/handler/index.go
@@ -22,10 +22,12 @@ func GetIndex(tmpl *template.Template, registryClient *registry.RegistryClient)
if err := tmpl.ExecuteTemplate(w, "index.html", struct {
Challenges []string
+ Name string
Team string
}{
Challenges: challenges,
- Team: session.Team,
+ Name: session.Name,
+ Team: session.TeamName,
}); err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
diff --git a/web/handler/instance.go b/web/handler/instance.go
index 0222a42..e5e4c53 100644
--- a/web/handler/instance.go
+++ b/web/handler/instance.go
@@ -15,9 +15,9 @@ func GetInstances(tmpl *template.Template, dockerDeployer *deployer.DockerDeploy
return func(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*session.UserSession)
- instances, err := dockerDeployer.GetTeamInstances(r.Context(), session.Team)
+ instances, err := dockerDeployer.GetTeamInstances(r.Context(), session.TeamID)
if err != nil {
- slog.Error("could not get team instances", "team", session.Team, "error", err)
+ slog.Error("could not get team instances", "team", session.TeamID, "error", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
@@ -70,7 +70,7 @@ func PostStopInstance(tmpl *template.Template, dockerDeployer *deployer.DockerDe
session := r.Context().Value("session").(*session.UserSession)
deployKey := r.PathValue("deployKey")
- err := dockerDeployer.StopInstance(r.Context(), deployKey, session.Team)
+ err := dockerDeployer.StopInstance(r.Context(), deployKey, session.TeamID)
if err != nil {
tmpl.ExecuteTemplate(w, "f_instance_result.html", struct {
State string