diff --git a/pkg/blocks/forms/create_profile.templ b/pkg/blocks/forms/create_profile.templ index 7a00f3eb1..e0fccda53 100644 --- a/pkg/blocks/forms/create_profile.templ +++ b/pkg/blocks/forms/create_profile.templ @@ -1,9 +1,22 @@ package forms -import "github.com/onsonr/sonr/pkg/blocks/layout" +import ( + "fmt" + "github.com/onsonr/sonr/pkg/blocks/layout" +) + +type CreateProfileData struct { + TurnstileSiteKey string + FirstNumber int + LastNumber int +} + +func (d CreateProfileData) IsHumanLabel() string { + return fmt.Sprintf("What is %d + %d?", d.FirstNumber, d.LastNumber) +} // ProfileForm is a standard form styled like a card -templ CreateProfile(action string, method string) { +templ CreateProfile(action string, method string, data CreateProfileData) {
@@ -21,6 +34,8 @@ templ CreateProfile(action string, method string) {
+ @layout.Spacer() +
@@ -41,3 +56,6 @@ templ CreateProfile(action string, method string) { } + +templ isHumanSlider(targetSum string) { +} diff --git a/pkg/blocks/forms/create_profile_templ.go b/pkg/blocks/forms/create_profile_templ.go index af34bb5fd..df0730bc7 100644 --- a/pkg/blocks/forms/create_profile_templ.go +++ b/pkg/blocks/forms/create_profile_templ.go @@ -8,10 +8,23 @@ package forms import "github.com/a-h/templ" import templruntime "github.com/a-h/templ/runtime" -import "github.com/onsonr/sonr/pkg/blocks/layout" +import ( + "fmt" + "github.com/onsonr/sonr/pkg/blocks/layout" +) + +type CreateProfileData struct { + TurnstileSiteKey string + FirstNumber int + LastNumber int +} + +func (d CreateProfileData) IsHumanLabel() string { + return fmt.Sprintf("What is %d + %d?", d.FirstNumber, d.LastNumber) +} // ProfileForm is a standard form styled like a card -func CreateProfile(action string, method string) templ.Component { +func CreateProfile(action string, method string, data CreateProfileData) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -48,7 +61,7 @@ func CreateProfile(action string, method string) templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(method) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/create_profile.templ`, Line: 7, Col: 55} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/create_profile.templ`, Line: 20, Col: 55} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -84,7 +97,28 @@ func CreateProfile(action string, method string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Cancel Next
") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = layout.Spacer().Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Cancel Next
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -92,4 +126,29 @@ func CreateProfile(action string, method string) templ.Component { }) } +func isHumanSlider(targetSum string) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var6 := templ.GetChildren(ctx) + if templ_7745c5c3_Var6 == nil { + templ_7745c5c3_Var6 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + return templ_7745c5c3_Err + }) +} + var _ = templruntime.GeneratedTemplate diff --git a/pkg/blocks/forms/register_passkey.templ b/pkg/blocks/forms/register_passkey.templ index f19ba1209..b69b09e50 100644 --- a/pkg/blocks/forms/register_passkey.templ +++ b/pkg/blocks/forms/register_passkey.templ @@ -13,22 +13,20 @@ templ RegisterPasskey(action, method string, data RegisterPasskeyData) {
-
+
@sonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock)
- @passkeyDropzone(data.Address, data.Handle, data.Challenge) -
- - - Register Vault - +
+ @passkeyDropzone(data.Address, data.Handle, data.Challenge) + + + Cancel
@@ -37,7 +35,7 @@ templ RegisterPasskey(action, method string, data RegisterPasskeyData) { } templ passkeyDropzone(addr string, userHandle string, challenge string) { - + Register Passkey diff --git a/pkg/blocks/forms/register_passkey_templ.go b/pkg/blocks/forms/register_passkey_templ.go index 9e1b924f2..3a9f43b5c 100644 --- a/pkg/blocks/forms/register_passkey_templ.go +++ b/pkg/blocks/forms/register_passkey_templ.go @@ -59,7 +59,7 @@ func RegisterPasskey(action, method string, data RegisterPasskeyData) templ.Comp if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" id=\"passkey-form\">
") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" id=\"passkey-form\">
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -67,7 +67,7 @@ func RegisterPasskey(action, method string, data RegisterPasskeyData) templ.Comp if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -75,7 +75,7 @@ func RegisterPasskey(action, method string, data RegisterPasskeyData) templ.Comp if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
Register Vault
") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" Cancel
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -108,7 +108,7 @@ func passkeyDropzone(addr string, userHandle string, challenge string) templ.Com if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" c.charCodeAt(0)), rp: { name: "Sonr.ID", @@ -161,21 +161,58 @@ func createPasskey(userId string, userHandle string, challenge string) templ.Com }, }, }; +// Helper function to convert ArrayBuffer to Base64URL string +function arrayBufferToBase64URL(buffer) { + const bytes = new Uint8Array(buffer); + let str = ''; + bytes.forEach(byte => { str += String.fromCharCode(byte) }); + return btoa(str) + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=/g, ''); +} + navigator.credentials .create({ publicKey }) .then((newCredentialInfo) => { - const credentialJSON = JSON.stringify(newCredentialInfo); - document.getElementById('credential-data').value = btoa(credentialJSON); - document.getElementById('passkey-form').submit(); - ) + if (!(newCredentialInfo instanceof PublicKeyCredential)) { + throw new Error('Received credential is not a PublicKeyCredential'); + } + + const response = newCredentialInfo.response; + if (!(response instanceof AuthenticatorAttestationResponse)) { + throw new Error('Response is not an AuthenticatorAttestationResponse'); + } + + // Convert the credential data to a cross-platform compatible format + const credentialJSON = { + id: newCredentialInfo.id, + rawId: arrayBufferToBase64URL(newCredentialInfo.rawId), + type: newCredentialInfo.type, + authenticatorAttachment: newCredentialInfo.authenticatorAttachment || null, + transports: Array.isArray(response.getTransports) ? response.getTransports() : [], + clientExtensionResults: newCredentialInfo.getClientExtensionResults(), + response: { + attestationObject: arrayBufferToBase64URL(response.attestationObject), + clientDataJSON: arrayBufferToBase64URL(response.clientDataJSON) + } + }; + + // Set the form value with the stringified credential data + const credentialInput = document.getElementById('credential-data'); + credentialInput.value = JSON.stringify(credentialJSON); + + // Submit the form + const form = document.getElementById('passkey-form'); + form.submit(); + }) .catch((err) => { - console.error(err); - alert('Failed to create passkey. Please try again.'); + console.error('Passkey creation failed:', err); + alert(` + "`" + `Failed to create passkey: ${err.message || 'Unknown error'}` + "`" + `); }); - } }`, - Call: templ.SafeScript(`__templ_createPasskey_d562`, userId, userHandle, challenge), - CallInline: templ.SafeScriptInline(`__templ_createPasskey_d562`, userId, userHandle, challenge), + Call: templ.SafeScript(`__templ_createPasskey_fc87`, userId, userHandle, challenge), + CallInline: templ.SafeScriptInline(`__templ_createPasskey_fc87`, userId, userHandle, challenge), } } @@ -207,7 +244,7 @@ func sonrProfile(addr string, name string, handle string, creationBlock string) var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(handle) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/register_passkey.templ`, Line: 100, Col: 43} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/register_passkey.templ`, Line: 135, Col: 43} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -220,7 +257,7 @@ func sonrProfile(addr string, name string, handle string, creationBlock string) var templ_7745c5c3_Var8 string templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(shortenAddress(addr)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/register_passkey.templ`, Line: 107, Col: 58} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/register_passkey.templ`, Line: 142, Col: 58} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { @@ -233,7 +270,7 @@ func sonrProfile(addr string, name string, handle string, creationBlock string) var templ_7745c5c3_Var9 string templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(creationBlock) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/register_passkey.templ`, Line: 112, Col: 55} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/register_passkey.templ`, Line: 147, Col: 55} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { @@ -246,7 +283,7 @@ func sonrProfile(addr string, name string, handle string, creationBlock string) var templ_7745c5c3_Var10 string templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/register_passkey.templ`, Line: 116, Col: 32} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/register_passkey.templ`, Line: 151, Col: 32} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) if templ_7745c5c3_Err != nil { diff --git a/pkg/gateway/handlers/register_handler.go b/pkg/gateway/handlers/register_handler.go index 01b578e3a..8c16274b9 100644 --- a/pkg/gateway/handlers/register_handler.go +++ b/pkg/gateway/handlers/register_handler.go @@ -11,14 +11,16 @@ import ( "github.com/onsonr/sonr/crypto/mpc" "github.com/onsonr/sonr/pkg/blocks/forms" "github.com/onsonr/sonr/pkg/common/response" - "github.com/onsonr/sonr/pkg/gateway/config" + "github.com/onsonr/sonr/pkg/gateway/internal/database" "github.com/onsonr/sonr/pkg/gateway/internal/pages/register" ) -func HandleRegisterView(env config.Env) echo.HandlerFunc { - return func(c echo.Context) error { - return response.TemplEcho(c, register.ProfileFormView(env.GetTurnstileSiteKey())) +func HandleRegisterView(c echo.Context) error { + dat := forms.CreateProfileData{ + FirstNumber: 1, + LastNumber: 2, } + return response.TemplEcho(c, register.ProfileFormView(dat)) } func HandleRegisterStart(c echo.Context) error { @@ -47,21 +49,7 @@ func HandleRegisterFinish(c echo.Context) error { if credentialJSON == "" { return echo.NewHTTPError(http.StatusBadRequest, "missing credential data") } - - // Define the credential structure matching our frontend data - var cred struct { - ID string `json:"id"` - RawID string `json:"rawId"` - Type string `json:"type"` - AuthenticatorAttachment string `json:"authenticatorAttachment"` - Transports []string `json:"transports"` - ClientExtensionResults map[string]interface{} `json:"clientExtensionResults"` - Response struct { - AttestationObject string `json:"attestationObject"` - ClientDataJSON string `json:"clientDataJSON"` - } `json:"response"` - } - + cred := database.Credential{} // Unmarshal the credential JSON if err := json.Unmarshal([]byte(credentialJSON), &cred); err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid credential format: %v", err)) @@ -106,12 +94,5 @@ func HandleRegisterFinish(c echo.Context) error { len(attestationObj), len(clientData)) - // TODO: Verify the attestation and store the credential - // This is where you would: - // 1. Verify the attestation signature - // 2. Check the origin in client data - // 3. Verify the challenge - // 4. Store the credential for future authentications - return response.TemplEcho(c, register.LoadingVaultView()) } diff --git a/pkg/gateway/internal/database/models.go b/pkg/gateway/internal/database/models.go index 22e837708..578c244ec 100644 --- a/pkg/gateway/internal/database/models.go +++ b/pkg/gateway/internal/database/models.go @@ -3,7 +3,6 @@ package database import ( "net/http" - "github.com/go-webauthn/webauthn/protocol" "github.com/labstack/echo/v4" "gorm.io/gorm" ) @@ -17,13 +16,27 @@ var ( ErrUserNotFound = echo.NewHTTPError(http.StatusNotFound, "User not found") ) +// Define the credential structure matching our frontend data +type Credential struct { + ID string `json:"id"` + RawID string `json:"rawId"` + Type string `json:"type"` + AuthenticatorAttachment string `json:"authenticatorAttachment"` + Transports []string `json:"transports"` + ClientExtensionResults map[string]interface{} `json:"clientExtensionResults"` + Response struct { + AttestationObject string `json:"attestationObject"` + ClientDataJSON string `json:"clientDataJSON"` + } `json:"response"` +} + type User struct { gorm.Model - Address string `json:"address"` - Handle string `json:"handle"` - Name string `json:"name"` - CID string `json:"cid"` - Credentials []*protocol.CredentialDescriptor `json:"credentials"` + Address string `json:"address"` + Handle string `json:"handle"` + Name string `json:"name"` + CID string `json:"cid"` + Credentials []*Credential `json:"credentials"` } type Session struct { diff --git a/pkg/gateway/internal/pages/register/page.templ b/pkg/gateway/internal/pages/register/page.templ index 759535948..c462ecf39 100644 --- a/pkg/gateway/internal/pages/register/page.templ +++ b/pkg/gateway/internal/pages/register/page.templ @@ -6,11 +6,11 @@ import ( "github.com/onsonr/sonr/pkg/blocks/text" ) -templ ProfileFormView(turnstileSiteKey string) { +templ ProfileFormView(data forms.CreateProfileData) { @layout.Root("New Profile | Sonr.ID") { @layout.Container() { @text.Header("Create a Profile", "Enter some basic information about yourself.") - @forms.CreateProfile("/register/start", "POST") + @forms.CreateProfile("/register/start", "POST", data) } } } diff --git a/pkg/gateway/internal/pages/register/page_templ.go b/pkg/gateway/internal/pages/register/page_templ.go index 4a1337b82..66a79d7c3 100644 --- a/pkg/gateway/internal/pages/register/page_templ.go +++ b/pkg/gateway/internal/pages/register/page_templ.go @@ -14,7 +14,7 @@ import ( "github.com/onsonr/sonr/pkg/blocks/text" ) -func ProfileFormView(turnstileSiteKey string) templ.Component { +func ProfileFormView(data forms.CreateProfileData) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -67,7 +67,7 @@ func ProfileFormView(turnstileSiteKey string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = forms.CreateProfile("/register/start", "POST").Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = forms.CreateProfile("/register/start", "POST", data).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/pkg/gateway/internal/session/context.go b/pkg/gateway/internal/session/context.go index 796f32ef5..efcaf1b35 100644 --- a/pkg/gateway/internal/session/context.go +++ b/pkg/gateway/internal/session/context.go @@ -1,7 +1,6 @@ package session import ( - "fmt" "regexp" "strings" @@ -34,13 +33,6 @@ func (s *HTTPContext) InitSession() error { return err } } - fmt.Println(sess.BrowserName) - fmt.Println(sess.BrowserVersion) - fmt.Println(sess.UserArchitecture) - fmt.Println(sess.Platform) - fmt.Println(sess.PlatformVersion) - fmt.Println(sess.DeviceModel) - s.sess = &sess return nil } diff --git a/pkg/gateway/internal/session/middleware.go b/pkg/gateway/internal/session/middleware.go index 84bff013f..d52fa2288 100644 --- a/pkg/gateway/internal/session/middleware.go +++ b/pkg/gateway/internal/session/middleware.go @@ -4,12 +4,13 @@ import ( "net/http" "github.com/labstack/echo/v4" + "github.com/onsonr/sonr/pkg/gateway/config" "github.com/onsonr/sonr/pkg/gateway/internal/database" "gorm.io/gorm" ) // Middleware creates a new session middleware -func Middleware(db *gorm.DB) echo.MiddlewareFunc { +func Middleware(db *gorm.DB, env config.Env) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { cc := NewHTTPContext(c, db) @@ -26,6 +27,7 @@ type HTTPContext struct { echo.Context db *gorm.DB sess *database.Session + env config.Env } // Get returns the HTTPContext from the echo context diff --git a/pkg/gateway/routes.go b/pkg/gateway/routes.go index 52f9c4407..1e5841da9 100644 --- a/pkg/gateway/routes.go +++ b/pkg/gateway/routes.go @@ -21,11 +21,11 @@ func RegisterRoutes(e *echo.Echo, env config.Env) error { } // Inject session middleware with database connection - e.Use(session.Middleware(db)) + e.Use(session.Middleware(db, env)) // Register routes e.GET("/", handlers.HandleIndex) - e.GET("/register", handlers.HandleRegisterView(env)) + e.GET("/register", handlers.HandleRegisterView) e.POST("/register/start", handlers.HandleRegisterStart) e.POST("/register/finish", handlers.HandleRegisterFinish) return nil