feat: implement passkey registration flow

This commit is contained in:
Prad Nukala 2024-12-10 14:37:54 -05:00
parent b51c36645e
commit 04d929aae9
7 changed files with 32 additions and 35 deletions

View File

@ -16,27 +16,12 @@ 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 []*Credential `json:"credentials"`
} }
type Session struct { type Session struct {

View File

@ -20,7 +20,6 @@ func NewGormDB(env config.Env) (*gorm.DB, error) {
// Migrate the schema // Migrate the schema
db.AutoMigrate(&Session{}) db.AutoMigrate(&Session{})
db.AutoMigrate(&User{}) db.AutoMigrate(&User{})
return db, nil return db, nil
} }

View File

@ -10,21 +10,21 @@ templ InitialView() {
@layout.Container() { @layout.Container() {
@text.Header("Sonr.ID", "The decentralized identity layer for the web.") @text.Header("Sonr.ID", "The decentralized identity layer for the web.")
<div class="pt-1.5 mb-3 flex flex-col items-center justify-center h-full"> <div class="pt-1.5 mb-3 flex flex-col items-center justify-center h-full">
<sl-button hx-target="#container" hx-get="/register" hx-push-url="/register" type="button"> <sl-button size="large" hx-target="#container" hx-get="/register" hx-push-url="/register" type="button">
<sl-icon slot="prefix" library="sonr" name="sonr"></sl-icon> <sl-icon slot="prefix" library="sonr" name="sonr"></sl-icon>
Get Started Get Started
<sl-icon slot="suffix" library="sonr" name="arrow-right"></sl-icon> <sl-icon slot="suffix" library="sonr" name="arrow-right"></sl-icon>
</sl-button> </sl-button>
</div> </div>
<div class="pt-5 flex flex-row items-center justify-center h-full gap-2"> <div class="pt-4 flex flex-row items-center justify-center h-full gap-3">
<sl-button circle outline> <sl-button circle outline href="https://sonr.io">
<sl-icon name="home" library="sonr" label="Home"></sl-icon> <sl-icon name="home" library="sonr" label="Home"></sl-icon>
</sl-button> </sl-button>
<sl-button circle outline> <sl-button circle outline href="https://onsonr.dev">
<sl-icon name="docs" library="sonr" label="Docs"></sl-icon> <sl-icon name="docs" library="sonr" label="Docs"></sl-icon>
</sl-button> </sl-button>
<sl-button circle outline> <sl-button circle outline href="https://github.com/onsonr/sonr">
<sl-icon name="question" label="About"></sl-icon> <sl-icon name="social-github" library="sonr" label="Open Source"></sl-icon>
</sl-button> </sl-button>
</div> </div>
} }

View File

@ -62,7 +62,7 @@ func InitialView() 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(" <div class=\"pt-1.5 mb-3 flex flex-col items-center justify-center h-full\"><sl-button hx-target=\"#container\" hx-get=\"/register\" hx-push-url=\"/register\" type=\"button\"><sl-icon slot=\"prefix\" library=\"sonr\" name=\"sonr\"></sl-icon> Get Started <sl-icon slot=\"suffix\" library=\"sonr\" name=\"arrow-right\"></sl-icon></sl-button></div><div class=\"pt-5 flex flex-row items-center justify-center h-full gap-2\"><sl-button circle outline><sl-icon name=\"home\" library=\"sonr\" label=\"Home\"></sl-icon></sl-button> <sl-button circle outline><sl-icon name=\"docs\" library=\"sonr\" label=\"Docs\"></sl-icon></sl-button> <sl-button circle outline><sl-icon name=\"question\" label=\"About\"></sl-icon></sl-button></div>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <div class=\"pt-1.5 mb-3 flex flex-col items-center justify-center h-full\"><sl-button size=\"large\" hx-target=\"#container\" hx-get=\"/register\" hx-push-url=\"/register\" type=\"button\"><sl-icon slot=\"prefix\" library=\"sonr\" name=\"sonr\"></sl-icon> Get Started <sl-icon slot=\"suffix\" library=\"sonr\" name=\"arrow-right\"></sl-icon></sl-button></div><div class=\"pt-4 flex flex-row items-center justify-center h-full gap-3\"><sl-button circle outline href=\"https://sonr.io\"><sl-icon name=\"home\" library=\"sonr\" label=\"Home\"></sl-icon></sl-button> <sl-button circle outline href=\"https://onsonr.dev\"><sl-icon name=\"docs\" library=\"sonr\" label=\"Docs\"></sl-icon></sl-button> <sl-button circle outline href=\"https://github.com/onsonr/sonr\"><sl-icon name=\"social-github\" library=\"sonr\" label=\"Open Source\"></sl-icon></sl-button></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

View File

@ -6,9 +6,23 @@ import (
"net/http" "net/http"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/database/sessions"
) )
// Define the credential structure matching our frontend data
type Credential struct {
Handle string `json:"handle"`
ID string `json:"id"`
RawID string `json:"rawId"`
Type string `json:"type"`
AuthenticatorAttachment string `json:"authenticatorAttachment"`
Transports string `json:"transports"`
ClientExtensionResults map[string]string `json:"clientExtensionResults"`
Response struct {
AttestationObject string `json:"attestationObject"`
ClientDataJSON string `json:"clientDataJSON"`
} `json:"response"`
}
type CreateProfileData struct { type CreateProfileData struct {
TurnstileSiteKey string TurnstileSiteKey string
FirstNumber int FirstNumber int
@ -35,8 +49,8 @@ func (d CreateProfileData) IsHumanLabel() string {
return fmt.Sprintf("What is %d + %d?", d.FirstNumber, d.LastNumber) return fmt.Sprintf("What is %d + %d?", d.FirstNumber, d.LastNumber)
} }
func extractCredentialDescriptor(jsonString string) (*sessions.Credential, error) { func extractCredentialDescriptor(jsonString string) (*Credential, error) {
cred := &sessions.Credential{} cred := &Credential{}
// Unmarshal the credential JSON // Unmarshal the credential JSON
if err := json.Unmarshal([]byte(jsonString), cred); err != nil { if err := json.Unmarshal([]byte(jsonString), cred); err != nil {
return nil, echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid credential format: %v", err)) return nil, echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid credential format: %v", err))

View File

@ -4,7 +4,7 @@ import "github.com/onsonr/sonr/pkg/common/styles/layout"
templ formCreateProfile(action string, method string, data CreateProfileData) { templ formCreateProfile(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-6 w-full max-w-2xl mx-auto px-4 sm:px-8"> <sl-card class="card-form gap-6 w-full max-w-xl">
<div slot="header"> <div slot="header">
<div class="w-full py-1"> <div class="w-full py-1">
<sl-progress-bar value="50"></sl-progress-bar> <sl-progress-bar value="50"></sl-progress-bar>
@ -63,7 +63,7 @@ templ formCreateProfile(action string, method string, data CreateProfileData) {
templ formRegisterPasskey(action, method string, data RegisterPasskeyData) { templ formRegisterPasskey(action, method string, data RegisterPasskeyData) {
<form action={ templ.SafeURL(action) } method={ method } id="passkey-form"> <form action={ templ.SafeURL(action) } method={ method } id="passkey-form">
<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-xl">
<div slot="header"> <div slot="header">
<div class="w-full py-2"> <div class="w-full py-2">
@sonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock) @sonrProfile(data.Address, data.Name, data.Handle, data.CreationBlock)
@ -123,4 +123,3 @@ templ formRegisterPasskey(action, method string, data RegisterPasskeyData) {
</sl-card> </sl-card>
</form> </form>
} }

View File

@ -53,7 +53,7 @@ func formCreateProfile(action string, method string, data CreateProfileData) tem
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-card class=\"card-form gap-6 w-full max-w-2xl mx-auto px-4 sm:px-8\"><div slot=\"header\"><div class=\"w-full py-1\"><sl-progress-bar value=\"50\"></sl-progress-bar></div></div><div class=\"grid grid-cols-1 lg:grid-cols-2 gap-4\"><sl-input name=\"first_name\" placeholder=\"Satoshi\" type=\"text\" label=\"First Name\" required autofocus></sl-input> <sl-input name=\"last_name\" placeholder=\"N\" maxlength=\"1\" type=\"text\" label=\"Last Initial\"></sl-input></div>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><sl-card class=\"card-form gap-6 w-full max-w-xl\"><div slot=\"header\"><div class=\"w-full py-1\"><sl-progress-bar value=\"50\"></sl-progress-bar></div></div><div class=\"grid grid-cols-1 lg:grid-cols-2 gap-4\"><sl-input name=\"first_name\" placeholder=\"Satoshi\" type=\"text\" label=\"First Name\" required autofocus></sl-input> <sl-input name=\"last_name\" placeholder=\"N\" maxlength=\"1\" type=\"text\" label=\"Last Initial\"></sl-input></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -133,7 +133,7 @@ func formRegisterPasskey(action, method string, data RegisterPasskeyData) templ.
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-2\">") _, 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-xl\"><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
} }