Files
sonr/pkg/gateway/internal/session/session.go
T

185 lines
5.1 KiB
Go
Raw Normal View History

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"
)
const kWebAuthnTimeout = 6000
2024-12-05 20:36:58 -05:00
// HTTPContext is the context for HTTP endpoints.
type HTTPContext struct {
echo.Context
role common.PeerRole
id string
chal string
bn string
bv string
}
2024-12-05 20:36:58 -05:00
// initHTTPContext loads the headers from the request.
func initHTTPContext(c echo.Context) *HTTPContext {
if c == nil {
return &HTTPContext{}
}
2024-12-05 20:36:58 -05:00
id, chal := extractPeerInfo(c)
bn, bv := extractBrowserInfo(c)
2024-12-05 20:36:58 -05:00
cc := &HTTPContext{
Context: c,
role: common.PeerRole(common.ReadCookieUnsafe(c, common.SessionRole)),
id: id,
chal: chal,
bn: bn,
bv: bv,
}
2024-12-05 20:36:58 -05:00
// Set the session data in both contexts
return cc
}
2024-12-05 20:36:58 -05:00
func (s *HTTPContext) ID() string {
return s.id
}
func (s *HTTPContext) BrowserName() string {
return s.bn
}
func (s *HTTPContext) BrowserVersion() string {
return s.bv
}
// ╭───────────────────────────────────────────────────────────╮
// │ Initialization │
// ╰───────────────────────────────────────────────────────────╯
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
2024-12-05 20:36:58 -05:00
if ok := common.CookieExists(c, common.SessionID); !ok {
sessionID = genKsuid()
} else {
2024-12-05 20:36:58 -05:00
sessionID, err = common.ReadCookie(c, common.SessionID)
if err != nil {
sessionID = genKsuid()
}
}
2024-12-05 20:36:58 -05:00
common.WriteCookie(c, common.SessionID, sessionID)
return nil
}
// ╭───────────────────────────────────────────────────────────╮
// │ Extraction │
// ╰───────────────────────────────────────────────────────────╯
func extractPeerInfo(c echo.Context) (string, string) {
var chal protocol.URLEncodedBase64
2024-12-05 20:36:58 -05:00
id, _ := common.ReadCookie(c, common.SessionID)
chalRaw, _ := common.ReadCookieBytes(c, common.SessionChallenge)
chal.UnmarshalJSON(chalRaw)
return id, common.Base64Encode(chal)
}
func extractBrowserInfo(c echo.Context) (string, string) {
2024-12-05 20:36:58 -05:00
secCHUA := common.HeaderRead(c, common.UserAgent)
2024-12-05 20:36:58 -05:00
// If common.is empty, return empty BrowserInfo
if secCHUA == "" {
return "N/A", "-1"
}
2024-12-05 20:36:58 -05:00
// Split the common.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.
2024-12-05 20:36:58 -05:00
func baseRegisterOptions() *protocol.PublicKeyCredentialCreationOptions {
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,
},
},
}
}
func formatAuth(ucanCID string) string {
return "Bearer " + ucanCID
}