package oauth2client import ( "crypto/tls" "errors" "local/oauth2" "net/http" "net/url" "strconv" "strings" "time" ) type cached struct { access string exp time.Time } var cache = map[string]cached{} func Authenticate(server, scope string, w http.ResponseWriter, r *http.Request) error { oauth2server, err := url.Parse(server) if err != nil { return err } access, exists := findAccess(w, r) if !exists { return login(oauth2server, scope, w, r) } return verify(access, oauth2server, scope, w, r) } func findAccess(w http.ResponseWriter, r *http.Request) (string, bool) { fresh, exists := findAccessFresh(w, r) if exists { return fresh, true } stable, exists := findAccessStable(w, r) return stable, exists } func findAccessFresh(w http.ResponseWriter, r *http.Request) (string, bool) { q := r.URL.Query() access := q.Get(oauth2.COOKIE) q.Del(oauth2.COOKIE) r.URL.RawQuery = q.Encode() if access == "" { return "", false } cookie := &http.Cookie{ Name: oauth2.COOKIE, Value: access, SameSite: http.SameSiteLaxMode, Path: "/", } http.SetCookie(w, cookie) return access, true } func findAccessStable(w http.ResponseWriter, r *http.Request) (string, bool) { access, err := r.Cookie(oauth2.COOKIE) if err == http.ErrNoCookie { return "", false } return access.Value, true } func login(oauth2server *url.URL, scope string, w http.ResponseWriter, r *http.Request) error { oauth2server.Path = "/users/log/" + scope url := *r.URL url.Host = r.Host if url.Scheme == "" { url.Scheme = oauth2server.Scheme } if url.Scheme == "" { url.Scheme = "https" } q := oauth2server.Query() q.Set(oauth2.REDIRECT, url.String()) oauth2server.RawQuery = q.Encode() http.Redirect(w, r, oauth2server.String(), http.StatusSeeOther) return errors.New("logging in") } func verify(access string, oauth2server *url.URL, scope string, w http.ResponseWriter, r *http.Request) error { if v, ok := cache[scope]; ok && v.access == access && time.Now().Before(v.exp) { return nil } oauth2server.Path = "/verify/" + scope data := url.Values{} data.Set("access", access) req, err := http.NewRequest("POST", oauth2server.String(), strings.NewReader(data.Encode())) if err != nil { return err } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode()))) c := &http.Client{ Timeout: 5 * time.Second, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, } resp, err := c.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return login(oauth2server, scope, w, r) } cache[scope] = cached{ access: access, exp: time.Now().Add(time.Minute), } return nil } func setCookie(access string, w http.ResponseWriter) { cookie := &http.Cookie{ Name: oauth2.COOKIE, Value: access, SameSite: http.SameSiteLaxMode, Path: "/", } if access == "" { cookie.Expires = time.Now().Add(-1 * time.Hour) } http.SetCookie(w, cookie) }