154 lines
3.5 KiB
Go
Executable File
154 lines
3.5 KiB
Go
Executable File
package oauth2client
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"gitea.inhome.blapointe.com/local/oauth2"
|
|
)
|
|
|
|
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(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) {
|
|
access, found := findAccessFreshQueryParam(w, r)
|
|
if !found {
|
|
access, found = findAccessFreshCookie(w, r)
|
|
}
|
|
if !found {
|
|
access, found = findAccessFreshBasicAuth(w, r)
|
|
}
|
|
if found {
|
|
setCookie(oauth2.COOKIE, access, "", w)
|
|
}
|
|
return access, found
|
|
}
|
|
|
|
func findAccessFreshBasicAuth(w http.ResponseWriter, r *http.Request) (string, bool) {
|
|
_, p, ok := r.BasicAuth()
|
|
return p, ok
|
|
}
|
|
|
|
func findAccessFreshQueryParam(w http.ResponseWriter, r *http.Request) (string, bool) {
|
|
q := r.URL.Query()
|
|
access := q.Get(oauth2.NEWCOOKIE)
|
|
q.Del(oauth2.NEWCOOKIE)
|
|
r.URL.RawQuery = q.Encode()
|
|
if access == "" {
|
|
return "", false
|
|
}
|
|
return access, true
|
|
}
|
|
|
|
func findAccessFreshCookie(w http.ResponseWriter, r *http.Request) (string, bool) {
|
|
access, err := r.Cookie(oauth2.NEWCOOKIE)
|
|
if err == http.ErrNoCookie {
|
|
return "", false
|
|
}
|
|
host := r.Host
|
|
if r.URL.Host != "" {
|
|
host = r.URL.Host
|
|
}
|
|
host = strings.Split(host, ":")[0]
|
|
hosts := strings.Split(host, ".")
|
|
if len(host) > 1 {
|
|
hosts = hosts[1:]
|
|
}
|
|
host = "." + strings.Join(hosts, ".")
|
|
setCookie(oauth2.NEWCOOKIE, "", host, w)
|
|
return access.Value, 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(scope string, w http.ResponseWriter, r *http.Request) error {
|
|
w.Header().Set("WWW-Authenticate", "Basic")
|
|
w.WriteHeader(403)
|
|
return errors.New("login pls")
|
|
}
|
|
|
|
var HTTPClient = &http.Client{
|
|
Timeout: 5 * time.Second,
|
|
Transport: &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
},
|
|
}
|
|
|
|
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 := HTTPClient
|
|
resp, err := c.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return login(scope, w, r)
|
|
}
|
|
cache[scope] = cached{
|
|
access: access,
|
|
exp: time.Now().Add(time.Minute),
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func setCookie(key, value, domain string, w http.ResponseWriter) {
|
|
cookie := &http.Cookie{
|
|
Name: key,
|
|
Value: value,
|
|
Path: "/",
|
|
Domain: domain,
|
|
}
|
|
if value == "" {
|
|
cookie.Expires = time.Now().Add(-1 * time.Hour)
|
|
}
|
|
http.SetCookie(w, cookie)
|
|
}
|