feature/1114 implement account interface (#1167)

- **refactor: move session-related code to middleware package**
- **refactor: update PKL build process and adjust related
configurations**
- **feat: integrate base.cosmos.v1 Genesis module**
- **refactor: pass session context to modal rendering functions**
- **refactor: move nebula package to app directory and update templ
version**
- **refactor: Move home section video view to dedicated directory**
- **refactor: remove unused views file**
- **refactor: move styles and UI components to global scope**
- **refactor: Rename images.go to cdn.go**
- **feat: Add Empty State Illustrations**
- **refactor: Consolidate Vault Index Logic**
- **fix: References to App.wasm and remove Vault Directory embedded CDN
files**
- **refactor: Move CDN types to Models**
- **fix: Correct line numbers in templ error messages for
arch_templ.go**
- **refactor: use common types for peer roles**
- **refactor: move common types and ORM to a shared package**
- **fix: Config import dwn**
- **refactor: move nebula directory to app**
- **feat: Rebuild nebula**
- **fix: correct file paths in panels templates**
- **feat: Remove duplicate types**
- **refactor: Move dwn to pkg/core**
- **refactor: Binary Structure**
- **feat: Introduce Crypto Pkg**
- **fix: Broken Process Start**
- **feat: Update pkg/* structure**
- **feat: Refactor PKL Structure**
- **build: update pkl build process**
- **chore: Remove Empty Files**
- **refactor: remove unused macaroon package**
- **feat: Add WebAwesome Components**
- **refactor: consolidate build and generation tasks into a single
taskfile, remove redundant makefile targets**
- **refactor: refactor server and move components to pkg/core/dwn**
- **build: update go modules**
- **refactor: move gateway logic into dedicated hway command**
- **feat: Add KSS (Krawczyk-Song-Song) MPC cryptography module**
- **feat: Implement MPC-based JWT signing and UCAN token generation**
- **feat: add support for MPC-based JWT signing**
- **feat: Implement MPC-based UCAN capabilities for smart accounts**
- **feat: add address field to keyshareSource**
- **feat: Add comprehensive MPC test suite for keyshares, UCAN tokens,
and token attenuations**
- **refactor: improve MPC keyshare management and signing process**
- **feat: enhance MPC capability hierarchy documentation**
- **refactor: rename GenerateKeyshares function to NewKeyshareSource for
clarity**
- **refactor: remove unused Ethereum address computation**
- **feat: Add HasHandle and IsAuthenticated methods to HTTPContext**
- **refactor: Add context.Context support to session HTTPContext**
- **refactor: Resolve context interface conflicts in HTTPContext**
- **feat: Add session ID context key and helper functions**
- **feat: Update WebApp Page Rendering**
- **refactor: Simplify context management by using single HTTPContext
key**
- **refactor: Simplify HTTPContext creation and context management in
session middleware**
- **refactor: refactor session middleware to use a single data
structure**
- **refactor: Simplify HTTPContext implementation and session data
handling**
- **refactor: Improve session context handling and prevent nil pointer
errors**
- **refactor: Improve session context handling with nil safety and type
support**
- **refactor: improve session data injection**
- **feat: add full-screen modal component and update registration flow**
- **chore: add .air.toml to .gitignore**
- **feat: add Air to devbox and update dependencies**
This commit is contained in:
Prad Nukala
2024-11-23 01:28:58 -05:00
committed by GitHub
parent bf94277b0f
commit 89989fa102
549 changed files with 74162 additions and 9856 deletions
+60
View File
@@ -0,0 +1,60 @@
package session
import (
"context"
"net/http"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/pkg/common"
"github.com/onsonr/sonr/pkg/common/types"
)
type contextKey string
// Context keys
const (
DataContextKey contextKey = "http_session_data"
)
type Context = common.SessionCtx
// Get returns the session.Context from the echo context.
func Get(c echo.Context) (Context, error) {
ctx, ok := c.(*HTTPContext)
if !ok {
return nil, echo.NewHTTPError(http.StatusInternalServerError, "Session Context not found")
}
return ctx, nil
}
// WithData sets the session data in the context
func WithData(ctx context.Context, data *types.Session) context.Context {
return context.WithValue(ctx, DataContextKey, data)
}
// GetData gets the session data from any context type
func GetData(ctx interface{}) *types.Session {
switch c := ctx.(type) {
case *HTTPContext:
if c != nil {
return c.sessionData
}
case context.Context:
if c != nil {
if val := c.Value(DataContextKey); val != nil {
if httpCtx, ok := val.(*types.Session); ok {
return httpCtx
}
}
}
case echo.Context:
if c != nil {
if httpCtx, ok := c.(*HTTPContext); ok && httpCtx != nil {
return httpCtx.sessionData
}
}
}
// Return empty session rather than nil to prevent nil pointer panics
return &types.Session{}
}
@@ -0,0 +1,73 @@
package session
import (
"encoding/json"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/pkg/common"
"github.com/onsonr/sonr/pkg/common/middleware/cookie"
"github.com/onsonr/sonr/pkg/common/middleware/header"
"github.com/onsonr/sonr/pkg/core/dwn"
)
// HwayMiddleware establishes a Session Cookie.
func HwayMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
cc := injectSession(c, common.RoleHway)
return next(cc)
}
}
}
// MotrMiddleware establishes a Session Cookie.
func MotrMiddleware(config *dwn.Config) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
err := injectConfig(c, config)
if err != nil {
return err
}
cc := injectSession(c, common.RoleMotr)
return next(cc)
}
}
}
func injectConfig(c echo.Context, config *dwn.Config) error {
header.Write(c, header.IPFSHost, config.IpfsGatewayUrl)
header.Write(c, header.ChainID, config.SonrChainId)
header.Write(c, header.SonrAPIURL, config.SonrApiUrl)
header.Write(c, header.SonrRPCURL, config.SonrRpcUrl)
cookie.Write(c, cookie.SonrAddress, config.MotrAddress)
cookie.Write(c, cookie.SonrKeyshare, config.MotrKeyshare)
schemaBz, err := json.Marshal(config.VaultSchema)
if err != nil {
return err
}
cookie.WriteBytes(c, cookie.VaultSchema, schemaBz)
return nil
}
// injectSession returns the session injectSession from the cookies.
func injectSession(c echo.Context, role common.PeerRole) *HTTPContext {
if c == nil {
return initHTTPContext(nil)
}
cookie.Write(c, cookie.SessionRole, role.String())
// Continue even if there are errors, just ensure we have valid session data
if err := loadOrGenKsuid(c); err != nil {
// Log error but continue
}
if err := loadOrGenChallenge(c); err != nil {
// Log error but continue
}
return initHTTPContext(c)
}
+84
View File
@@ -0,0 +1,84 @@
package session
import (
"time"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/pkg/common"
"github.com/onsonr/sonr/pkg/common/middleware/cookie"
"github.com/onsonr/sonr/pkg/common/types"
)
// HTTPContext is the context for HTTP endpoints.
type HTTPContext struct {
echo.Context
role common.PeerRole
sessionData *types.Session
}
// Ensure HTTPContext implements context.Context
func (s *HTTPContext) Deadline() (deadline time.Time, ok bool) {
return s.Context.Request().Context().Deadline()
}
func (s *HTTPContext) Done() <-chan struct{} {
return s.Context.Request().Context().Done()
}
func (s *HTTPContext) Err() error {
return s.Context.Request().Context().Err()
}
func (s *HTTPContext) Value(key interface{}) interface{} {
return s.Context.Request().Context().Value(key)
}
// initHTTPContext loads the headers from the request.
func initHTTPContext(c echo.Context) *HTTPContext {
if c == nil {
return &HTTPContext{
sessionData: &types.Session{},
}
}
sessionData := injectSessionData(c)
if sessionData == nil {
sessionData = &types.Session{}
}
cc := &HTTPContext{
Context: c,
role: common.PeerRole(cookie.ReadUnsafe(c, cookie.SessionRole)),
sessionData: sessionData,
}
// Set the session data in both contexts
c.SetRequest(c.Request().WithContext(WithData(c.Request().Context(), sessionData)))
return cc
}
func (s *HTTPContext) ID() string {
return s.GetData().Id
}
func (s *HTTPContext) LoginOptions(credentials []common.CredDescriptor) *common.LoginOptions {
ch, _ := common.Base64Decode(s.GetData().Challenge)
return &common.LoginOptions{
Challenge: ch,
Timeout: 10000,
AllowedCredentials: credentials,
}
}
func (s *HTTPContext) RegisterOptions(subject string) *common.RegisterOptions {
ch, _ := common.Base64Decode(s.GetData().Challenge)
opts := baseRegisterOptions()
opts.Challenge = ch
opts.User = buildUserEntity(subject)
return opts
}
func (s *HTTPContext) GetData() *types.Session {
return s.sessionData
}
+188
View File
@@ -0,0 +1,188 @@
package session
import (
"regexp"
"strings"
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/protocol/webauthncose"
"github.com/labstack/echo/v4"
"github.com/segmentio/ksuid"
"github.com/onsonr/sonr/pkg/common"
"github.com/onsonr/sonr/pkg/common/middleware/cookie"
"github.com/onsonr/sonr/pkg/common/middleware/header"
"github.com/onsonr/sonr/pkg/common/types"
)
const kWebAuthnTimeout = 6000
// ╭───────────────────────────────────────────────────────────╮
// │ Initialization │
// ╰───────────────────────────────────────────────────────────╯
func loadOrGenChallenge(c echo.Context) error {
var (
chal protocol.URLEncodedBase64
chalRaw []byte
err error
)
// Setup genChal function
genChal := func() []byte {
ch, _ := protocol.CreateChallenge()
bz, _ := ch.MarshalJSON()
return bz
}
// Check if there is a session challenge cookie
if !cookie.Exists(c, cookie.SessionChallenge) {
chalRaw = genChal()
cookie.WriteBytes(c, cookie.SessionChallenge, chalRaw)
} else {
chalRaw, err = cookie.ReadBytes(c, cookie.SessionChallenge)
if err != nil {
return err
}
}
// Attempt to read the session challenge from the "session" cookie
err = chal.UnmarshalJSON(chalRaw)
if err != nil {
return err
}
return nil
}
func loadOrGenKsuid(c echo.Context) error {
var (
sessionID string
err error
)
// Setup genKsuid function
genKsuid := func() string {
return ksuid.New().String()
}
// Attempt to read the session ID from the "session" cookie
if ok := cookie.Exists(c, cookie.SessionID); !ok {
sessionID = genKsuid()
} else {
sessionID, err = cookie.Read(c, cookie.SessionID)
if err != nil {
sessionID = genKsuid()
}
}
cookie.Write(c, cookie.SessionID, sessionID)
return nil
}
// ╭───────────────────────────────────────────────────────────╮
// │ Extraction │
// ╰───────────────────────────────────────────────────────────╯
func injectSessionData(c echo.Context) *types.Session {
id, chal := extractPeerInfo(c)
bn, bv := extractBrowserInfo(c)
return &types.Session{
Id: id,
Challenge: chal,
BrowserName: bn,
BrowserVersion: bv,
UserArchitecture: header.Read(c, header.Architecture),
Platform: header.Read(c, header.Platform),
PlatformVersion: header.Read(c, header.PlatformVersion),
DeviceModel: header.Read(c, header.Model),
IsMobile: header.Equals(c, header.Mobile, "?1"),
}
}
func extractPeerInfo(c echo.Context) (string, string) {
var chal protocol.URLEncodedBase64
id, _ := cookie.Read(c, cookie.SessionID)
chalRaw, _ := cookie.ReadBytes(c, cookie.SessionChallenge)
chal.UnmarshalJSON(chalRaw)
return id, common.Base64Encode(chal)
}
func extractBrowserInfo(c echo.Context) (string, string) {
secCHUA := header.Read(c, header.UserAgent)
// If header is empty, return empty BrowserInfo
if secCHUA == "" {
return "N/A", "-1"
}
// Split the header into individual browser entries
var (
name string
ver string
)
entries := strings.Split(strings.TrimSpace(secCHUA), ",")
for _, entry := range entries {
// Remove leading/trailing spaces and quotes
entry = strings.TrimSpace(entry)
// Use regex to extract the browser name and version
re := regexp.MustCompile(`"([^"]+)";v="([^"]+)"`)
matches := re.FindStringSubmatch(entry)
if len(matches) == 3 {
browserName := matches[1]
version := matches[2]
// Skip "Not A;Brand"
if !validBrowser(browserName) {
continue
}
// Store the first valid browser info as fallback
name = browserName
ver = version
}
}
return name, ver
}
func validBrowser(name string) bool {
return name != common.BrowserNameUnknown.String() && name != common.BrowserNameChromium.String()
}
// ╭───────────────────────────────────────────────────────────╮
// │ Authentication │
// ╰───────────────────────────────────────────────────────────╯
func buildUserEntity(userID string) protocol.UserEntity {
return protocol.UserEntity{
ID: userID,
}
}
// returns the base options for registering a new user without challenge or user entity.
func baseRegisterOptions() *common.RegisterOptions {
return &protocol.PublicKeyCredentialCreationOptions{
Timeout: kWebAuthnTimeout,
Attestation: protocol.PreferDirectAttestation,
AuthenticatorSelection: protocol.AuthenticatorSelection{
AuthenticatorAttachment: "platform",
ResidentKey: protocol.ResidentKeyRequirementPreferred,
UserVerification: "preferred",
},
Parameters: []protocol.CredentialParameter{
{
Type: "public-key",
Algorithm: webauthncose.AlgES256,
},
{
Type: "public-key",
Algorithm: webauthncose.AlgES256K,
},
{
Type: "public-key",
Algorithm: webauthncose.AlgEdDSA,
},
},
}
}