diff options
author | Emile <git@emile.space> | 2025-02-19 19:53:25 +0100 |
---|---|---|
committer | Emile <git@emile.space> | 2025-02-19 19:53:25 +0100 |
commit | ae39f02812bcfe903e956220c890bfb7b9bb9ff4 (patch) | |
tree | dff7028627665a7d2cb7cd64533ac74ec8919379 /nix/templates/goapp/frontend/src/handlers.go | |
parent | 07425c679f7399284c0fe3dcbee54f45b23d07a0 (diff) |
removed the backend, added the frontend with oidc support
So I've added oidc support which is nice, yet I have to test this with some https foo, so I'm pushing this.
Diffstat (limited to 'nix/templates/goapp/frontend/src/handlers.go')
-rw-r--r-- | nix/templates/goapp/frontend/src/handlers.go | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/nix/templates/goapp/frontend/src/handlers.go b/nix/templates/goapp/frontend/src/handlers.go new file mode 100644 index 0000000..8fdd325 --- /dev/null +++ b/nix/templates/goapp/frontend/src/handlers.go @@ -0,0 +1,236 @@ +package main + +import ( + "fmt" + "html/template" + "log" + "net/http" + + "github.com/coreos/go-oidc/v3/oidc" + "github.com/gorilla/sessions" + "golang.org/x/oauth2" +) + +func indexHandler(w http.ResponseWriter, r *http.Request) { + session, err := globalState.sessions.Get(r, "session") + if err != nil { + log.Println("error getting the session") + } + + tpl := indexTplData{ + Error: r.FormValue("error"), + } + + tpl.Breadcrumbs = []Breadcrumb{ + { + Link{"a", "b"}, + []Link{ + {"c", "d"}, + {"e", "f"}, + }, + }, + { + Link{"g", "h"}, + []Link{ + {"i", "j"}, + {"k", "l"}, + }, + }, + } + tpl.NextLinks = []Link{ + {"Login", "/login"}, + } + + if logged, ok := session.Values["logged"].(bool); ok && logged { + tpl.LoggedIn = true + tpl.Claims.IDToken = session.Values["id_token"].(Claims) + tpl.Claims.UserInfo = session.Values["userinfo"].(Claims) + + if len(options.GroupsFilter) >= 1 { + for _, group := range tpl.Claims.UserInfo.Groups { + if isStringInSlice(group, options.GroupsFilter) { + tpl.Groups = append(tpl.Groups, filterText(group, options.Filters)) + } + } + } else { + tpl.Groups = filterSliceOfText(tpl.Claims.UserInfo.Groups, options.Filters) + } + + tpl.Claims.IDToken.PreferredUsername = filterText(tpl.Claims.IDToken.PreferredUsername, options.Filters) + tpl.Claims.UserInfo.PreferredUsername = filterText(tpl.Claims.UserInfo.PreferredUsername, options.Filters) + tpl.Claims.IDToken.Audience = filterSliceOfText(tpl.Claims.IDToken.Audience, options.Filters) + tpl.Claims.UserInfo.Audience = filterSliceOfText(tpl.Claims.UserInfo.Audience, options.Filters) + tpl.Claims.IDToken.Issuer = filterText(tpl.Claims.IDToken.Issuer, options.Filters) + tpl.Claims.UserInfo.Issuer = filterText(tpl.Claims.UserInfo.Issuer, options.Filters) + tpl.Claims.IDToken.Email = filterText(tpl.Claims.IDToken.Email, options.Filters) + tpl.Claims.UserInfo.Email = filterText(tpl.Claims.UserInfo.Email, options.Filters) + tpl.Claims.IDToken.Name = filterText(tpl.Claims.IDToken.Name, options.Filters) + tpl.Claims.UserInfo.Name = filterText(tpl.Claims.UserInfo.Name, options.Filters) + tpl.RawToken = rawTokens[tpl.Claims.IDToken.JWTIdentifier] + tpl.AuthorizeCodeURL = acURLs[tpl.Claims.IDToken.JWTIdentifier].String() + } + + w.Header().Add("Content-Type", "text/html") + + // get the template + t, err := template.New("index").Funcs(templateFuncMap).ParseGlob(fmt.Sprintf("%s/*.html", options.TemplatesPath)) + if err != nil { + log.Printf("Error reading the template Path: %s/*.html", options.TemplatesPath) + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("500 - Error reading template file")) + return + } + + // exec! + err = t.ExecuteTemplate(w, "index", tpl) + if err != nil { + log.Println(err) + } +} + +func loginHandler(w http.ResponseWriter, r *http.Request) { + log.Println("[ ] Getting the global session from the session cookie:") + session, err := globalState.sessions.Get(r, options.CookieName) + if err != nil { + log.Println("[ ] Error getting the cookie") + writeErr(w, nil, "error getting cookie", http.StatusInternalServerError) + return + } + + log.Println("[ ] Setting the redirect URL") + session.Values["redirect-url"] = "/" + + log.Println("[ ] Saving the session") + if err = session.Save(r, w); err != nil { + writeErr(w, err, "error saving session", http.StatusInternalServerError) + return + } + + log.Printf("[ ] Redirecting to %s", oauth2Config.AuthCodeURL("random-string")) + http.Redirect(w, r, oauth2Config.AuthCodeURL("random-string-here"), http.StatusFound) +} + +func logoutHandler(w http.ResponseWriter, r *http.Request) { + session, err := globalState.sessions.Get(r, options.CookieName) + if err != nil { + writeErr(w, err, "error getting cookie", http.StatusInternalServerError) + return + } + + // wet the session + session.Values = make(map[interface{}]interface{}) + + if err = session.Save(r, w); err != nil { + writeErr(w, err, "error saving session", http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/", http.StatusFound) +} + +func oauthCallbackHandler(res http.ResponseWriter, req *http.Request) { + log.Println("hit the oauth callback handler") + if req.FormValue("error") != "" { + log.Printf("got an error from the idp: %s", req.FormValue("error")) + http.Redirect(res, req, fmt.Sprintf("/error?%s", req.Form.Encode()), http.StatusFound) + + return + } + + var ( + token *oauth2.Token + idToken *oidc.IDToken + err error + idTokenRaw string + ok bool + ) + + // The state should be checked here in production + if token, err = oauth2Config.Exchange(req.Context(), req.URL.Query().Get("code")); err != nil { + log.Println("Unable to exchange authorization code for tokens") + writeErr(res, err, "unable to exchange authorization code for tokens", http.StatusInternalServerError) + return + } + + // Extract the ID Token from OAuth2 token. + if idTokenRaw, ok = token.Extra("id_token").(string); !ok { + log.Println("missing id token") + writeErr(res, nil, "missing id token", http.StatusInternalServerError) + return + } + + // Parse and verify ID Token payload. + if idToken, err = verifier.Verify(req.Context(), idTokenRaw); err != nil { + log.Printf("unable to verify id token or token is invalid: %+v", idTokenRaw) + writeErr(res, err, "unable to verify id token or token is invalid", http.StatusInternalServerError) + return + } + + // Extract custom claims + claimsIDToken := Claims{} + + if err = idToken.Claims(&claimsIDToken); err != nil { + log.Printf("unable to decode id token claims: %+v", &claimsIDToken) + writeErr(res, err, "unable to decode id token claims", http.StatusInternalServerError) + return + } + + var userinfo *oidc.UserInfo + + if userinfo, err = provider.UserInfo(req.Context(), oauth2.StaticTokenSource(token)); err != nil { + log.Printf("unable to retreive userinfo claims") + writeErr(res, err, "unable to retrieve userinfo claims", http.StatusInternalServerError) + return + } + + claimsUserInfo := Claims{} + + if err = userinfo.Claims(&claimsUserInfo); err != nil { + log.Printf("unable to decode userinfo claims") + writeErr(res, err, "unable to decode userinfo claims", http.StatusInternalServerError) + return + } + + var session *sessions.Session + + if session, err = globalState.sessions.Get(req, options.CookieName); err != nil { + log.Printf("unable to get session from cookie") + writeErr(res, err, "unable to get session from cookie", http.StatusInternalServerError) + return + } + + session.Values["id_token"] = claimsIDToken + session.Values["userinfo"] = claimsUserInfo + session.Values["logged"] = true + rawTokens[claimsIDToken.JWTIdentifier] = idTokenRaw + acURLs[claimsIDToken.JWTIdentifier] = req.URL + + if err = session.Save(req, res); err != nil { + log.Printf("unable to save session") + writeErr(res, err, "unable to save session", http.StatusInternalServerError) + return + } + + var redirectUrl string + + if redirectUrl, ok = session.Values["redirect-url"].(string); ok { + log.Printf("all fine!") + http.Redirect(res, req, redirectUrl, http.StatusFound) + return + } + + http.Redirect(res, req, "/", http.StatusFound) +} + +func writeErr(w http.ResponseWriter, err error, msg string, statusCode int) { + switch { + case err == nil: + log.Println(msg) + http.Error(w, msg, statusCode) + default: + log.Println(msg) + log.Println(err) + http.Error(w, fmt.Errorf("%s: %w", msg, err).Error(), statusCode) + } +} |