aboutsummaryrefslogtreecommitdiff
path: root/internal/auth/handler.go
diff options
context:
space:
mode:
authorMax Resnick <max@ofmax.li>2020-08-14 23:13:41 -0700
committerMax Resnick <max@ofmax.li>2020-11-08 07:57:13 -0800
commit689a57ec4a444f8233fe2e5ec7ceb0903218218d (patch)
tree1bcfe6786c38b4ae11997d5d97dc3c5fba747b97 /internal/auth/handler.go
parent77c2e6aca2dc0f851f55e30a0f49c9ee7c2c952e (diff)
downloadiserv-master.tar.gz
feat: working login gauthHEADmaster
Diffstat (limited to 'internal/auth/handler.go')
-rw-r--r--internal/auth/handler.go105
1 files changed, 105 insertions, 0 deletions
diff --git a/internal/auth/handler.go b/internal/auth/handler.go
new file mode 100644
index 0000000..992608c
--- /dev/null
+++ b/internal/auth/handler.go
@@ -0,0 +1,105 @@
+package auth
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "log"
+ "net/http"
+
+ "github.com/alexedwards/scs/v2"
+ "golang.org/x/oauth2"
+)
+
+var (
+ profileURL = "https://www.googleapis.com/oauth2/v3/userinfo"
+)
+
+// Handler authentication handler
+// handles, login, oauth and registration
+type Handler interface {
+ Login(w http.ResponseWriter, r *http.Request)
+ OauthCallback(w http.ResponseWriter, r *http.Request)
+}
+
+type authHandler struct {
+ service Servicer
+ ses *scs.SessionManager
+ oclient *oauth2.Config
+}
+
+// NewHandler create new instance of handler
+// Servicer, session, and oauth client required
+func NewHandler(service Servicer,
+ session *scs.SessionManager,
+ oclient *oauth2.Config) Handler {
+ return &authHandler{
+ service,
+ session,
+ oclient,
+ }
+}
+
+func (h *authHandler) Login(w http.ResponseWriter, r *http.Request) {
+ stateValue, err := h.service.GenerateStateToken()
+ if err != nil {
+ return
+ }
+ h.ses.Put(r.Context(), "state", stateValue)
+ url := h.oclient.AuthCodeURL(stateValue, oauth2.AccessTypeOnline)
+ http.Redirect(w, r, url, 302)
+}
+
+func (h *authHandler) OauthCallback(w http.ResponseWriter, r *http.Request) {
+ // this needs to be random
+ stateFromSession := h.ses.GetString(r.Context(), "state")
+ stateFromCallback := r.FormValue("state")
+ // prevent arbitrary authorization exchanges
+ if stateFromCallback != stateFromSession {
+ http.Error(w, "state value miss match bad data", 400)
+ return
+ }
+ stateValid, err := h.service.ValidateStateToken(stateFromCallback, stateFromSession)
+ if err != nil {
+ http.Error(w, "error validating", 400)
+ return
+ }
+ if !stateValid {
+ http.Error(w, "invalid tokens", 400)
+ return
+ }
+ // valid and same as state
+ code := r.FormValue("code")
+ token, err := h.oclient.Exchange(r.Context(), code)
+ if err != nil {
+ http.Error(w, err.Error(), 400)
+ return
+ }
+ log.Printf("returned token %v", token)
+ // google profile
+ client := h.oclient.Client(r.Context(), token)
+ resp, err := client.Get(profileURL)
+ if err != nil {
+ http.Error(w, err.Error(), 400)
+ return
+ }
+ defer resp.Body.Close()
+ data, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ http.Error(w, err.Error(), 400)
+ return
+ }
+ gp := &GoogleAuthProfile{}
+ err = json.Unmarshal(data, &gp)
+ profileID, newProfile, err := h.service.LoginOrRegisterSessionID(token, gp)
+ if err != nil {
+ http.Error(w, err.Error(), 400)
+ return
+ }
+ h.ses.Put(r.Context(), "profid", profileID)
+ // send to registration
+ if newProfile == true {
+ http.Redirect(w, r, "/account/register", 302)
+ return
+ }
+ http.Redirect(w, r, "/", 302)
+}