From 8a347409c88bc8af205abfc646f5cfadedbc327d Mon Sep 17 00:00:00 2001 From: tjblackheart Date: Fri, 31 Jul 2026 09:40:50 +0200 Subject: [PATCH 1/3] chore: fix non static demo secret --- tools/srv/main.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tools/srv/main.go b/tools/srv/main.go index f4a65e5..8675a95 100644 --- a/tools/srv/main.go +++ b/tools/srv/main.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "encoding/base32" "encoding/base64" "encoding/gob" "fmt" @@ -116,7 +117,6 @@ func getLogin(w http.ResponseWriter, r *http.Request) { } func postLogin(w http.ResponseWriter, r *http.Request) { - u, err := store.Find(r.PostFormValue("username")) if err != nil { log.Println(err) @@ -189,12 +189,24 @@ func createOTP(w http.ResponseWriter, r *http.Request) { return } + var secret []byte + if u.Secret != "" { + var err error + if secret, err = base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(u.Secret); err != nil { + log.Println("otp:", err) + session.Put(r.Context(), "flash", &flash{err.Error(), "danger"}) + http.Redirect(w, r, "/otp/new", http.StatusFound) + return + } + } + opts := totp.GenerateOpts{ Issuer: issuer, Algorithm: otpAlg, AccountName: u.Username, SecretSize: otpSecretSize, Digits: otp.DigitsSix, + Secret: secret, } otp, err := totp.Generate(opts) @@ -206,6 +218,7 @@ func createOTP(w http.ResponseWriter, r *http.Request) { } u.Secret = otp.Secret() + if err := store.Update(u); err != nil { log.Println(err) session.Put(r.Context(), "flash", &flash{err.Error(), "danger"}) @@ -255,7 +268,13 @@ func postOTP(w http.ResponseWriter, r *http.Request) { return } - opts := totp.ValidateOpts{Algorithm: otpAlg, Digits: otpLen} + opts := totp.ValidateOpts{ + Algorithm: otpAlg, + Digits: otpLen, + Period: 30, + Skew: 1, + } + if valid, err := totp.ValidateCustom(token, user.Secret, time.Now().UTC(), opts); !valid || err != nil { if err != nil { log.Println("otp: validate:", err) From 02748e460a05c09d39cce88e4fe020a4f1759469 Mon Sep 17 00:00:00 2001 From: tjblackheart Date: Fri, 31 Jul 2026 10:16:29 +0200 Subject: [PATCH 2/3] refactor: migrate stick to stdlib html/template --- .../templates/{base.html.twig => base.gohtml} | 8 +++- .../srv/assets/templates/blocks/flash.gohtml | 7 ++++ .../assets/templates/blocks/flash.html.twig | 1 - ...2fa-create.html.twig => 2fa-create.gohtml} | 11 ++--- .../pages/{2fa.html.twig => 2fa.gohtml} | 8 ++-- .../pages/{login.html.twig => login.gohtml} | 8 ++-- .../{register.html.twig => register.gohtml} | 8 ++-- tools/srv/assets/templates/pages/user.gohtml | 6 +++ .../srv/assets/templates/pages/user.html.twig | 8 ---- tools/srv/go.mod | 6 +-- tools/srv/go.sum | 26 ------------ tools/srv/main.go | 42 +++++++++++++++---- 12 files changed, 66 insertions(+), 73 deletions(-) rename tools/srv/assets/templates/{base.html.twig => base.gohtml} (82%) create mode 100644 tools/srv/assets/templates/blocks/flash.gohtml delete mode 100644 tools/srv/assets/templates/blocks/flash.html.twig rename tools/srv/assets/templates/pages/{2fa-create.html.twig => 2fa-create.gohtml} (50%) rename tools/srv/assets/templates/pages/{2fa.html.twig => 2fa.gohtml} (65%) rename tools/srv/assets/templates/pages/{login.html.twig => login.gohtml} (76%) rename tools/srv/assets/templates/pages/{register.html.twig => register.gohtml} (76%) create mode 100644 tools/srv/assets/templates/pages/user.gohtml delete mode 100644 tools/srv/assets/templates/pages/user.html.twig diff --git a/tools/srv/assets/templates/base.html.twig b/tools/srv/assets/templates/base.gohtml similarity index 82% rename from tools/srv/assets/templates/base.html.twig rename to tools/srv/assets/templates/base.gohtml index 9740375..1bd0417 100644 --- a/tools/srv/assets/templates/base.html.twig +++ b/tools/srv/assets/templates/base.gohtml @@ -1,3 +1,4 @@ +{{ define "base" }} @@ -9,7 +10,7 @@ -
{% block body %}{% endblock %}
+
+ {{ template "body" . }} +
+{{ end }} diff --git a/tools/srv/assets/templates/blocks/flash.gohtml b/tools/srv/assets/templates/blocks/flash.gohtml new file mode 100644 index 0000000..df7cefd --- /dev/null +++ b/tools/srv/assets/templates/blocks/flash.gohtml @@ -0,0 +1,7 @@ +{{ define "flash" }} + {{ if .flash}} +
+ {{ .flash.Message }} +
+ {{ end }} +{{ end }} diff --git a/tools/srv/assets/templates/blocks/flash.html.twig b/tools/srv/assets/templates/blocks/flash.html.twig deleted file mode 100644 index ec6548a..0000000 --- a/tools/srv/assets/templates/blocks/flash.html.twig +++ /dev/null @@ -1 +0,0 @@ -{% if flash.Message %}
{{ flash.Message }}
{% endif %} diff --git a/tools/srv/assets/templates/pages/2fa-create.html.twig b/tools/srv/assets/templates/pages/2fa-create.gohtml similarity index 50% rename from tools/srv/assets/templates/pages/2fa-create.html.twig rename to tools/srv/assets/templates/pages/2fa-create.gohtml index eeae583..767cae8 100644 --- a/tools/srv/assets/templates/pages/2fa-create.html.twig +++ b/tools/srv/assets/templates/pages/2fa-create.gohtml @@ -1,16 +1,13 @@ -{% extends "base.html.twig" %} - -{% block body %} +{{ define "body" }}

Create OTP


-{% include "blocks/flash.html.twig" %} +{{ template "flash" . }}
-
qr
+
qr
- -{% endblock %} +{{ end }} diff --git a/tools/srv/assets/templates/pages/2fa.html.twig b/tools/srv/assets/templates/pages/2fa.gohtml similarity index 65% rename from tools/srv/assets/templates/pages/2fa.html.twig rename to tools/srv/assets/templates/pages/2fa.gohtml index 79b5dda..f6ab007 100644 --- a/tools/srv/assets/templates/pages/2fa.html.twig +++ b/tools/srv/assets/templates/pages/2fa.gohtml @@ -1,14 +1,12 @@ -{% extends "base.html.twig" %} - -{% block body %} +{{ define "body" }}

Token


-{% include "blocks/flash.html.twig" %} +{{ template "flash" . }}
-{% endblock %} +{{ end }} diff --git a/tools/srv/assets/templates/pages/login.html.twig b/tools/srv/assets/templates/pages/login.gohtml similarity index 76% rename from tools/srv/assets/templates/pages/login.html.twig rename to tools/srv/assets/templates/pages/login.gohtml index a00feb5..a360c8c 100644 --- a/tools/srv/assets/templates/pages/login.html.twig +++ b/tools/srv/assets/templates/pages/login.gohtml @@ -1,10 +1,8 @@ -{% extends "base.html.twig" %} - -{% block body %} +{{ define "body" }}

Login


-{% include "blocks/flash.html.twig" %} +{{ template "flash" . }}
@@ -16,4 +14,4 @@
-{% endblock %} +{{ end }} diff --git a/tools/srv/assets/templates/pages/register.html.twig b/tools/srv/assets/templates/pages/register.gohtml similarity index 76% rename from tools/srv/assets/templates/pages/register.html.twig rename to tools/srv/assets/templates/pages/register.gohtml index a05b895..7a4a115 100644 --- a/tools/srv/assets/templates/pages/register.html.twig +++ b/tools/srv/assets/templates/pages/register.gohtml @@ -1,10 +1,8 @@ -{% extends "base.html.twig" %} - -{% block body %} +{{ define "body" }}

Register account


-{% include "blocks/flash.html.twig" %} +{{ template "flash" . }}
@@ -16,4 +14,4 @@
-{% endblock %} +{{ end }} diff --git a/tools/srv/assets/templates/pages/user.gohtml b/tools/srv/assets/templates/pages/user.gohtml new file mode 100644 index 0000000..f8167e0 --- /dev/null +++ b/tools/srv/assets/templates/pages/user.gohtml @@ -0,0 +1,6 @@ +{{ define "body" }} +

User area

+ {{ template "flash" . }} + Hello {{ .user.Username }}! + Logout +{{ end }} diff --git a/tools/srv/assets/templates/pages/user.html.twig b/tools/srv/assets/templates/pages/user.html.twig deleted file mode 100644 index 83e8a38..0000000 --- a/tools/srv/assets/templates/pages/user.html.twig +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "base.html.twig" %} - -{% block body %} -

User area

- {% include "blocks/flash.html.twig" %} - Hello {{ user.Username }}! - Logout -{% endblock %} diff --git a/tools/srv/go.mod b/tools/srv/go.mod index 64f9cfe..8688979 100644 --- a/tools/srv/go.mod +++ b/tools/srv/go.mod @@ -6,11 +6,7 @@ require ( github.com/alexedwards/scs/v2 v2.9.0 github.com/go-chi/chi/v5 v5.3.1 github.com/pquerna/otp v1.5.0 - github.com/tyler-sommer/stick v1.0.10 golang.org/x/crypto v0.54.0 ) -require ( - github.com/boombuler/barcode v1.1.0 // indirect - github.com/shopspring/decimal v1.4.0 // indirect -) +require github.com/boombuler/barcode v1.1.0 // indirect diff --git a/tools/srv/go.sum b/tools/srv/go.sum index 5eccdc2..0152f13 100644 --- a/tools/srv/go.sum +++ b/tools/srv/go.sum @@ -1,44 +1,18 @@ -github.com/alexedwards/scs/v2 v2.8.0 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw= -github.com/alexedwards/scs/v2 v2.8.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8= github.com/alexedwards/scs/v2 v2.9.0 h1:xa05mVpwTBm1iLeTMNFfAWpKUm4fXAW7CeAViqBVS90= github.com/alexedwards/scs/v2 v2.9.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4= -github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo= github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= -github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= -github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -github.com/go-chi/chi/v5 v5.3.0 h1:halUjDxhshgXHMrao5bB8eNBXo/rnzwr8m5m36glehM= -github.com/go-chi/chi/v5 v5.3.0/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= github.com/go-chi/chi/v5 v5.3.1 h1:3j4HZLGZQ3JpMCrPJF/Jl3mYJfWLKBfNJ6quurUGCf8= github.com/go-chi/chi/v5 v5.3.1/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= 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/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= -github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/tyler-sommer/stick v1.0.6 h1:LLdJ8oGotXCsAuVx2BRZZT6s3bYXHe0ImQ+azF4HtJg= -github.com/tyler-sommer/stick v1.0.6/go.mod h1:rjBy3zi6GwoxExa6OSRPPPaLqUEKNsBxTeWckhIX1us= -github.com/tyler-sommer/stick v1.0.10 h1:VlXTvaxXnOtfkoTxqy+FWRM0OdN7sGX8Nfh8t2I1GSI= -github.com/tyler-sommer/stick v1.0.10/go.mod h1:CYdsPuvxGEU32yi4ZTDul+55R8oS8/aHuWSppEq01B8= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= -golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= diff --git a/tools/srv/main.go b/tools/srv/main.go index 8675a95..0543d58 100644 --- a/tools/srv/main.go +++ b/tools/srv/main.go @@ -2,10 +2,12 @@ package main import ( "bytes" + "embed" "encoding/base32" "encoding/base64" "encoding/gob" "fmt" + "html/template" "image/png" "log" "net/http" @@ -18,7 +20,6 @@ import ( "github.com/go-chi/chi/v5/middleware" "github.com/pquerna/otp" "github.com/pquerna/otp/totp" - "github.com/tyler-sommer/stick" "golang.org/x/crypto/bcrypt" ) @@ -40,7 +41,7 @@ type ( flash struct{ Message, Type string } - m map[string]stick.Value + m map[string]any db struct { *sync.Mutex @@ -49,11 +50,31 @@ type ( ) var ( - env = stick.New(stick.NewFilesystemLoader("./assets/templates")) + //go:embed assets/templates + tplFS embed.FS + pages = map[string]*template.Template{} store = &db{new(sync.Mutex), make(map[string]*user)} session = scs.New() ) +func init() { + for _, p := range []string{"login", "register", "2fa", "2fa-create", "user"} { + pages[p] = template.Must(template.ParseFS( + tplFS, + "assets/templates/base.gohtml", + "assets/templates/blocks/flash.gohtml", + "assets/templates/pages/"+p+".gohtml", + )) + } +} + +func render(w http.ResponseWriter, page string, data m) { + if err := pages[page].ExecuteTemplate(w, "base", data); err != nil { + log.Println("render:", err) + http.Error(w, "internal error", 500) + } +} + /// app func main() { @@ -113,7 +134,7 @@ func getLogin(w http.ResponseWriter, r *http.Request) { } flash, _ := session.Pop(r.Context(), "flash").(*flash) - env.Execute("pages/login.html.twig", w, m{"flash": flash}) + render(w, "login", m{"flash": flash}) } func postLogin(w http.ResponseWriter, r *http.Request) { @@ -143,7 +164,7 @@ func postLogin(w http.ResponseWriter, r *http.Request) { func getRegister(w http.ResponseWriter, r *http.Request) { flash, _ := session.Pop(r.Context(), "flash").(*flash) - env.Execute("pages/register.html.twig", w, m{"flash": flash}) + render(w, "register", m{"flash": flash}) } func postRegister(w http.ResponseWriter, r *http.Request) { @@ -241,8 +262,10 @@ func createOTP(w http.ResponseWriter, r *http.Request) { } flash, _ := session.Pop(r.Context(), "flash").(*flash) - env.Execute("pages/2fa-create.html.twig", w, m{ - "qr": base64.RawStdEncoding.EncodeToString(buf.Bytes()), + imgBytes := base64.RawStdEncoding.EncodeToString(buf.Bytes()) + + render(w, "2fa-create", m{ + "qr": template.URL("data:image/png;base64," + imgBytes), "flash": flash, }) } @@ -254,7 +277,8 @@ func getOTP(w http.ResponseWriter, r *http.Request) { } flash, _ := session.Pop(r.Context(), "flash").(*flash) - env.Execute("pages/2fa.html.twig", w, m{"flash": flash}) + + render(w, "2fa", m{"flash": flash}) } func postOTP(w http.ResponseWriter, r *http.Request) { @@ -299,7 +323,7 @@ func postOTP(w http.ResponseWriter, r *http.Request) { func getUser(w http.ResponseWriter, r *http.Request) { u, _ := session.Get(r.Context(), "user").(*user) - env.Execute("pages/user.html.twig", w, m{"user": u}) + render(w, "user", m{"user": u}) } func getLogout(w http.ResponseWriter, r *http.Request) { From 3e5a5bffd077c9f454df813cef53869e03be5f4f Mon Sep 17 00:00:00 2001 From: tjblackheart Date: Fri, 31 Jul 2026 10:36:33 +0200 Subject: [PATCH 3/3] chore: embed resources --- tools/srv/assets/templates/base.gohtml | 2 +- tools/srv/main.go | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/srv/assets/templates/base.gohtml b/tools/srv/assets/templates/base.gohtml index 1bd0417..e811482 100644 --- a/tools/srv/assets/templates/base.gohtml +++ b/tools/srv/assets/templates/base.gohtml @@ -6,7 +6,7 @@ 2fa demo - +