diff --git a/go.mod b/go.mod index ce98026..066269d 100644 --- a/go.mod +++ b/go.mod @@ -1,22 +1,25 @@ module OwOrganizer -go 1.24.0 +go 1.25.0 require ( - github.com/go-webauthn/webauthn v0.10.2 + github.com/go-sql-driver/mysql v1.10.0 + github.com/go-webauthn/webauthn v0.17.4 github.com/google/uuid v1.6.0 + github.com/joho/godotenv v1.5.1 ) require ( filippo.io/edwards25519 v1.2.0 // indirect - github.com/fxamacker/cbor/v2 v2.6.0 // indirect - github.com/go-sql-driver/mysql v1.10.0 // indirect - github.com/go-webauthn/x v0.1.9 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect - github.com/google/go-tpm v0.9.0 // indirect - github.com/joho/godotenv v1.5.1 // indirect + github.com/fxamacker/cbor/v2 v2.9.2 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/go-webauthn/x v0.2.6 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/google/go-tpm v0.9.8 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/philhofer/fwd v1.2.0 // indirect + github.com/tinylib/msgp v1.6.4 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/sys v0.45.0 // indirect ) diff --git a/go.sum b/go.sum index fbb104f..bcb458d 100644 --- a/go.sum +++ b/go.sum @@ -6,42 +6,62 @@ github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADi github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78= +github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw= github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-webauthn/webauthn v0.10.1 h1:+RFKj4yHPy282teiiy5sqTYPfRilzBpJyedrz9KsNFE= github.com/go-webauthn/webauthn v0.10.1/go.mod h1:a7BwAtrSMkeuJXtIKz433Av99nAv01pdfzB0a9xkDnI= github.com/go-webauthn/webauthn v0.10.2 h1:OG7B+DyuTytrEPFmTX503K77fqs3HDK/0Iv+z8UYbq4= github.com/go-webauthn/webauthn v0.10.2/go.mod h1:Gd1IDsGAybuvK1NkwUTLbGmeksxuRJjVN2PE/xsPxHs= +github.com/go-webauthn/webauthn v0.17.4 h1:KFTSz3R2RYDiUn/0cDi3XTJgFenSG74eKTTHlqWhlxk= +github.com/go-webauthn/webauthn v0.17.4/go.mod h1:pZk63EE/BdztlmyS4Yc+9H5g4a8blNlbtGmdHQHbZX8= github.com/go-webauthn/x v0.1.8 h1:f1C6k1AyUlDvnIzWSW+G9rN9nbp1hhLXZagUtyxZ8nc= github.com/go-webauthn/x v0.1.8/go.mod h1:i8UNlGVt3oy6oAFcP4SZB1djZLx/4pbekCbWowjTaJg= github.com/go-webauthn/x v0.1.9 h1:v1oeLmoaa+gPOaZqUdDentu6Rl7HkSSsmOT6gxEQHhE= github.com/go-webauthn/x v0.1.9/go.mod h1:pJNMlIMP1SU7cN8HNlKJpLEnFHCygLCvaLZ8a1xeoQA= +github.com/go-webauthn/x v0.2.6 h1:TEyDuQAIiEgYpx60nKiBJIX/5nSUC8LxNbH+uf5U9uk= +github.com/go-webauthn/x v0.2.6/go.mod h1:45bA7YEqyQhRcQJ/TiBb46Ww8yqHBGvgEhQ3WWF0aDo= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= +github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= +github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= 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/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ= +github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 625ee88..01eaafb 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "database/sql" "encoding/json" "fmt" "log" @@ -10,6 +11,7 @@ import ( "time" "github.com/go-webauthn/webauthn/webauthn" "github.com/joho/godotenv" + "github.com/go-sql-driver/mysql" ) var ( @@ -49,9 +51,12 @@ func main() { host := getEnv("HOST", "localhost") port := ":" + getEnv("PORT", "8080") origin := fmt.Sprintf("%s://%s%s", proto, host, port) - dbUser := getEnv("DB_USER") - dbPassword := getEnv("DB_PASSWORD") - dbName := getEnv("DB_NAME") + cfg := mysql.NewConfig() + cfg.User = os.Getenv("DB_USER") + cfg.Passwd = os.Getenv("DB_PASS") + cfg.Net = "tcp" + cfg.Addr = "127.0.0.1:3306" + dbName := os.Getenv("DB_NAME") l.Printf("[INFO] make webauthn config") wconfig := &webauthn.Config{ @@ -66,17 +71,46 @@ func main() { os.Exit(1) } - db, err := sql.Open("mysql", dbUser + ":" + dbPassword + "@/") + db, err := sql.Open("mysql", cfg.FormatDSN()) if err != nil { panic(err) } + defer db.Close() // See "Important settings" section. db.SetConnMaxLifetime(time.Minute * 3) db.SetMaxOpenConns(10) db.SetMaxIdleConns(10) + _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", dbName)) + if err != nil { + log.Fatalf("Failed to verify/create database: %v", err) + } + + // 3. Switch your current connection pool to the database + _, err = db.Exec(fmt.Sprintf("USE %s", dbName)) + if err != nil { + log.Fatalf("Failed to switch database: %v", err) + } + + query := "CREATE TABLE IF NOT EXISTS users (" + + "username VARCHAR(64) NOT NULL," + + "created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP," + +// "last_used_at TIMESTAMP NULL," + + "rpid VARCHAR(64) NOT NULL," + // -- Relying Party ID" + + "kid VARBINARY(64) NOT NULL," + // -- Credential.ID" + + "aaguid CHAR(36) NULL," + // -- Authenticator.AAGUID" + + "public_key BLOB NOT NULL," + // -- Credential.PublicKey (encrypt at rest)" + + "attestation_type VARCHAR(32) NOT NULL," + // -- CredentialAttestation.AttestationType" + + "attestation_format VARCHAR(32) NOT NULL," + // -- CredentialAttestation.AttestationFormat" + + "attestation BLOB NULL DEFAULT NULL" + // -- CredentialAttestation serialized as Message Pack or JSON (encrypt at rest)" + + ")" + + _, err = db.Exec(query) + if err != nil { + log.Fatalf("Failed to verify/create table: %v", err) + } l.Printf("[INFO] create datastore") - datastore = NewInMem(l) + datastore = NewInMem(l, db) l.Printf("[INFO] register routes") // Serve the web files diff --git a/store.go b/store.go index 98115c8..6b1be42 100644 --- a/store.go +++ b/store.go @@ -5,7 +5,6 @@ import ( "database/sql" "encoding/base64" "fmt" - "time" _ "github.com/go-sql-driver/mysql" "github.com/go-webauthn/webauthn/webauthn" ) @@ -17,6 +16,7 @@ type InMem struct { sessions map[string]webauthn.SessionData log Logger + db* sql.DB } func (i *InMem) GenSessionID() (string, error) { @@ -30,11 +30,12 @@ func (i *InMem) GenSessionID() (string, error) { } -func NewInMem(log Logger) *InMem { +func NewInMem(log Logger, db *sql.DB) *InMem { return &InMem{ users: make(map[string]PasskeyUser), sessions: make(map[string]webauthn.SessionData), log: log, + db: db, } } @@ -70,21 +71,20 @@ func (i *InMem) GetOrCreateUser(userName string) PasskeyUser { } func (i *InMem) SaveUser(user PasskeyUser) { - // Store user in MySQL database - db, err := sql.Open("mysql", "root:password@/oworganizer") - if err != nil { - panic(err) - } - // See "Important settings" section. - db.SetConnMaxLifetime(time.Minute * 3) - db.SetMaxOpenConns(10) - db.SetMaxIdleConns(10) + username := user.WebAuthnName() + rpid := "oworganizer.com" + kid := base64.StdEncoding.EncodeToString(user.WebAuthnCredentials()[0].Descriptor().CredentialID) + aaguid := base64.StdEncoding.EncodeToString(user.WebAuthnCredentials()[0].Authenticator.AAGUID) + publicKey := base64.StdEncoding.EncodeToString(user.WebAuthnCredentials()[0].PublicKey) + attestationType := user.WebAuthnCredentials()[0].AttestationType + attestationFormat := user.WebAuthnCredentials()[0].AttestationFormat + attestation := user.WebAuthnCredentials()[0].Attestation.ClientDataJSON - insert, err := db.Query("INSERT INTO users (username, key) VALUES(" + user.WebAuthnName(), user.WebAuthnCredentials()) - if err !=nil { + _, err = i.db.Exec("INSERT INTO users (username, rpid, kid, aaguid, public_key, attestation_type, attestation_format, attestation) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + username, rpid, kid, aaguid, publicKey, attestationType, attestationFormat, attestation) + if err != nil { panic(err.Error()) } - defer insert.Close() fmt.Println("Yay, values added!") i.log.Printf("[DEBUG] SaveUser: %v", user.WebAuthnName())