feat: add User-Agent and Platform to session

This commit is contained in:
Prad Nukala
2024-10-12 12:52:20 -04:00
parent 58aa71997d
commit 104df074e9
6 changed files with 80 additions and 137 deletions
+34 -84
View File
@@ -1,120 +1,70 @@
package ctx
import (
"errors"
"fmt"
"github.com/go-webauthn/webauthn/protocol"
"github.com/gorilla/sessions"
"github.com/labstack/echo/v4"
)
type WebBytes = protocol.URLEncodedBase64
type Session interface {
ID() string
Origin() string
Address() string
ChainID() string
GetChallenge(subject string) (WebBytes, error)
ValidateChallenge(challenge WebBytes, subject string) error
SaveHTTP(c echo.Context) error
}
func NewSessionFromValues(vals map[interface{}]interface{}) *session {
s := &session{
id: vals["id"].(string),
origin: vals["origin"].(string),
address: vals["address"].(string),
chainID: vals["chainID"].(string),
challenge: vals["challenge"].(WebBytes),
subject: vals["subject"].(string),
}
return s
}
type session struct {
type Session struct {
// Defaults
session *sessions.Session
id string // Generated ksuid http cookie; Initialized on first request
origin string // Webauthn mapping to Relaying Party ID; Initialized on first request
ID string // Generated ksuid http cookie; Initialized on first request
Origin string // Webauthn mapping to Relaying Party ID; Initialized on first request
UserAgent string
Platform string
// Initialization
address string // Webauthn mapping to User ID; Supplied by DWN frontend
chainID string // Macaroon mapping to location; Supplied by DWN frontend
Address string // Webauthn mapping to User ID; Supplied by DWN frontend
ChainID string // Macaroon mapping to location; Supplied by DWN frontend
Subject string // Webauthn mapping to User Displayable Name; Supplied by DWN frontend
// Authentication
challenge WebBytes // Webauthn mapping to Challenge; Per session based on origin
subject string // Webauthn mapping to User Displayable Name; Supplied by DWN frontend
}
func (s *session) ID() string {
return s.id
}
func (s *Session) GetChallenge(subject string) (WebBytes, error) {
// Check if challenge is already set and subject matches
if s.Subject != "" && s.Subject != subject {
return nil, errors.New("challenge already set, and subject does not match")
} else if s.Subject == "" {
s.Subject = subject
} else {
return s.challenge, nil
}
func (s *session) Origin() string {
return s.origin
}
func (s *session) Address() string {
return s.address
}
func (s *session) ChainID() string {
return s.chainID
}
func (s *session) GetChallenge(subject string) (WebBytes, error) {
if s.challenge == nil {
return nil, nil
chl, err := protocol.CreateChallenge()
if err != nil {
return nil, err
}
s.challenge = chl
}
return s.challenge, nil
}
func (s *session) ValidateChallenge(challenge WebBytes, subject string) error {
func (s *Session) ValidateChallenge(challenge WebBytes, subject string) error {
if s.challenge == nil {
return nil
}
if s.challenge.String() != challenge.String() {
return fmt.Errorf("invalid challenge")
}
s.subject = subject
s.Subject = subject
return nil
}
func (s *session) SaveHTTP(c echo.Context) error {
sess, err := store.Get(c.Request(), s.id)
if err != nil {
return err
}
sess.Values = s.Values()
err = sess.Save(c.Request(), c.Response().Writer)
if err != nil {
return err
}
return nil
}
func (s *session) Values() map[interface{}]interface{} {
vals := make(map[interface{}]interface{})
vals["id"] = s.id
vals["address"] = s.address
vals["chainID"] = s.chainID
vals["challenge"] = s.challenge
vals["subject"] = s.subject
return vals
}
func GetSession(c echo.Context) Session {
func GetSession(c echo.Context) *Session {
id, _ := getSessionID(c.Request().Context())
sess, _ := store.Get(c.Request(), id)
if sess.IsNew {
s := defaultSession(id, sess)
s.SaveHTTP(c)
return s
}
s, _ := readSessionFromStore(c, id)
return s
return buildSession(c, id)
}
func SetAddress(c echo.Context, address string) *Session {
// Write address to X-Sonr-Address header
c.Response().Header().Set("X-Sonr-Address", address)
return buildSession(c, "")
}