mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
feat: add scheduled binary release workflow
This commit is contained in:
@@ -1,161 +0,0 @@
|
||||
//go:build js && wasm
|
||||
// +build js,wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"syscall/js"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
promise "github.com/nlepage/go-js-promise"
|
||||
|
||||
"github.com/onsonr/sonr/pkg/nebula/pages"
|
||||
"github.com/onsonr/sonr/x/vault/client/dwn/middleware"
|
||||
"github.com/onsonr/sonr/x/vault/client/dwn/state"
|
||||
)
|
||||
|
||||
func main() {
|
||||
e := echo.New()
|
||||
e.Use(middleware.UseSession)
|
||||
registerViews(e)
|
||||
registerState(e)
|
||||
Serve(e)
|
||||
}
|
||||
|
||||
func registerState(e *echo.Echo) {
|
||||
g := e.Group("state")
|
||||
g.POST("/login/:identifier", state.HandleCredentialAssertion)
|
||||
// g.GET("/discovery", state.GetDiscovery)
|
||||
g.GET("/jwks", state.GetJWKS)
|
||||
g.GET("/token", state.GetToken)
|
||||
g.POST("/:origin/grant/:subject", state.GrantAuthorization)
|
||||
g.POST("/register/:subject", state.HandleCredentialCreation)
|
||||
g.POST("/register/:subject/check", state.CheckSubjectIsValid)
|
||||
}
|
||||
|
||||
func registerViews(e *echo.Echo) {
|
||||
e.GET("/home", pages.Home)
|
||||
e.GET("/login", pages.Login)
|
||||
e.GET("/register", pages.Register)
|
||||
e.GET("/profile", pages.Profile)
|
||||
e.GET("/authorize", pages.Authorize)
|
||||
}
|
||||
|
||||
// Serve serves HTTP requests using handler or http.DefaultServeMux if handler is nil.
|
||||
func Serve(handler http.Handler) func() {
|
||||
h := handler
|
||||
if h == nil {
|
||||
h = http.DefaultServeMux
|
||||
}
|
||||
|
||||
prefix := js.Global().Get("wasmhttp").Get("path").String()
|
||||
for strings.HasSuffix(prefix, "/") {
|
||||
prefix = strings.TrimSuffix(prefix, "/")
|
||||
}
|
||||
|
||||
if prefix != "" {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle(prefix+"/", http.StripPrefix(prefix, h))
|
||||
h = mux
|
||||
}
|
||||
|
||||
cb := js.FuncOf(func(_ js.Value, args []js.Value) interface{} {
|
||||
resPromise, resolve, reject := promise.New()
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if err, ok := r.(error); ok {
|
||||
reject(fmt.Sprintf("wasmhttp: panic: %+v\n", err))
|
||||
} else {
|
||||
reject(fmt.Sprintf("wasmhttp: panic: %v\n", r))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
res := NewResponseRecorder()
|
||||
|
||||
h.ServeHTTP(res, Request(args[0]))
|
||||
|
||||
resolve(res.JSResponse())
|
||||
}()
|
||||
|
||||
return resPromise
|
||||
})
|
||||
|
||||
js.Global().Get("wasmhttp").Call("setHandler", cb)
|
||||
|
||||
return cb.Release
|
||||
}
|
||||
|
||||
// Request builds and returns the equivalent http.Request
|
||||
func Request(r js.Value) *http.Request {
|
||||
jsBody := js.Global().Get("Uint8Array").New(promise.Await(r.Call("arrayBuffer")))
|
||||
body := make([]byte, jsBody.Get("length").Int())
|
||||
js.CopyBytesToGo(body, jsBody)
|
||||
|
||||
req := httptest.NewRequest(
|
||||
r.Get("method").String(),
|
||||
r.Get("url").String(),
|
||||
bytes.NewBuffer(body),
|
||||
)
|
||||
|
||||
headersIt := r.Get("headers").Call("entries")
|
||||
for {
|
||||
e := headersIt.Call("next")
|
||||
if e.Get("done").Bool() {
|
||||
break
|
||||
}
|
||||
v := e.Get("value")
|
||||
req.Header.Set(v.Index(0).String(), v.Index(1).String())
|
||||
}
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
// ResponseRecorder uses httptest.ResponseRecorder to build a JS Response
|
||||
type ResponseRecorder struct {
|
||||
*httptest.ResponseRecorder
|
||||
}
|
||||
|
||||
// NewResponseRecorder returns a new ResponseRecorder
|
||||
func NewResponseRecorder() ResponseRecorder {
|
||||
return ResponseRecorder{httptest.NewRecorder()}
|
||||
}
|
||||
|
||||
// JSResponse builds and returns the equivalent JS Response
|
||||
func (rr ResponseRecorder) JSResponse() js.Value {
|
||||
res := rr.Result()
|
||||
|
||||
body := js.Undefined()
|
||||
if res.ContentLength != 0 {
|
||||
b, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
body = js.Global().Get("Uint8Array").New(len(b))
|
||||
js.CopyBytesToJS(body, b)
|
||||
}
|
||||
|
||||
init := make(map[string]interface{}, 2)
|
||||
|
||||
if res.StatusCode != 0 {
|
||||
init["status"] = res.StatusCode
|
||||
}
|
||||
|
||||
if len(res.Header) != 0 {
|
||||
headers := make(map[string]interface{}, len(res.Header))
|
||||
for k := range res.Header {
|
||||
headers[k] = res.Header.Get(k)
|
||||
}
|
||||
init["headers"] = headers
|
||||
}
|
||||
|
||||
return js.Global().Get("Response").New(body, init)
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package middleware
|
||||
|
||||
type RequestHeaders struct {
|
||||
Authorization *string `header:"Authorization"`
|
||||
CacheControl *string `header:"Cache-Control"`
|
||||
DeviceMemory *string `header:"Device-Memory"`
|
||||
Forwarded *string `header:"Forwarded"`
|
||||
From *string `header:"From"`
|
||||
Host *string `header:"Host"`
|
||||
Link *string `header:"Link"`
|
||||
PermissionsPolicy *string `header:"Permissions-Policy"`
|
||||
ProxyAuthorization *string `header:"Proxy-Authorization"`
|
||||
Referer *string `header:"Referer"`
|
||||
UserAgent *string `header:"User-Agent"`
|
||||
ViewportWidth *string `header:"Viewport-Width"`
|
||||
Width *string `header:"Width"`
|
||||
WWWAuthenticate *string `header:"WWW-Authenticate"`
|
||||
|
||||
// HTMX Specific
|
||||
HXBoosted *string `header:"HX-Boosted"`
|
||||
HXCurrentURL *string `header:"HX-Current-URL"`
|
||||
HXHistoryRestoreRequest *string `header:"HX-History-Restore-Request"`
|
||||
HXPrompt *string `header:"HX-Prompt"`
|
||||
HXRequest *string `header:"HX-Request"`
|
||||
HXTarget *string `header:"HX-Target"`
|
||||
HXTriggerName *string `header:"HX-Trigger-Name"`
|
||||
HXTrigger *string `header:"HX-Trigger"`
|
||||
}
|
||||
|
||||
type ResponseHeaders struct {
|
||||
AcceptCH *string `header:"Accept-CH"`
|
||||
AccessControlAllowCredentials *string `header:"Access-Control-Allow-Credentials"`
|
||||
AccessControlAllowHeaders *string `header:"Access-Control-Allow-Headers"`
|
||||
AccessControlAllowMethods *string `header:"Access-Control-Allow-Methods"`
|
||||
AccessControlExposeHeaders *string `header:"Access-Control-Expose-Headers"`
|
||||
AccessControlRequestHeaders *string `header:"Access-Control-Request-Headers"`
|
||||
ContentSecurityPolicy *string `header:"Content-Security-Policy"`
|
||||
CrossOriginEmbedderPolicy *string `header:"Cross-Origin-Embedder-Policy"`
|
||||
PermissionsPolicy *string `header:"Permissions-Policy"`
|
||||
ProxyAuthorization *string `header:"Proxy-Authorization"`
|
||||
WWWAuthenticate *string `header:"WWW-Authenticate"`
|
||||
|
||||
// HTMX Specific
|
||||
HXLocation *string `header:"HX-Location"`
|
||||
HXPushURL *string `header:"HX-Push-Url"`
|
||||
HXRedirect *string `header:"HX-Redirect"`
|
||||
HXRefresh *string `header:"HX-Refresh"`
|
||||
HXReplaceURL *string `header:"HX-Replace-Url"`
|
||||
HXReswap *string `header:"HX-Reswap"`
|
||||
HXRetarget *string `header:"HX-Retarget"`
|
||||
HXReselect *string `header:"HX-Reselect"`
|
||||
HXTrigger *string `header:"HX-Trigger"`
|
||||
HXTriggerAfterSettle *string `header:"HX-Trigger-After-Settle"`
|
||||
HXTriggerAfterSwap *string `header:"HX-Trigger-After-Swap"`
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"gopkg.in/macaroon.v2"
|
||||
)
|
||||
|
||||
// GetSession returns the current Session
|
||||
func GetSession(c echo.Context) *Session {
|
||||
return c.(*Session)
|
||||
}
|
||||
|
||||
// UseSession establishes a Session Cookie.
|
||||
func UseSession(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
sc := initSession(c)
|
||||
headers := new(RequestHeaders)
|
||||
sc.Bind(headers)
|
||||
return next(sc)
|
||||
}
|
||||
}
|
||||
|
||||
func MacaroonMiddleware(secretKeyStr string, location string) echo.MiddlewareFunc {
|
||||
secretKey := []byte(secretKeyStr)
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
// Extract the macaroon from the Authorization header
|
||||
auth := c.Request().Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Missing Authorization header"})
|
||||
}
|
||||
|
||||
// Decode the macaroon
|
||||
mac, err := macaroon.Base64Decode([]byte(auth))
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid macaroon encoding"})
|
||||
}
|
||||
|
||||
token, err := macaroon.New(secretKey, mac, location, macaroon.LatestVersion)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid macaroon"})
|
||||
}
|
||||
|
||||
// Verify the macaroon
|
||||
err = token.Verify(secretKey, func(caveat string) error {
|
||||
for _, c := range MacroonCaveats {
|
||||
if c.String() == caveat {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil // Return nil if the caveat is valid
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Invalid macaroon"})
|
||||
}
|
||||
|
||||
// Macaroon is valid, proceed to the next handler
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/donseba/go-htmx"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/segmentio/ksuid"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
echo.Context
|
||||
htmx *htmx.HTMX
|
||||
}
|
||||
|
||||
func (c *Session) Htmx() *htmx.HTMX {
|
||||
return c.htmx
|
||||
}
|
||||
|
||||
func (c *Session) ID() string {
|
||||
return ReadCookie(c, "session")
|
||||
}
|
||||
|
||||
func initSession(c echo.Context) *Session {
|
||||
s := &Session{Context: c}
|
||||
if val := ReadCookie(c, "session"); val == "" {
|
||||
id := ksuid.New().String()
|
||||
WriteCookie(c, "session", id)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func ReadCookie(c echo.Context, key string) string {
|
||||
cookie, err := c.Cookie(key)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if cookie == nil {
|
||||
return ""
|
||||
}
|
||||
return cookie.Value
|
||||
}
|
||||
|
||||
func WriteCookie(c echo.Context, key string, value string) {
|
||||
cookie := new(http.Cookie)
|
||||
cookie.Name = key
|
||||
cookie.Value = value
|
||||
cookie.Expires = time.Now().Add(24 * time.Hour)
|
||||
c.SetCookie(cookie)
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
OriginMacroonCaveat MacroonCaveat = "origin"
|
||||
ScopesMacroonCaveat MacroonCaveat = "scopes"
|
||||
SubjectMacroonCaveat MacroonCaveat = "subject"
|
||||
ExpMacroonCaveat MacroonCaveat = "exp"
|
||||
TokenMacroonCaveat MacroonCaveat = "token"
|
||||
)
|
||||
|
||||
type MacroonCaveat string
|
||||
|
||||
func (c MacroonCaveat) Equal(other string) bool {
|
||||
return string(c) == other
|
||||
}
|
||||
|
||||
func (c MacroonCaveat) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
func (c MacroonCaveat) Verify(value string) error {
|
||||
switch c {
|
||||
case OriginMacroonCaveat:
|
||||
return nil
|
||||
case ScopesMacroonCaveat:
|
||||
return nil
|
||||
case SubjectMacroonCaveat:
|
||||
return nil
|
||||
case ExpMacroonCaveat:
|
||||
// Check if the expiration time is still valid
|
||||
exp, err := time.Parse(time.RFC3339, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if time.Now().After(exp) {
|
||||
return fmt.Errorf("expired")
|
||||
}
|
||||
return nil
|
||||
case TokenMacroonCaveat:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unknown caveat: %s", c)
|
||||
}
|
||||
}
|
||||
|
||||
var MacroonCaveats = []MacroonCaveat{OriginMacroonCaveat, ScopesMacroonCaveat, SubjectMacroonCaveat, ExpMacroonCaveat, TokenMacroonCaveat}
|
||||
@@ -1,43 +0,0 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func CheckSubjectIsValid(e echo.Context) error {
|
||||
credentialID := e.FormValue("credentialID")
|
||||
return e.JSON(200, credentialID)
|
||||
}
|
||||
|
||||
func HandleCredentialAssertion(e echo.Context) error {
|
||||
return e.JSON(200, "HandleCredentialAssertion")
|
||||
}
|
||||
|
||||
func HandleCredentialCreation(e echo.Context) error {
|
||||
// Get the serialized credential data from the form
|
||||
credentialDataJSON := e.FormValue("credentialData")
|
||||
|
||||
// Deserialize the JSON into a temporary struct
|
||||
var ccr protocol.CredentialCreationResponse
|
||||
err := json.Unmarshal([]byte(credentialDataJSON), &ccr)
|
||||
if err != nil {
|
||||
return e.JSON(500, err.Error())
|
||||
}
|
||||
//
|
||||
// // Parse the CredentialCreationResponse
|
||||
// parsedData, err := ccr.Parse()
|
||||
// if err != nil {
|
||||
// return e.JSON(500, err.Error())
|
||||
// }
|
||||
//
|
||||
// // Create the Credential
|
||||
// // credential := orm.NewCredential(parsedData, e.Request().Host, "")
|
||||
//
|
||||
// // Set additional fields
|
||||
// credential.Controller = "" // Set this to the appropriate controller value
|
||||
return e.JSON(200, fmt.Sprintf("REGISTER: %s", string(ccr.ID)))
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package state
|
||||
@@ -1,23 +0,0 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func GrantAuthorization(e echo.Context) error {
|
||||
// Implement authorization endpoint using passkey authentication
|
||||
// Store session data in cache
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetJWKS(e echo.Context) error {
|
||||
// Implement token endpoint
|
||||
// Use cached session data for validation
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetToken(e echo.Context) error {
|
||||
// Implement token endpoint
|
||||
// Use cached session data for validation
|
||||
return nil
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package state
|
||||
Reference in New Issue
Block a user