Modify login for browser forms instead of API

This commit is contained in:
Anders Pitman committed 2020-09-29 22:03:34 -06:00
1 parent 0cb3250711
commit bae4eaf02d
2 files changed
+87 -97

No files matched your search

+24 -43
View File
@@ -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
View File
@@ -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) {