Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve(passkeys): improve passkey naming #1303

Merged
merged 5 commits into from
Jan 31, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions backend/Dockerfile
lfleischmann marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the hanko binary
FROM --platform=$BUILDPLATFORM golang:1.20 as builder
FROM --platform=$BUILDPLATFORM golang:1.20 AS builder

ARG TARGETARCH

@@ -28,16 +28,21 @@ COPY build_info build_info/
COPY middleware middleware/
COPY template template/
COPY utils utils/
COPY mapper mapper/

# Load AAGUID map
RUN wget --quiet https://raw.githubusercontent.com/passkeydeveloper/passkey-authenticator-aaguids/main/aaguid.json -O aaguid.json
lfleischmann marked this conversation as resolved.
Show resolved Hide resolved

# Build
RUN go generate ./...
RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -a -o hanko main.go
RUN CGO_ENABLED=0 GOOS=linux GOARCH="$TARGETARCH" go build -a -o hanko main.go

# Use distroless as minimal base image to package hanko binary
# See https://github.com/GoogleContainerTools/distroless for details
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY --from=builder /workspace/hanko .
COPY --from=builder /workspace/aaguid.json /etc/config/
USER 65532:65532

ENTRYPOINT ["/hanko"]
9 changes: 7 additions & 2 deletions backend/cmd/serve/all.go
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import (
"github.com/labstack/echo-contrib/echoprometheus"
"github.com/spf13/cobra"
"github.com/teamhanko/hanko/backend/config"
"github.com/teamhanko/hanko/backend/mapper"
"github.com/teamhanko/hanko/backend/persistence"
"github.com/teamhanko/hanko/backend/server"
"log"
@@ -15,7 +16,8 @@ import (

func NewServeAllCommand() *cobra.Command {
var (
configFile string
configFile string
aaguidMapFile string
)

cmd := &cobra.Command{
@@ -28,6 +30,8 @@ func NewServeAllCommand() *cobra.Command {
log.Fatal(err)
}

aaguidMap := mapper.LoadAaguidMap(&aaguidMapFile)

persister, err := persistence.New(cfg.Database)
if err != nil {
log.Fatal(err)
@@ -37,14 +41,15 @@ func NewServeAllCommand() *cobra.Command {

prometheus := echoprometheus.NewMiddleware("hanko")

go server.StartPublic(cfg, &wg, persister, prometheus)
go server.StartPublic(cfg, &wg, persister, prometheus, aaguidMap)
go server.StartAdmin(cfg, &wg, persister, prometheus)

wg.Wait()
},
}

cmd.Flags().StringVar(&configFile, "config", config.DefaultConfigFilePath, "config file")
cmd.Flags().StringVar(&aaguidMapFile, "aaguid-map", "", "aaguid map file")

return cmd
}
9 changes: 7 additions & 2 deletions backend/cmd/serve/public.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ package serve
import (
"github.com/spf13/cobra"
"github.com/teamhanko/hanko/backend/config"
"github.com/teamhanko/hanko/backend/mapper"
"github.com/teamhanko/hanko/backend/persistence"
"github.com/teamhanko/hanko/backend/server"
"log"
@@ -14,7 +15,8 @@ import (

func NewServePublicCommand() *cobra.Command {
var (
configFile string
configFile string
aaguidMapFile string
)

cmd := &cobra.Command{
@@ -27,20 +29,23 @@ func NewServePublicCommand() *cobra.Command {
log.Fatal(err)
}

aaguidMap := mapper.LoadAaguidMap(&aaguidMapFile)

persister, err := persistence.New(cfg.Database)
if err != nil {
log.Fatal(err)
}
var wg sync.WaitGroup
wg.Add(1)

go server.StartPublic(cfg, &wg, persister, nil)
go server.StartPublic(cfg, &wg, persister, nil, aaguidMap)

wg.Wait()
},
}

cmd.Flags().StringVar(&configFile, "config", config.DefaultConfigFilePath, "config file")
cmd.Flags().StringVar(&aaguidMapFile, "aaguid-map", "", "config file")

return cmd
}
19 changes: 16 additions & 3 deletions backend/config/config.go
Original file line number Diff line number Diff line change
@@ -39,14 +39,27 @@ var (
DefaultConfigFilePath = "./config/config.yaml"
)

func Load(cfgFile *string) (*Config, error) {
func LoadFile(filePath *string, pa koanf.Parser) (*koanf.Koanf, error) {
k := koanf.New(".")
var err error

if filePath == nil || *filePath == "" {
return nil, nil
}

if err := k.Load(file.Provider(*filePath), pa); err != nil {
return nil, fmt.Errorf("failed to load file from '%s': %w", *filePath, err)
}

return k, nil
}

func Load(cfgFile *string) (*Config, error) {
if cfgFile == nil || *cfgFile == "" {
*cfgFile = DefaultConfigFilePath
}

if err = k.Load(file.Provider(*cfgFile), yaml.Parser()); err != nil {
k, err := LoadFile(cfgFile, yaml.Parser())
if err != nil {
if *cfgFile != DefaultConfigFilePath {
return nil, fmt.Errorf("failed to load config from: %s: %w", *cfgFile, err)
}
4 changes: 3 additions & 1 deletion backend/dto/intern/WebauthnCredential.go
Original file line number Diff line number Diff line change
@@ -5,17 +5,19 @@ import (
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/gofrs/uuid"
"github.com/teamhanko/hanko/backend/mapper"
"github.com/teamhanko/hanko/backend/persistence/models"
"time"
)

func WebauthnCredentialToModel(credential *webauthn.Credential, userId uuid.UUID, backupEligible bool, backupState bool) *models.WebauthnCredential {
func WebauthnCredentialToModel(credential *webauthn.Credential, userId uuid.UUID, backupEligible bool, backupState bool, aaguidMap mapper.AaguidMap) *models.WebauthnCredential {
now := time.Now().UTC()
aaguid, _ := uuid.FromBytes(credential.Authenticator.AAGUID)
credentialID := base64.RawURLEncoding.EncodeToString(credential.ID)

c := &models.WebauthnCredential{
ID: credentialID,
Name: aaguidMap.GetNameForAaguid(aaguid),
UserId: userId,
PublicKey: base64.RawURLEncoding.EncodeToString(credential.PublicKey),
AttestationType: credential.AttestationType,
5 changes: 3 additions & 2 deletions backend/handler/public_router.go
Original file line number Diff line number Diff line change
@@ -11,13 +11,14 @@ import (
"github.com/teamhanko/hanko/backend/dto"
"github.com/teamhanko/hanko/backend/ee/saml"
"github.com/teamhanko/hanko/backend/mail"
"github.com/teamhanko/hanko/backend/mapper"
hankoMiddleware "github.com/teamhanko/hanko/backend/middleware"
"github.com/teamhanko/hanko/backend/persistence"
"github.com/teamhanko/hanko/backend/session"
"github.com/teamhanko/hanko/backend/template"
)

func NewPublicRouter(cfg *config.Config, persister persistence.Persister, prometheus echo.MiddlewareFunc) *echo.Echo {
func NewPublicRouter(cfg *config.Config, persister persistence.Persister, prometheus echo.MiddlewareFunc, aaguidMap mapper.AaguidMap) *echo.Echo {
e := echo.New()
e.Renderer = template.NewTemplateRenderer()
e.HideBanner = true
@@ -102,7 +103,7 @@ func NewPublicRouter(cfg *config.Config, persister persistence.Persister, promet
}

healthHandler := NewHealthHandler()
webauthnHandler, err := NewWebauthnHandler(cfg, persister, sessionManager, auditLogger)
webauthnHandler, err := NewWebauthnHandler(cfg, persister, sessionManager, auditLogger, aaguidMap)
if err != nil {
panic(fmt.Errorf("failed to create public webauthn handler: %w", err))
}
7 changes: 5 additions & 2 deletions backend/handler/webauthn.go
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ import (
"github.com/teamhanko/hanko/backend/config"
"github.com/teamhanko/hanko/backend/dto"
"github.com/teamhanko/hanko/backend/dto/intern"
"github.com/teamhanko/hanko/backend/mapper"
"github.com/teamhanko/hanko/backend/persistence"
"github.com/teamhanko/hanko/backend/persistence/models"
"github.com/teamhanko/hanko/backend/session"
@@ -28,10 +29,11 @@ type WebauthnHandler struct {
sessionManager session.Manager
cfg *config.Config
auditLogger auditlog.Logger
aaguidMap mapper.AaguidMap
}

// NewWebauthnHandler creates a new handler which handles all webauthn related routes
func NewWebauthnHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, auditLogger auditlog.Logger) (*WebauthnHandler, error) {
func NewWebauthnHandler(cfg *config.Config, persister persistence.Persister, sessionManager session.Manager, auditLogger auditlog.Logger, aaguidMap mapper.AaguidMap) (*WebauthnHandler, error) {
f := false
wa, err := webauthn.New(&webauthn.Config{
RPDisplayName: cfg.Webauthn.RelyingParty.DisplayName,
@@ -66,6 +68,7 @@ func NewWebauthnHandler(cfg *config.Config, persister persistence.Persister, ses
sessionManager: sessionManager,
cfg: cfg,
auditLogger: auditLogger,
aaguidMap: aaguidMap,
}, nil
}

@@ -196,7 +199,7 @@ func (h *WebauthnHandler) FinishRegistration(c echo.Context) error {

backupEligible := request.Response.AttestationObject.AuthData.Flags.HasBackupEligible()
backupState := request.Response.AttestationObject.AuthData.Flags.HasBackupState()
model := intern.WebauthnCredentialToModel(credential, sessionData.UserId, backupEligible, backupState)
model := intern.WebauthnCredentialToModel(credential, sessionData.UserId, backupEligible, backupState, h.aaguidMap)
err = h.persister.GetWebauthnCredentialPersisterWithConnection(tx).Create(*model)
if err != nil {
return fmt.Errorf("failed to store webauthn credential: %w", err)
48 changes: 48 additions & 0 deletions backend/mapper/aaguid_mapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package mapper

import (
"fmt"
"github.com/gofrs/uuid"
"github.com/knadh/koanf/parsers/json"
"github.com/teamhanko/hanko/backend/config"
"log"
)

type Aaguid struct {
Name string `json:"name"`
IconLight string `json:"icon_light"`
IconDark string `json:"icon_dark"`
}

type AaguidMap map[string]Aaguid
lfleischmann marked this conversation as resolved.
Show resolved Hide resolved

func (w AaguidMap) GetNameForAaguid(aaguid uuid.UUID) *string {
if webauthnAaguid, ok := w[aaguid.String()]; ok {
return &webauthnAaguid.Name
} else {
return nil
}
}

func LoadAaguidMap(aaguidFilePath *string) AaguidMap {
k, err := config.LoadFile(aaguidFilePath, json.Parser())

if err != nil {
log.Println(err)
return nil
}

if k == nil {
log.Println("no aaguid map file provided. Skipping...")
return nil
}

var aaguidMap AaguidMap
err = k.Unmarshal("", &aaguidMap)
if err != nil {
log.Println(fmt.Errorf("unable to unmarshal aaguid map: %w", err))
return nil
}

return aaguidMap
}
5 changes: 3 additions & 2 deletions backend/server/server.go
Original file line number Diff line number Diff line change
@@ -4,13 +4,14 @@ import (
"github.com/labstack/echo/v4"
"github.com/teamhanko/hanko/backend/config"
"github.com/teamhanko/hanko/backend/handler"
"github.com/teamhanko/hanko/backend/mapper"
"github.com/teamhanko/hanko/backend/persistence"
"sync"
)

func StartPublic(cfg *config.Config, wg *sync.WaitGroup, persister persistence.Persister, prometheus echo.MiddlewareFunc) {
func StartPublic(cfg *config.Config, wg *sync.WaitGroup, persister persistence.Persister, prometheus echo.MiddlewareFunc, aaguidMap mapper.AaguidMap) {
defer wg.Done()
router := handler.NewPublicRouter(cfg, persister, prometheus)
router := handler.NewPublicRouter(cfg, persister, prometheus, aaguidMap)
router.Logger.Fatal(router.Start(cfg.Server.Public.Address))
}

105 changes: 105 additions & 0 deletions deploy/docker-compose/aaguid.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion deploy/docker-compose/quickstart.yaml
lfleischmann marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ services:
- '8000:8000' # public
- '8001:8001' # admin
restart: unless-stopped
command: serve --config /etc/config/config.yaml all
command: serve --config /etc/config/config.yaml --aaguid-map /etc/config/aaguid.json all
volumes:
- type: bind
source: ./config.yaml