Add QR code login URLs for tokens

Also set cookies to max age of a year rather than session cookies.
This commit is contained in:
Anders Pitman 2020-10-18 18:39:30 -06:00
parent 0df20b2e1e
commit 0e39e9dd57
5 changed files with 49 additions and 4 deletions

1
go.mod
View File

@ -5,5 +5,6 @@ go 1.15
require ( require (
github.com/GeertJohan/go.rice v1.0.0 github.com/GeertJohan/go.rice v1.0.0
github.com/caddyserver/certmagic v0.12.0 github.com/caddyserver/certmagic v0.12.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
) )

2
go.sum
View File

@ -33,6 +33,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=

View File

@ -1,8 +1,10 @@
package main package main
import ( import (
"encoding/base64"
"fmt" "fmt"
"github.com/GeertJohan/go.rice" "github.com/GeertJohan/go.rice"
qrcode "github.com/skip2/go-qrcode"
"html/template" "html/template"
"io" "io"
"log" "log"
@ -30,6 +32,7 @@ type IndexData struct {
Tokens map[string]TokenData Tokens map[string]TokenData
Users map[string]User Users map[string]User
IsAdmin bool IsAdmin bool
QrCodes map[string]template.URL
} }
type TunnelsData struct { type TunnelsData struct {
@ -205,6 +208,7 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
var tokens map[string]TokenData var tokens map[string]TokenData
var users map[string]User var users map[string]User
qrCodes := make(map[string]template.URL)
if user.IsAdmin { if user.IsAdmin {
tokens = h.db.GetTokens() tokens = h.db.GetTokens()
@ -216,6 +220,19 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
if tokenData.Owner == td.Owner { if tokenData.Owner == td.Owner {
tokens[token] = td tokens[token] = td
} }
loginUrl := fmt.Sprintf("https://%s/login?access_token=%s", h.config.WebUiDomain, token)
var png []byte
png, err := qrcode.Encode(loginUrl, qrcode.Medium, 256)
if err != nil {
w.WriteHeader(500)
h.alertDialog(w, r, err.Error(), "/#/tokens")
return
}
data := base64.StdEncoding.EncodeToString(png)
qrCodes[token] = template.URL("data:image/png;base64," + data)
} }
users = make(map[string]User) users = make(map[string]User)
@ -228,9 +245,15 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
Tokens: tokens, Tokens: tokens,
Users: users, Users: users,
IsAdmin: user.IsAdmin, IsAdmin: user.IsAdmin,
QrCodes: qrCodes,
} }
tmpl.Execute(w, indexData) err = tmpl.Execute(w, indexData)
if err != nil {
w.WriteHeader(500)
h.alertDialog(w, r, err.Error(), "/#/tokens")
return
}
case "/tunnels": case "/tunnels":
h.handleTunnels(w, r, tokenData) h.handleTunnels(w, r, tokenData)
@ -298,7 +321,12 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
tmpl.Execute(w, data) tmpl.Execute(w, data)
case "/logout": case "/logout":
cookie := &http.Cookie{Name: "access_token", Value: "", Secure: true, HttpOnly: true} cookie := &http.Cookie{
Name: "access_token",
Value: "",
Secure: true,
HttpOnly: true,
}
http.SetCookie(w, cookie) http.SetCookie(w, cookie)
http.Redirect(w, r, "/#/tunnels", 303) http.Redirect(w, r, "/#/tunnels", 303)
case "/loading": case "/loading":
@ -366,7 +394,13 @@ func (h *WebUiHandler) handleLogin(w http.ResponseWriter, r *http.Request) {
token := tokenList[0] token := tokenList[0]
if h.auth.Authorized(token) { if h.auth.Authorized(token) {
cookie := &http.Cookie{Name: "access_token", Value: token, Secure: true, HttpOnly: true} cookie := &http.Cookie{
Name: "access_token",
Value: token,
Secure: true,
HttpOnly: true,
MaxAge: 86400 * 365,
}
http.SetCookie(w, cookie) http.SetCookie(w, cookie)
http.Redirect(w, r, "/#/tunnels", 303) http.Redirect(w, r, "/#/tunnels", 303)
} else { } else {

View File

@ -79,8 +79,11 @@
<div class='content'> <div class='content'>
<div class='list'> <div class='list'>
{{range $token, $tokenData := .Tokens}} {{range $token, $tokenData := .Tokens}}
<div class='list-item'> <div class='list-item'>
<span class='token'>{{$token}}</span> ({{$tokenData.Owner}}) <span class='token'>{{$token}} ({{$tokenData.Owner}})</span>
<a href='/login?access_token={{$token}}'>Login link</a>
<img class='qr-code' src='{{index $.QrCodes $token}}' width=100 height=100>
<a href="/confirm-delete-token?token={{$token}}"> <a href="/confirm-delete-token?token={{$token}}">
<button class='button'>Delete</button> <button class='button'>Delete</button>
</a> </a>

View File

@ -141,6 +141,11 @@ main {
flex-direction: column; flex-direction: column;
} }
.qr-code {
width: 8em;
height: 8em;
}
main *:target { main *:target {
display: flex; display: flex;
} }