Replace email login with direct token login

This commit is contained in:
Anders Pitman 2020-10-11 15:18:33 -06:00
parent 1e25527ab2
commit 2ca7800ca6
3 changed files with 27 additions and 106 deletions

61
auth.go
View File

@ -2,10 +2,7 @@ package main
import ( import (
"crypto/rand" "crypto/rand"
"errors"
"fmt"
"math/big" "math/big"
"net/smtp"
"sync" "sync"
) )
@ -37,64 +34,6 @@ func (a *Auth) Authorized(token string) bool {
return false return false
} }
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/login?key=%s", config.WebUiDomain, key)
bodyTemplate := "From: %s <%s>\r\n" +
"To: %s\r\n" +
"Subject: Email Verification\r\n" +
"\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"
fromText := "boringproxy email verifier"
fromEmail := fmt.Sprintf("auth@%s", config.WebUiDomain)
emailBody := fmt.Sprintf(bodyTemplate, fromText, fromEmail, email, config.WebUiDomain, link)
emailAuth := smtp.PlainAuth("", config.Smtp.Username, config.Smtp.Password, config.Smtp.Server)
srv := fmt.Sprintf("%s:%d", config.Smtp.Server, config.Smtp.Port)
msg := []byte(emailBody)
err = smtp.SendMail(srv, emailAuth, fromEmail, []string{email}, msg)
if err != nil {
return "", errors.New("Sending email failed. Probably a bad email address.")
}
a.mutex.Lock()
a.pendingRequests[key] = &LoginRequest{email}
a.mutex.Unlock()
return "", nil
}
func (a *Auth) Verify(key string) (string, error) {
a.mutex.Lock()
defer a.mutex.Unlock()
request, ok := a.pendingRequests[key]
if !ok {
return "", errors.New("No pending request for that key. It may have expired.")
}
delete(a.pendingRequests, key)
token, err := genRandomKey()
if err != nil {
return "", errors.New("Error generating key")
}
a.db.SetTokenData(token, TokenData{Id: request.Email})
return token, nil
}
const chars string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" const chars string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func genRandomKey() (string, error) { func genRandomKey() (string, error) {

View File

@ -19,9 +19,9 @@
</head> </head>
<body> <body>
<form class='dialog' action="/login" method="post"> <form class='dialog' action="/login" method="GET">
<label for="mail">E-mail:</label> <label for="token">Token:</label>
<input type="email" id="mail" name="email"> <input type="password" id="token" name="token">
<button class='button green-button' type="submit">Login</button> <button class='button green-button' type="submit">Login</button>
</form> </form>
</body> </body>

View File

@ -226,51 +226,33 @@ func (h *WebUiHandler) handleTunnels(w http.ResponseWriter, r *http.Request) {
func (h *WebUiHandler) handleLogin(w http.ResponseWriter, r *http.Request) { func (h *WebUiHandler) handleLogin(w http.ResponseWriter, r *http.Request) {
switch r.Method { // Using GET requests to avoid form resubmission warnings in browsers
case "GET": if r.Method != "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 := h.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 h.auth.Login(toEmail[0], h.config)
io.WriteString(w, "Check your email to finish logging in")
default:
w.WriteHeader(405) w.WriteHeader(405)
w.Write([]byte("Invalid method for login")) w.Write([]byte("Invalid method for login"))
} }
r.ParseForm()
tokenList, ok := r.Form["token"]
if !ok {
w.WriteHeader(400)
w.Write([]byte("Token required for login"))
return
}
token := tokenList[0]
if h.auth.Authorized(token) {
cookie := &http.Cookie{Name: "access_token", Value: token, Secure: true, HttpOnly: true}
http.SetCookie(w, cookie)
http.Redirect(w, r, "/", 307)
} else {
w.WriteHeader(401)
w.Write([]byte("Invalid token"))
return
}
} }
func (h *WebUiHandler) handleCreateTunnel(w http.ResponseWriter, r *http.Request) { func (h *WebUiHandler) handleCreateTunnel(w http.ResponseWriter, r *http.Request) {