diff options
Diffstat (limited to '')
| -rw-r--r-- | internal/auth/handler.go | 105 |
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) +} |