package main import ( "fmt" "log" "net/http" "net/url" "os" "p83.nl/go/indieauth" ) const ClientID = "https://p83.nl/track-me" type IndieAuthHandler struct { } func performIndieauthCallback(state, code string, sess *Session) (bool, *indieauth.AuthResponse, error) { if state != sess.State { return false, &indieauth.AuthResponse{}, fmt.Errorf("mismatched state %q != %q", state, sess.State) } return indieauth.VerifyAuthCode(ClientID, code, sess.RedirectURI, sess.AuthorizationEndpoint) } func (h *IndieAuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { sess, err := NewSession(w, r) if err != nil { http.Error(w, err.Error(), 500) return } defer sess.Flush() if sess.LoggedIn { http.Redirect(w, r, "/", 302) return } if r.Method == http.MethodGet { if r.URL.Path == "" { fmt.Fprint(w, ` Login - Track Me

Login in with your own website

`) return } else if r.URL.Path == "callback" { state := r.URL.Query().Get("state") code := r.URL.Query().Get("code") verified, authResponse, err := performIndieauthCallback(state, code, sess) if err != nil { fmt.Fprintf(w, "ERROR: %q\n", err) return } if verified { sess.Me = authResponse.Me sess.LoggedIn = true nextURI := "/" if sess.NextURI != "" { nextURI = sess.NextURI } http.Redirect(w, r, nextURI, 302) return } return } else if r.URL.Path == "logout" { cleanupSession(sess) http.Redirect(w, r, "/", 302) return } } else if r.Method == http.MethodPost { r.ParseForm() if r.URL.Path == "login" { // redirect to endpoint me := r.Form.Get("url") meURL, err := url.Parse(me) if err != nil { http.Error(w, fmt.Sprintf("Bad Request: %s, %s", err.Error(), me), 400) return } endpoints, err := indieauth.GetEndpoints(meURL) if err != nil { http.Error(w, fmt.Sprintf("Bad Request: while getting endpoints for %s: %s", me, err.Error()), 400) return } log.Println(endpoints) authURL, err := url.Parse(endpoints.AuthorizationEndpoint) if err != nil { http.Error(w, fmt.Sprintf("Bad Request: %s %s", err.Error(), me), 400) return } log.Println(authURL) state := RandStringBytes(16) redirectURI := fmt.Sprintf("%s/auth/callback", os.Getenv("TRACKME_HOST")) sess.AuthorizationEndpoint = endpoints.AuthorizationEndpoint sess.Me = meURL.String() sess.State = state sess.RedirectURI = redirectURI sess.LoggedIn = false authenticationURL := indieauth.CreateAuthenticationURL(*authURL, meURL.String(), ClientID, redirectURI, state) http.Redirect(w, r, authenticationURL, 302) return } } } func cleanupSession(sess *Session) { sess.LoggedIn = false sess.State = "" sess.Me = "" sess.AuthorizationEndpoint = "" sess.NextURI = "" sess.RedirectURI = "" }