feat: implement passkey registration flow

This commit is contained in:
Prad Nukala 2024-12-09 16:23:18 -05:00
parent 27d4dfcdfd
commit 0e8b92e53d
11 changed files with 182 additions and 82 deletions

View File

@ -1,9 +1,22 @@
package forms 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 // ProfileForm is a standard form styled like a card
templ CreateProfile(action string, method string) { templ CreateProfile(action string, method string, data CreateProfileData) {
<form action={ templ.SafeURL(action) } method={ method }> <form action={ templ.SafeURL(action) } method={ method }>
<sl-card class="card-form gap-4 max-w-lg"> <sl-card class="card-form gap-4 max-w-lg">
<div slot="header"> <div slot="header">
@ -21,6 +34,8 @@ templ CreateProfile(action string, method string) {
<sl-icon name="at-sign" library="sonr"></sl-icon> <sl-icon name="at-sign" library="sonr"></sl-icon>
</div> </div>
</sl-input> </sl-input>
@layout.Spacer()
<sl-range name="is_human" label={ data.IsHumanLabel() } help-text="Prove you are a human." min="0" max="9" step="1"></sl-range>
<div slot="footer"> <div slot="footer">
<sl-button href="/" outline> <sl-button href="/" outline>
<sl-icon slot="prefix" name="arrow-left" library="sonr"></sl-icon> <sl-icon slot="prefix" name="arrow-left" library="sonr"></sl-icon>
@ -41,3 +56,6 @@ templ CreateProfile(action string, method string) {
</sl-card> </sl-card>
</form> </form>
} }
templ isHumanSlider(targetSum string) {
}

View File

@ -8,10 +8,23 @@ package forms
import "github.com/a-h/templ" import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime" 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 // 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) { 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 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 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 var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(method) templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(method)
if templ_7745c5c3_Err != nil { 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)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -84,7 +97,28 @@ func CreateProfile(action string, method string) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<sl-input name=\"handle\" placeholder=\"thoughtdiff\" type=\"text\" label=\"Handle\" minlength=\"4\" maxlength=\"12\" required><div slot=\"prefix\"><sl-icon name=\"at-sign\" library=\"sonr\"></sl-icon></div></sl-input><div slot=\"footer\"><sl-button href=\"/\" outline><sl-icon slot=\"prefix\" name=\"arrow-left\" library=\"sonr\"></sl-icon> Cancel</sl-button> <sl-button type=\"submit\">Next <sl-icon slot=\"suffix\" name=\"arrow-right\" library=\"sonr\"></sl-icon></sl-button></div><style>\n \t\t.card-form [slot='footer'] {\n \t\tdisplay: flex;\n \t\tjustify-content: space-between;\n \t\talign-items: center;\n \t\t}\n\t\t</style></sl-card></form>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<sl-input name=\"handle\" placeholder=\"thoughtdiff\" type=\"text\" label=\"Handle\" minlength=\"4\" maxlength=\"12\" required><div slot=\"prefix\"><sl-icon name=\"at-sign\" library=\"sonr\"></sl-icon></div></sl-input>")
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("<sl-range name=\"is_human\" label=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(data.IsHumanLabel())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pkg/blocks/forms/create_profile.templ`, Line: 38, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" help-text=\"Prove you are a human.\" min=\"0\" max=\"9\" step=\"1\"></sl-range><div slot=\"footer\"><sl-button href=\"/\" outline><sl-icon slot=\"prefix\" name=\"arrow-left\" library=\"sonr\"></sl-icon> Cancel</sl-button> <sl-button type=\"submit\">Next <sl-icon slot=\"suffix\" name=\"arrow-right\" library=\"sonr\"></sl-icon></sl-button></div><style>\n \t\t.card-form [slot='footer'] {\n \t\tdisplay: flex;\n \t\tjustify-content: space-between;\n \t\talign-items: center;\n \t\t}\n\t\t</style></sl-card></form>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err 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 var _ = templruntime.GeneratedTemplate

View File

@ -13,22 +13,20 @@ templ RegisterPasskey(action, method string, data RegisterPasskeyData) {
<input type="hidden" name="credential" id="credential-data" required/> <input type="hidden" name="credential" id="credential-data" required/>
<sl-card class="card-form gap-4 max-w-lg"> <sl-card class="card-form gap-4 max-w-lg">
<div slot="header"> <div slot="header">
<div class="w-full py-1"> <div class="w-full py-2">
@sonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock) @sonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock)
</div> </div>
</div> </div>
@passkeyDropzone(data.Address, data.Handle, data.Challenge) <div slot="footer" class="space-y-2">
<div slot="footer"> @passkeyDropzone(data.Address, data.Handle, data.Challenge)
<sl-button type="submit" pill style="width: 100%;" variant="primary"> <sl-button href="/" style="width: 100%;" outline>
<sl-icon slot="prefix" name="shield-fill-check"></sl-icon> <sl-icon slot="prefix" name="x-lg"></sl-icon>
Register Vault Cancel
<sl-icon slot="suffix" name="arrow-outbound" library="sonr"></sl-icon>
</sl-button> </sl-button>
</div> </div>
<style> <style>
.card-form [slot='footer'] { .card-form [slot='footer'] {
display: flex; justify-content: space-evenly;
justify-content: space-between;
align-items: center; align-items: center;
} }
</style> </style>
@ -37,7 +35,7 @@ templ RegisterPasskey(action, method string, data RegisterPasskeyData) {
} }
templ passkeyDropzone(addr string, userHandle string, challenge string) { templ passkeyDropzone(addr string, userHandle string, challenge string) {
<sl-button pill style="width: 100%;" onclick={ createPasskey(addr, userHandle, challenge) }> <sl-button style="width: 100%;" onclick={ createPasskey(addr, userHandle, challenge) }>
<sl-icon slot="prefix" name="passkey" library="sonr" style="font-size: 24px;" class="text-neutral-500"></sl-icon> <sl-icon slot="prefix" name="passkey" library="sonr" style="font-size: 24px;" class="text-neutral-500"></sl-icon>
Register Passkey Register Passkey
</sl-button> </sl-button>

View File

@ -59,7 +59,7 @@ func RegisterPasskey(action, method string, data RegisterPasskeyData) templ.Comp
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" id=\"passkey-form\"><input type=\"hidden\" name=\"credential\" id=\"credential-data\" required> <sl-card class=\"card-form gap-4 max-w-lg\"><div slot=\"header\"><div class=\"w-full py-1\">") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" id=\"passkey-form\"><input type=\"hidden\" name=\"credential\" id=\"credential-data\" required> <sl-card class=\"card-form gap-4 max-w-lg\"><div slot=\"header\"><div class=\"w-full py-2\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -67,7 +67,7 @@ func RegisterPasskey(action, method string, data RegisterPasskeyData) templ.Comp
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div><div slot=\"footer\" class=\"space-y-2\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -75,7 +75,7 @@ func RegisterPasskey(action, method string, data RegisterPasskeyData) templ.Comp
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div slot=\"footer\"><sl-button type=\"submit\" pill style=\"width: 100%;\" variant=\"primary\"><sl-icon slot=\"prefix\" name=\"shield-fill-check\"></sl-icon> Register Vault <sl-icon slot=\"suffix\" name=\"arrow-outbound\" library=\"sonr\"></sl-icon></sl-button></div><style>\n \t\t.card-form [slot='footer'] {\n \t\tdisplay: flex;\n \t\tjustify-content: space-between;\n \t\talign-items: center;\n \t\t}\n\t\t</style></sl-card></form>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<sl-button href=\"/\" style=\"width: 100%;\" outline><sl-icon slot=\"prefix\" name=\"x-lg\"></sl-icon> Cancel</sl-button></div><style>\n \t\t.card-form [slot='footer'] {\n \t\tjustify-content: space-evenly;\n \t\talign-items: center;\n \t\t}\n\t\t</style></sl-card></form>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -108,7 +108,7 @@ func passkeyDropzone(addr string, userHandle string, challenge string) templ.Com
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<sl-button pill style=\"width: 100%;\" onclick=\"") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<sl-button style=\"width: 100%;\" onclick=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -127,8 +127,8 @@ func passkeyDropzone(addr string, userHandle string, challenge string) templ.Com
func createPasskey(userId string, userHandle string, challenge string) templ.ComponentScript { func createPasskey(userId string, userHandle string, challenge string) templ.ComponentScript {
return templ.ComponentScript{ return templ.ComponentScript{
Name: `__templ_createPasskey_d562`, Name: `__templ_createPasskey_fc87`,
Function: `function __templ_createPasskey_d562(userId, userHandle, challenge){const publicKey = { Function: `function __templ_createPasskey_fc87(userId, userHandle, challenge){const publicKey = {
challenge: Uint8Array.from(challenge, (c) => c.charCodeAt(0)), challenge: Uint8Array.from(challenge, (c) => c.charCodeAt(0)),
rp: { rp: {
name: "Sonr.ID", 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 navigator.credentials
.create({ publicKey }) .create({ publicKey })
.then((newCredentialInfo) => { .then((newCredentialInfo) => {
const credentialJSON = JSON.stringify(newCredentialInfo); if (!(newCredentialInfo instanceof PublicKeyCredential)) {
document.getElementById('credential-data').value = btoa(credentialJSON); throw new Error('Received credential is not a PublicKeyCredential');
document.getElementById('passkey-form').submit(); }
)
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) => { .catch((err) => {
console.error(err); console.error('Passkey creation failed:', err);
alert('Failed to create passkey. Please try again.'); alert(` + "`" + `Failed to create passkey: ${err.message || 'Unknown error'}` + "`" + `);
}); });
}
}`, }`,
Call: templ.SafeScript(`__templ_createPasskey_d562`, userId, userHandle, challenge), Call: templ.SafeScript(`__templ_createPasskey_fc87`, userId, userHandle, challenge),
CallInline: templ.SafeScriptInline(`__templ_createPasskey_d562`, 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 var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(handle) templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(handle)
if templ_7745c5c3_Err != nil { 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)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -220,7 +257,7 @@ func sonrProfile(addr string, name string, handle string, creationBlock string)
var templ_7745c5c3_Var8 string var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(shortenAddress(addr)) templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(shortenAddress(addr))
if templ_7745c5c3_Err != nil { 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)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -233,7 +270,7 @@ func sonrProfile(addr string, name string, handle string, creationBlock string)
var templ_7745c5c3_Var9 string var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(creationBlock) templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(creationBlock)
if templ_7745c5c3_Err != nil { 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)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -246,7 +283,7 @@ func sonrProfile(addr string, name string, handle string, creationBlock string)
var templ_7745c5c3_Var10 string var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(name) templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(name)
if templ_7745c5c3_Err != nil { 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)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {

View File

@ -11,14 +11,16 @@ import (
"github.com/onsonr/sonr/crypto/mpc" "github.com/onsonr/sonr/crypto/mpc"
"github.com/onsonr/sonr/pkg/blocks/forms" "github.com/onsonr/sonr/pkg/blocks/forms"
"github.com/onsonr/sonr/pkg/common/response" "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" "github.com/onsonr/sonr/pkg/gateway/internal/pages/register"
) )
func HandleRegisterView(env config.Env) echo.HandlerFunc { func HandleRegisterView(c echo.Context) error {
return func(c echo.Context) error { dat := forms.CreateProfileData{
return response.TemplEcho(c, register.ProfileFormView(env.GetTurnstileSiteKey())) FirstNumber: 1,
LastNumber: 2,
} }
return response.TemplEcho(c, register.ProfileFormView(dat))
} }
func HandleRegisterStart(c echo.Context) error { func HandleRegisterStart(c echo.Context) error {
@ -47,21 +49,7 @@ func HandleRegisterFinish(c echo.Context) error {
if credentialJSON == "" { if credentialJSON == "" {
return echo.NewHTTPError(http.StatusBadRequest, "missing credential data") return echo.NewHTTPError(http.StatusBadRequest, "missing credential data")
} }
cred := database.Credential{}
// 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"`
}
// Unmarshal the credential JSON // Unmarshal the credential JSON
if err := json.Unmarshal([]byte(credentialJSON), &cred); err != nil { if err := json.Unmarshal([]byte(credentialJSON), &cred); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid credential format: %v", err)) 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(attestationObj),
len(clientData)) 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()) return response.TemplEcho(c, register.LoadingVaultView())
} }

View File

@ -3,7 +3,6 @@ package database
import ( import (
"net/http" "net/http"
"github.com/go-webauthn/webauthn/protocol"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -17,13 +16,27 @@ var (
ErrUserNotFound = echo.NewHTTPError(http.StatusNotFound, "User not found") 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 { type User struct {
gorm.Model gorm.Model
Address string `json:"address"` Address string `json:"address"`
Handle string `json:"handle"` Handle string `json:"handle"`
Name string `json:"name"` Name string `json:"name"`
CID string `json:"cid"` CID string `json:"cid"`
Credentials []*protocol.CredentialDescriptor `json:"credentials"` Credentials []*Credential `json:"credentials"`
} }
type Session struct { type Session struct {

View File

@ -6,11 +6,11 @@ import (
"github.com/onsonr/sonr/pkg/blocks/text" "github.com/onsonr/sonr/pkg/blocks/text"
) )
templ ProfileFormView(turnstileSiteKey string) { templ ProfileFormView(data forms.CreateProfileData) {
@layout.Root("New Profile | Sonr.ID") { @layout.Root("New Profile | Sonr.ID") {
@layout.Container() { @layout.Container() {
@text.Header("Create a Profile", "Enter some basic information about yourself.") @text.Header("Create a Profile", "Enter some basic information about yourself.")
@forms.CreateProfile("/register/start", "POST") @forms.CreateProfile("/register/start", "POST", data)
} }
} }
} }

View File

@ -14,7 +14,7 @@ import (
"github.com/onsonr/sonr/pkg/blocks/text" "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) { 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 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { 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 { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err 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 { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

View File

@ -1,7 +1,6 @@
package session package session
import ( import (
"fmt"
"regexp" "regexp"
"strings" "strings"
@ -34,13 +33,6 @@ func (s *HTTPContext) InitSession() error {
return err 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 s.sess = &sess
return nil return nil
} }

View File

@ -4,12 +4,13 @@ import (
"net/http" "net/http"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/onsonr/sonr/pkg/gateway/config"
"github.com/onsonr/sonr/pkg/gateway/internal/database" "github.com/onsonr/sonr/pkg/gateway/internal/database"
"gorm.io/gorm" "gorm.io/gorm"
) )
// Middleware creates a new session middleware // 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(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { return func(c echo.Context) error {
cc := NewHTTPContext(c, db) cc := NewHTTPContext(c, db)
@ -26,6 +27,7 @@ type HTTPContext struct {
echo.Context echo.Context
db *gorm.DB db *gorm.DB
sess *database.Session sess *database.Session
env config.Env
} }
// Get returns the HTTPContext from the echo context // Get returns the HTTPContext from the echo context

View File

@ -21,11 +21,11 @@ func RegisterRoutes(e *echo.Echo, env config.Env) error {
} }
// Inject session middleware with database connection // Inject session middleware with database connection
e.Use(session.Middleware(db)) e.Use(session.Middleware(db, env))
// Register routes // Register routes
e.GET("/", handlers.HandleIndex) e.GET("/", handlers.HandleIndex)
e.GET("/register", handlers.HandleRegisterView(env)) e.GET("/register", handlers.HandleRegisterView)
e.POST("/register/start", handlers.HandleRegisterStart) e.POST("/register/start", handlers.HandleRegisterStart)
e.POST("/register/finish", handlers.HandleRegisterFinish) e.POST("/register/finish", handlers.HandleRegisterFinish)
return nil return nil