Fix passkey authentication

Also prep for better router layout
This commit is contained in:
Melody Becker 2024-10-15 16:16:18 +02:00
parent e2260e4a0f
commit b9eb4234f4
11 changed files with 289 additions and 21 deletions

16
server/constants.go Normal file
View file

@ -0,0 +1,16 @@
package server
const ContextKeyPasskeyUsername = "context-passkey-username"
type ContextKey string
const (
ContextKeyStorage ContextKey = "Context key for storage"
ContextKeyActorId ContextKey = "Context key for actor id"
)
const (
HttpErrIdPlaceholder = iota
HttpErrIdMissingContextValue
HttpErrIdDbFailure
)

22
server/contextUtils.go Normal file
View file

@ -0,0 +1,22 @@
package server
import (
"net/http"
"gitlab.com/mstarongitlab/goutils/other"
"gitlab.com/mstarongitlab/linstrom/storage"
)
func StorageFromRequest(w http.ResponseWriter, r *http.Request) *storage.Storage {
store, ok := r.Context().Value(ContextKeyStorage).(*storage.Storage)
if !ok {
other.HttpErr(
w,
HttpErrIdMissingContextValue,
"Missing storage reference",
http.StatusInternalServerError,
)
return nil
}
return store
}

14
server/frontend.go Normal file
View file

@ -0,0 +1,14 @@
package server
import (
"io/fs"
"net/http"
)
func setupFrontendRouter(interactiveFs, noscriptFs fs.FS) http.Handler {
router := http.NewServeMux()
router.Handle("/noscript/", http.StripPrefix("/noscript", http.FileServerFS(noscriptFs)))
router.Handle("/", http.FileServerFS(interactiveFs))
return router
}

View file

@ -8,6 +8,7 @@ import (
"github.com/rs/zerolog/hlog"
"github.com/rs/zerolog/log"
"gitlab.com/mstarongitlab/goutils/other"
)
type HandlerBuilder func(http.Handler) http.Handler
@ -51,3 +52,36 @@ func LoggingMiddleware(handler http.Handler) http.Handler {
hlog.RequestIDHandler("req_id", "Request-Id"),
)
}
func passkeyIdToAccountIdTransformerMiddleware(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s := StorageFromRequest(w, r)
if s == nil {
return
}
log := hlog.FromRequest(r)
passkeyId, ok := r.Context().Value(ContextKeyPasskeyUsername).(string)
if !ok {
other.HttpErr(
w,
HttpErrIdMissingContextValue,
"Actor name missing",
http.StatusInternalServerError,
)
return
}
log.Debug().Bytes("passkey-bytes", []byte(passkeyId)).Msg("Id from passkey auth")
acc, err := s.FindAccountByPasskeyId([]byte(passkeyId))
if err != nil {
other.HttpErr(
w,
HttpErrIdDbFailure,
"Failed to get account from storage",
http.StatusInternalServerError,
)
return
}
r = r.WithContext(context.WithValue(r.Context(), ContextKeyActorId, acc.ID))
handler.ServeHTTP(w, r)
})
}

View file

@ -4,6 +4,7 @@ import (
"fmt"
"io/fs"
"net/http"
"net/url"
"github.com/mstarongithub/passkey"
"github.com/rs/zerolog/log"
@ -17,7 +18,9 @@ type Server struct {
func NewServer(store *storage.Storage, pkey *passkey.Passkey, reactiveFS, staticFS fs.FS) *Server {
handler := buildRootHandler(pkey, reactiveFS, staticFS)
handler = ChainMiddlewares(handler, LoggingMiddleware, ContextValsMiddleware(map[any]any{}))
handler = ChainMiddlewares(handler, LoggingMiddleware, ContextValsMiddleware(map[any]any{
ContextKeyStorage: store,
}))
return &Server{
store: store,
router: handler,
@ -27,15 +30,22 @@ func NewServer(store *storage.Storage, pkey *passkey.Passkey, reactiveFS, static
func buildRootHandler(pkey *passkey.Passkey, reactiveFS, staticFS fs.FS) http.Handler {
mux := http.NewServeMux()
pkey.MountRoutes(mux, "/webauthn/")
mux.Handle("/", http.FileServerFS(reactiveFS))
mux.Handle("/nojs/", http.StripPrefix("/nojs", http.FileServerFS(staticFS)))
mux.Handle("/", setupFrontendRouter(reactiveFS, staticFS))
mux.Handle("/pk/", http.StripPrefix("/pk", http.FileServer(http.Dir("pk-auth"))))
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, true) })
mux.Handle(
"/authonly/",
pkey.Auth(
ContextKeyPasskeyUsername,
nil,
passkey.RedirectUnauthorized(url.URL{Path: "/"}),
)(ChainMiddlewares(setupTestEndpoints(), passkeyIdToAccountIdTransformerMiddleware)),
)
return mux
}
func (s *Server) Start(addr string) {
func (s *Server) Start(addr string) error {
log.Info().Str("addr", addr).Msg("Starting server")
http.ListenAndServe(addr, s.router)
return http.ListenAndServe(addr, s.router)
}

View file

@ -0,0 +1,16 @@
package server
import (
"fmt"
"net/http"
)
func setupTestEndpoints() http.Handler {
router := http.NewServeMux()
router.HandleFunc(
"/",
func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test root") },
)
return router
}