Skip to content

Commit

Permalink
add logout flow
Browse files Browse the repository at this point in the history
  • Loading branch information
mpoegel committed Mar 9, 2024
1 parent 6a7d24c commit 872750f
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 52 deletions.
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -5,6 +5,7 @@ go 1.21
require (
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/fauna/faunadb-go/v4 v4.2.0
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/mattn/go-sqlite3 v1.14.19
github.com/prometheus/client_golang v1.15.1
Expand All @@ -23,7 +24,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
github.com/kr/text v0.2.0 // indirect
Expand Down
2 changes: 1 addition & 1 deletion pkg/pizza/config.go
Expand Up @@ -105,7 +105,7 @@ func LoadConfigEnv() Config {
OAuth2: OAuth2Config{
ClientID: loadStrEnv("OAUTH2_CLIENT_ID", ""),
ClientSecret: loadStrEnv("OAUTH2_CLIENT_SECRET", ""),
RedirectURL: loadStrEnv("OAUTH2_REDIRECT", "http://localhost/login/callback"),
RedirectURL: loadStrEnv("OAUTH2_REDIRECT", "http://localhost"),
RealmsURL: loadStrEnv("REALMS_URL", "http://localhost:8080/auth/realms/pizza"),
},
}
Expand Down
136 changes: 89 additions & 47 deletions pkg/pizza/server.go
Expand Up @@ -70,8 +70,6 @@ func NewServer(config Config, metricsReg MetricsRegistry) (Server, error) {
return Server{}, err
}

// ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
// defer cancel()
ctx := context.Background()
provider, err := oidc.NewProvider(ctx, config.OAuth2.RealmsURL)
if err != nil {
Expand All @@ -93,7 +91,7 @@ func NewServer(config Config, metricsReg MetricsRegistry) (Server, error) {
oauth2Conf: oauth2.Config{
ClientID: config.OAuth2.ClientID,
ClientSecret: config.OAuth2.ClientSecret,
RedirectURL: config.OAuth2.RedirectURL,
RedirectURL: config.OAuth2.RedirectURL + "/login/callback",
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
},
Expand Down Expand Up @@ -122,6 +120,7 @@ func NewServer(config Config, metricsReg MetricsRegistry) (Server, error) {
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(config.StaticDir))))
r.HandleFunc("/login", s.HandleLogin)
r.HandleFunc("/login/callback", s.HandleLoginCallback)
r.HandleFunc("/logout", s.HandleLogout)

return s, nil
}
Expand Down Expand Up @@ -204,63 +203,68 @@ type IndexFridayData struct {
type PageData struct {
FridayTimes []IndexFridayData
Name string
LoggedIn bool
LogoutURL string
}

func (s *Server) HandleIndex(w http.ResponseWriter, r *http.Request) {
s.indexGetMetric.Increment()

plate, err := template.ParseFiles(path.Join(s.config.StaticDir, "html/index.html"))
if err != nil {
Log.Error("template index failure", zap.Error(err))
s.Handle500(w, r)
return
}
data := PageData{
LoggedIn: false,
}

var claims *TokenClaims
for _, cookie := range r.Cookies() {
if cookie.Name == "session" {
var ok bool
claims, ok = s.sessions[cookie.Value]
// either bad session or auth has expired
if !ok || time.Now().After(time.Unix(claims.Exp, 0)) {
Log.Debug("cookie session invalid or expired")
delete(s.sessions, cookie.Value)
http.Redirect(w, r, "/login", http.StatusFound)
return
break
}
}
}
if claims == nil {
http.Redirect(w, r, "/login", http.StatusFound)
return
}

Log.Info("welcome", zap.String("name", claims.Name))

plate, err := template.ParseFiles(path.Join(s.config.StaticDir, "html/index.html"))
if err != nil {
Log.Error("template index failure", zap.Error(err))
s.Handle500(w, r)
return
}
data := PageData{
Name: claims.GivenName,
}
Log.Debug("no login cookies found")
} else {
data.LoggedIn = true

fridays, err := s.store.GetUpcomingFridays(30)
if err != nil {
Log.Error("failed to get fridays", zap.Error(err))
s.Handle500(w, r)
return
}
Log.Info("welcome", zap.String("name", claims.Name))
data.Name = claims.GivenName
data.LogoutURL = fmt.Sprintf("%s/%s?post_logout_redirect_uri=%s/logout&client_id=%s", s.oauth2Conf.Endpoint.AuthURL, "../logout", s.config.OAuth2.RedirectURL, "pizza")

estZone, _ := time.LoadLocation("America/New_York")
data.FridayTimes = make([]IndexFridayData, len(fridays))
for i, t := range fridays {
t = t.In(estZone)
data.FridayTimes[i].Date = t.Format(time.RFC822)
data.FridayTimes[i].ID = t.Unix()
fridays, err := s.store.GetUpcomingFridays(30)
if err != nil {
Log.Error("failed to get fridays", zap.Error(err))
s.Handle500(w, r)
return
}

eventID := strconv.FormatInt(data.FridayTimes[i].ID, 10)
if event, err := s.calendar.GetEvent(eventID); err != nil && err != ErrEventNotFound {
Log.Warn("failed to get calendar event", zap.Error(err), zap.String("eventID", eventID))
data.FridayTimes[i].Guests = make([]int, 0)
} else if err != nil {
data.FridayTimes[i].Guests = make([]int, 0)
} else {
data.FridayTimes[i].Guests = make([]int, len(event.Attendees))
estZone, _ := time.LoadLocation("America/New_York")
data.FridayTimes = make([]IndexFridayData, len(fridays))
for i, t := range fridays {
t = t.In(estZone)
data.FridayTimes[i].Date = t.Format(time.RFC822)
data.FridayTimes[i].ID = t.Unix()

eventID := strconv.FormatInt(data.FridayTimes[i].ID, 10)
if event, err := s.calendar.GetEvent(eventID); err != nil && err != ErrEventNotFound {
Log.Warn("failed to get calendar event", zap.Error(err), zap.String("eventID", eventID))
data.FridayTimes[i].Guests = make([]int, 0)
} else if err != nil {
data.FridayTimes[i].Guests = make([]int, 0)
} else {
data.FridayTimes[i].Guests = make([]int, len(event.Attendees))
}
}
}

Expand All @@ -273,6 +277,25 @@ func (s *Server) HandleIndex(w http.ResponseWriter, r *http.Request) {

func (s *Server) HandleSubmit(w http.ResponseWriter, r *http.Request) {
s.submitPostMetric.Increment()

var claims *TokenClaims
for _, cookie := range r.Cookies() {
if cookie.Name == "session" {
var ok bool
claims, ok = s.sessions[cookie.Value]
// either bad session or auth has expired
if !ok || time.Now().After(time.Unix(claims.Exp, 0)) {
delete(s.sessions, cookie.Value)
http.Redirect(w, r, "/login", http.StatusFound)
return
}
}
}
if claims == nil {
http.Redirect(w, r, "/login", http.StatusFound)
return
}

plate, err := template.ParseFiles(path.Join(s.config.StaticDir, "html/submit.html"))
if err != nil {
Log.Error("template submit failure", zap.Error(err))
Expand All @@ -289,12 +312,7 @@ func (s *Server) HandleSubmit(w http.ResponseWriter, r *http.Request) {
s.Handle4xx(w, r)
return
}
email := form.Get("email")
if len(email) == 0 {
s.Handle4xx(w, r)
return
}
email = strings.ToLower(email)
email := strings.ToLower(claims.Email)
Log.Debug("rsvp request", zap.String("email", email), zap.Strings("dates", dates))

if ok, err := s.store.IsFriendAllowed(email); !ok {
Expand Down Expand Up @@ -458,7 +476,10 @@ func (s *Server) HandleLoginCallback(w http.ResponseWriter, r *http.Request) {
Path: "/",
Expires: time.Now().AddDate(0, 0, 10),
HttpOnly: true,
SameSite: http.SameSiteNoneMode,
}
if strings.HasPrefix(s.config.OAuth2.RedirectURL, "https") {
cookie.Secure = true
cookie.SameSite = http.SameSiteNoneMode
}
if err := cookie.Valid(); err != nil {
Log.Warn("bad cookie", zap.Error(err))
Expand All @@ -470,6 +491,27 @@ func (s *Server) HandleLoginCallback(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusSeeOther)
}

func (s *Server) HandleLogout(w http.ResponseWriter, r *http.Request) {
for _, cookie := range r.Cookies() {
if cookie.Name == "session" {
delete(s.sessions, cookie.Value)
}
}

plate, err := template.ParseFiles(path.Join(s.config.StaticDir, "html/logout.html"))
if err != nil {
Log.Error("template submit failure", zap.Error(err))
s.Handle500(w, r)
return
}

if err = plate.Execute(w, nil); err != nil {
Log.Error("template execution failure", zap.Error(err))
s.Handle500(w, r)
return
}
}

type WrappedPageData struct {
Email string
Name string
Expand Down
1 change: 1 addition & 0 deletions static/html/4xx.html
@@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>

<head>
Expand Down
1 change: 1 addition & 0 deletions static/html/500.html
@@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>

<head>
Expand Down
12 changes: 9 additions & 3 deletions static/html/index.html
Expand Up @@ -9,6 +9,7 @@
<body>
<h2>RSVP For Pizza</h2>

{{if .LoggedIn}}
<p>Hi {{.Name}}</p>

<form method="get" action="/submit">
Expand All @@ -19,14 +20,19 @@ <h2>RSVP For Pizza</h2>
{{else}}
<p>There are no upcoming pizza fridays.</p>
{{end}}
<label for="email">Email</label>
<input type="text" id="email" name="email" />
<br>
<div id="submit">
<input type="submit" value="Submit">
</div>
</form>

<br><br>
<a href="{{.LogoutURL}}">logout</a>

{{else}}
<a href="/login">login</a>
{{end}}

</body>

</html>
</html>
19 changes: 19 additions & 0 deletions static/html/logout.html
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>

<head>
<link rel="stylesheet" href="/static/css/index.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
<h2>RSVP For Pizza</h2>

<p>Good bye</p>
<br><br>
<a href="/">pizza</a> |
<a href="/login">login</a>

</body>

</html>
1 change: 1 addition & 0 deletions static/html/submit.html
@@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>

<head>
Expand Down
1 change: 1 addition & 0 deletions static/html/wrapped.html
@@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>

<head>
Expand Down

0 comments on commit 872750f

Please sign in to comment.