mirror of
https://github.com/boringproxy/boringproxy.git
synced 2026-11-04 13:17:30 -06:00
Modify login for browser forms instead of API
This commit is contained in:
1 parent
0cb3250711
commit
bae4eaf02d
2 files changed
+87
-97
No files matched your search
@@ -10,15 +10,19 @@ import (
|
||||
"math/big"
|
||||
"net/smtp"
|
||||
"sync"
|
||||
"time"
|
||||
//"time"
|
||||
)
|
||||
|
||||
type Auth struct {
|
||||
pendingRequests map[string]chan struct{}
|
||||
pendingRequests map[string]*LoginRequest
|
||||
sessions map[string]*Session
|
||||
mutex *sync.Mutex
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
@@ -39,7 +43,7 @@ func NewAuth() *Auth {
|
||||
sessions = make(map[string]*Session)
|
||||
}
|
||||
|
||||
pendingRequests := make(map[string]chan struct{})
|
||||
pendingRequests := make(map[string]*LoginRequest)
|
||||
mutex := &sync.Mutex{}
|
||||
|
||||
return &Auth{pendingRequests, sessions, mutex}
|
||||
@@ -57,20 +61,20 @@ func (a *Auth) Authorized(token string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Auth) Login(email string, config *BoringProxyConfig, canceled <-chan struct{}) (string, error) {
|
||||
func (a *Auth) Login(email string, config *BoringProxyConfig) (string, error) {
|
||||
|
||||
key, err := genRandomKey()
|
||||
if err != nil {
|
||||
return "", errors.New("Error generating key")
|
||||
}
|
||||
|
||||
link := fmt.Sprintf("https://%s/verify?key=%s", config.AdminDomain, key)
|
||||
link := fmt.Sprintf("https://%s/login?key=%s", config.AdminDomain, key)
|
||||
|
||||
bodyTemplate := "From: %s <%s>\r\n" +
|
||||
"To: %s\r\n" +
|
||||
"Subject: Email Verification\r\n" +
|
||||
"\r\n" +
|
||||
"This is an email verification request from %s. Please click the following link to complete the verification:\r\n" +
|
||||
"This is email verification request from %s. Please click the following link to complete the verification:\r\n" +
|
||||
"\r\n" +
|
||||
"%s\r\n"
|
||||
|
||||
@@ -86,58 +90,35 @@ func (a *Auth) Login(email string, config *BoringProxyConfig, canceled <-chan st
|
||||
return "", errors.New("Sending email failed. Probably a bad email address.")
|
||||
}
|
||||
|
||||
doneChan := make(chan struct{})
|
||||
|
||||
a.mutex.Lock()
|
||||
a.pendingRequests[key] = doneChan
|
||||
a.pendingRequests[key] = &LoginRequest{email}
|
||||
a.mutex.Unlock()
|
||||
|
||||
// Starting timeout here after sending the email, because sometimes it takes
|
||||
// a few seconds to send.
|
||||
timeout := time.After(time.Duration(60) * time.Second)
|
||||
|
||||
select {
|
||||
case <-doneChan:
|
||||
token, err := genRandomKey()
|
||||
if err != nil {
|
||||
return "", errors.New("Error generating key")
|
||||
}
|
||||
|
||||
a.mutex.Lock()
|
||||
a.sessions[token] = &Session{Id: email}
|
||||
a.mutex.Unlock()
|
||||
|
||||
saveJson(a.sessions, "sessions.json")
|
||||
|
||||
return token, nil
|
||||
case <-timeout:
|
||||
a.mutex.Lock()
|
||||
delete(a.pendingRequests, key)
|
||||
a.mutex.Unlock()
|
||||
return "", errors.New("Timeout")
|
||||
case <-canceled:
|
||||
a.mutex.Lock()
|
||||
delete(a.pendingRequests, key)
|
||||
a.mutex.Unlock()
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (a *Auth) Verify(key string) error {
|
||||
func (a *Auth) Verify(key string) (string, error) {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
doneChan, ok := a.pendingRequests[key]
|
||||
request, ok := a.pendingRequests[key]
|
||||
|
||||
if !ok {
|
||||
return errors.New("No pending request for that key. It may have expired.")
|
||||
return "", errors.New("No pending request for that key. It may have expired.")
|
||||
}
|
||||
|
||||
delete(a.pendingRequests, key)
|
||||
close(doneChan)
|
||||
|
||||
return nil
|
||||
token, err := genRandomKey()
|
||||
if err != nil {
|
||||
return "", errors.New("Error generating key")
|
||||
}
|
||||
|
||||
a.sessions[token] = &Session{Id: request.Email}
|
||||
|
||||
saveJson(a.sessions, "sessions.json")
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
const chars string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
+63
-54
@@ -98,13 +98,27 @@ func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request)
|
||||
switch r.URL.Path {
|
||||
case "/login":
|
||||
p.handleLogin(w, r)
|
||||
case "/verify":
|
||||
p.handleVerify(w, r)
|
||||
case "/":
|
||||
box, err := rice.FindBox("webui")
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
io.WriteString(w, "Error opening webui")
|
||||
return
|
||||
}
|
||||
|
||||
token, err := extractToken("access_token", r)
|
||||
if err != nil {
|
||||
|
||||
loginTemplate, err := box.String("login.tmpl")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(500)
|
||||
io.WriteString(w, "Error reading login.tmpl")
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(401)
|
||||
w.Write([]byte("No token provided"))
|
||||
io.WriteString(w, loginTemplate)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -114,12 +128,6 @@ func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
box, err := rice.FindBox("webui")
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
io.WriteString(w, "Error opening webui")
|
||||
return
|
||||
}
|
||||
|
||||
indexTemplate, err := box.String("index.tmpl")
|
||||
if err != nil {
|
||||
@@ -127,7 +135,8 @@ func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request)
|
||||
io.WriteString(w, "Error reading index.tmpl")
|
||||
return
|
||||
}
|
||||
fmt.Println(indexTemplate)
|
||||
|
||||
io.WriteString(w, indexTemplate)
|
||||
|
||||
case "/tunnels":
|
||||
|
||||
@@ -179,53 +188,53 @@ func (p *BoringProxy) handleTunnels(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (p *BoringProxy) handleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
//io.WriteString(w, "login")
|
||||
if r.Method != "POST" {
|
||||
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
query := r.URL.Query()
|
||||
key, exists := query["key"]
|
||||
|
||||
if !exists {
|
||||
w.WriteHeader(400)
|
||||
fmt.Fprintf(w, "Must provide key for verification")
|
||||
return
|
||||
}
|
||||
|
||||
token, err := p.auth.Verify(key[0])
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
fmt.Fprintf(w, "Invalid key")
|
||||
return
|
||||
}
|
||||
|
||||
cookie := &http.Cookie{Name: "access_token", Value: token, Secure: true, HttpOnly: true}
|
||||
http.SetCookie(w, cookie)
|
||||
|
||||
http.Redirect(w, r, "/", 307)
|
||||
|
||||
|
||||
case "POST":
|
||||
|
||||
r.ParseForm()
|
||||
|
||||
toEmail, ok := r.Form["email"]
|
||||
|
||||
if !ok {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte("Email required for login"))
|
||||
return
|
||||
}
|
||||
|
||||
// run in goroutine because it can take some time to send the
|
||||
// email
|
||||
go p.auth.Login(toEmail[0], p.config)
|
||||
|
||||
io.WriteString(w, "Check your email to finish logging in")
|
||||
default:
|
||||
w.WriteHeader(405)
|
||||
w.Write([]byte("Invalid method for login"))
|
||||
return
|
||||
}
|
||||
|
||||
query := r.URL.Query()
|
||||
|
||||
toEmail, ok := query["email"]
|
||||
|
||||
if !ok {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte("Email required for login"))
|
||||
return
|
||||
}
|
||||
|
||||
token, err := p.auth.Login(toEmail[0], p.config, r.Context().Done())
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
//w.Write(err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(token))
|
||||
}
|
||||
|
||||
func (p *BoringProxy) handleVerify(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
key, exists := query["key"]
|
||||
|
||||
if !exists {
|
||||
w.WriteHeader(400)
|
||||
fmt.Fprintf(w, "Must provide key for verification")
|
||||
return
|
||||
}
|
||||
|
||||
err := p.auth.Verify(key[0])
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(400)
|
||||
fmt.Fprintf(w, "Invalid key")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "Verification successful. You can close this tab and return to your original session.")
|
||||
}
|
||||
|
||||
func (p *BoringProxy) handleCreateTunnel(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in new issue
Block a user