diff --git a/auth_handlers.go b/auth_handlers.go index 8b10fac..3981d80 100644 --- a/auth_handlers.go +++ b/auth_handlers.go @@ -2,13 +2,10 @@ package statusphere import ( _ "embed" - "fmt" "log/slog" "net/http" - "net/url" - "github.com/gorilla/sessions" - "github.com/willdot/statusphere-go/oauth" + "github.com/bluesky-social/indigo/atproto/syntax" ) type LoginData struct { @@ -18,8 +15,8 @@ type LoginData struct { func (s *Server) authMiddleware(next func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - _, ok := s.getDidFromSession(r) - if !ok { + did, _ := s.currentSessionDID(r) + if did == nil { http.Redirect(w, r, "/login", http.StatusFound) return } @@ -48,7 +45,7 @@ func (s *Server) HandlePostLogin(w http.ResponseWriter, r *http.Request) { handle := r.FormValue("handle") - result, err := s.oauthService.StartOAuthFlow(r.Context(), handle) + redirectURL, err := s.oauthClient.StartAuthFlow(r.Context(), handle) if err != nil { slog.Error("starting oauth flow", "error", err) data.Error = "error logging in" @@ -56,98 +53,27 @@ func (s *Server) HandlePostLogin(w http.ResponseWriter, r *http.Request) { return } - u, _ := url.Parse(result.AuthorizationEndpoint) - u.RawQuery = fmt.Sprintf("client_id=%s&request_uri=%s", url.QueryEscape(fmt.Sprintf("%s/client-metadata.json", s.host)), result.RequestURI) - - // ignore error here as it only returns an error for decoding an existing session but it will always return a session anyway which - // is what we want - session, _ := s.sessionStore.Get(r, "oauth-session") - session.Values = map[any]any{} - - session.Options = &sessions.Options{ - Path: "/", - MaxAge: 300, // save for five minutes - HttpOnly: true, - } - - session.Values["oauth_state"] = result.State - session.Values["oauth_did"] = result.DID - - err = session.Save(r, w) - if err != nil { - slog.Error("save session", "error", err) - data.Error = "error logging in" - tmpl.Execute(w, data) - return - } - - http.Redirect(w, r, u.String(), http.StatusFound) + http.Redirect(w, r, redirectURL, http.StatusFound) } func (s *Server) handleOauthCallback(w http.ResponseWriter, r *http.Request) { tmpl := s.getTemplate("login.html") data := LoginData{} - resState := r.FormValue("state") - resIss := r.FormValue("iss") - resCode := r.FormValue("code") - - session, err := s.sessionStore.Get(r, "oauth-session") - if err != nil { - slog.Error("getting session", "error", err) - data.Error = "error logging in" - tmpl.Execute(w, data) - return - } - - if resState == "" || resIss == "" || resCode == "" { - slog.Error("request missing needed parameters") - data.Error = "error logging in" - tmpl.Execute(w, data) - return - } - - sessionState, ok := session.Values["oauth_state"].(string) - if !ok { - slog.Error("oauth_state not found in sesssion") - data.Error = "error logging in" - tmpl.Execute(w, data) - return - } - - if resState != sessionState { - slog.Error("session state does not match response state") - data.Error = "error logging in" - tmpl.Execute(w, data) - return - } - - params := oauth.CallBackParams{ - Iss: resIss, - State: resState, - Code: resCode, - } - usersDID, err := s.oauthService.OAuthCallback(r.Context(), params) + sessData, err := s.oauthClient.ProcessCallback(r.Context(), r.URL.Query()) if err != nil { - slog.Error("handling oauth callback", "error", err) + slog.Error("processing OAuth callback", "error", err) data.Error = "error logging in" tmpl.Execute(w, data) return } - session.Options = &sessions.Options{ - Path: "/", - MaxAge: 86400 * 7, - HttpOnly: true, - } - - // make sure the session is empty before setting new values - session.Values = map[any]any{} - session.Values["did"] = usersDID - - err = session.Save(r, w) - if err != nil { - slog.Error("save session", "error", err) + // create signed cookie session, indicating account DID + sess, _ := s.sessionStore.Get(r, "oauth-demo") + sess.Values["account_did"] = sessData.AccountDID.String() + sess.Values["session_id"] = sessData.SessionID + if err := sess.Save(r, w); err != nil { + slog.Error("storing session data", "error", err) data.Error = "error logging in" tmpl.Execute(w, data) return @@ -157,34 +83,38 @@ func (s *Server) handleOauthCallback(w http.ResponseWriter, r *http.Request) { } func (s *Server) HandleLogOut(w http.ResponseWriter, r *http.Request) { - session, err := s.sessionStore.Get(r, "oauth-session") - if err != nil { - slog.Error("getting session", "error", err) - http.Redirect(w, r, "/", http.StatusFound) - return - } - - did, ok := session.Values["did"] - if ok { - err = s.oauthService.DeleteOAuthSession(fmt.Sprintf("%s", did)) + did, sessionID := s.currentSessionDID(r) + if did != nil { + err := s.oauthClient.Store.DeleteSession(r.Context(), *did, sessionID) if err != nil { slog.Error("deleting oauth session", "error", err) } } - session.Values = map[any]any{} - session.Options = &sessions.Options{ - Path: "/", - MaxAge: -1, - HttpOnly: true, + sess, _ := s.sessionStore.Get(r, "oauth-demo") + sess.Values = make(map[any]any) + err := sess.Save(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return } + http.Redirect(w, r, "/", http.StatusFound) +} - err = session.Save(r, w) +func (s *Server) currentSessionDID(r *http.Request) (*syntax.DID, string) { + sess, _ := s.sessionStore.Get(r, "oauth-demo") + accountDID, ok := sess.Values["account_did"].(string) + if !ok || accountDID == "" { + return nil, "" + } + did, err := syntax.ParseDID(accountDID) if err != nil { - slog.Error("save session", "error", err) - http.Redirect(w, r, "/", http.StatusFound) - return + return nil, "" + } + sessionID, ok := sess.Values["session_id"].(string) + if !ok || sessionID == "" { + return nil, "" } - http.Redirect(w, r, "/", http.StatusFound) + return &did, sessionID } diff --git a/cmd/main.go b/cmd/main.go index 8533e49..42ae73b 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -3,6 +3,7 @@ package main import ( "context" "errors" + "fmt" "log" "log/slog" "net/http" @@ -13,10 +14,10 @@ import ( "time" "github.com/avast/retry-go/v4" + "github.com/bluesky-social/indigo/atproto/auth/oauth" "github.com/joho/godotenv" "github.com/willdot/statusphere-go" "github.com/willdot/statusphere-go/database" - "github.com/willdot/statusphere-go/oauth" ) const ( @@ -60,13 +61,25 @@ func main() { }, } - oauthService, err := oauth.NewService(db, host, httpClient) - if err != nil { - slog.Error("creating new oauth service", "error", err) - return + var config oauth.ClientConfig + bind := ":8080" + scopes := []string{"atproto", "transition:generic"} + if host == "" { + config = oauth.NewLocalhostConfig( + fmt.Sprintf("http://127.0.0.1%s/oauth/callback", bind), + scopes, + ) + slog.Info("configuring localhost OAuth client", "CallbackURL", config.CallbackURL) + } else { + config = oauth.NewPublicConfig( + fmt.Sprintf("%s/oauth/client-metadata.json", host), + fmt.Sprintf("%s/oauth/oauth-callback", host), + scopes, + ) } + oauthClient := oauth.NewClientApp(&config, db) - server, err := statusphere.NewServer(host, 8080, db, oauthService, httpClient) + server, err := statusphere.NewServer(host, 8080, db, oauthClient, httpClient) if err != nil { slog.Error("create new server", "error", err) return diff --git a/database/oauth_requests.go b/database/oauth_requests.go index 7e12561..70034ee 100644 --- a/database/oauth_requests.go +++ b/database/oauth_requests.go @@ -1,24 +1,28 @@ package database import ( + "context" "database/sql" "fmt" "log/slog" - "github.com/willdot/statusphere-go/oauth" + "github.com/bluesky-social/indigo/atproto/auth/oauth" + "github.com/bluesky-social/indigo/atproto/syntax" ) func createOauthRequestsTable(db *sql.DB) error { createOauthRequestsTableSQL := `CREATE TABLE IF NOT EXISTS oauthrequests ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "authserverIss" TEXT, "state" TEXT, - "did" TEXT, - "pdsUrl" TEXT, + "authServerURL" TEXT, + "accountDID" TEXT, + "scope" TEXT, + "requestURI" TEXT, + "authServerTokenEndpoint" TEXT, "pkceVerifier" TEXT, "dpopAuthserverNonce" TEXT, - "dpopPrivateJwk" TEXT, - UNIQUE(did,state) + "dpopPrivateKeyMultibase" TEXT, + UNIQUE(state) );` slog.Info("Create oauthrequests table...") @@ -35,36 +39,52 @@ func createOauthRequestsTable(db *sql.DB) error { return nil } -func (d *DB) CreateOauthRequest(request oauth.Request) error { - sql := `INSERT INTO oauthrequests (authserverIss, state, did, pdsUrl, pkceVerifier, dpopAuthServerNonce, dpopPrivateJwk) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(did,state) DO NOTHING;` - _, err := d.db.Exec(sql, request.AuthserverIss, request.State, request.Did, request.PdsURL, request.PkceVerifier, request.DpopAuthserverNonce, request.DpopPrivateJwk) +func (d *DB) SaveAuthRequestInfo(ctx context.Context, info oauth.AuthRequestData) error { + did := "" + if info.AccountDID != nil { + did = info.AccountDID.String() + } + + sql := `INSERT INTO oauthrequests (state, authServerURL, accountDID, scope, requestURI, authServerTokenEndpoint, pkceVerifier, dpopAuthserverNonce, dpopPrivateKeyMultibase) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(state) DO NOTHING;` + _, err := d.db.Exec(sql, info.State, info.AuthServerURL, did, info.Scope, info.RequestURI, info.AuthServerTokenEndpoint, info.PKCEVerifier, info.DPoPAuthServerNonce, info.DPoPPrivateKeyMultibase) if err != nil { + slog.Error("saving auth request info", "error", err) return fmt.Errorf("exec insert oauth request: %w", err) } return nil } -func (d *DB) GetOauthRequest(state string) (oauth.Request, error) { - var oauthRequest oauth.Request - sql := "SELECT authserverIss, state, did, pdsUrl, pkceVerifier, dpopAuthServerNonce, dpopPrivateJwk FROM oauthrequests WHERE state = ?;" +func (d *DB) GetAuthRequestInfo(ctx context.Context, state string) (*oauth.AuthRequestData, error) { + var oauthRequest oauth.AuthRequestData + sql := "SELECT state, authServerURL, accountDID, scope, requestURI, authServerTokenEndpoint, pkceVerifier, dpopAuthserverNonce, dpopPrivateKeyMultibase FROM oauthrequests where state = ?;" rows, err := d.db.Query(sql, state) if err != nil { - return oauthRequest, fmt.Errorf("run query to get oauth request: %w", err) + return nil, fmt.Errorf("run query to get oauth request: %w", err) } defer rows.Close() + var did string + for rows.Next() { - if err := rows.Scan(&oauthRequest.AuthserverIss, &oauthRequest.State, &oauthRequest.Did, &oauthRequest.PdsURL, &oauthRequest.PkceVerifier, &oauthRequest.DpopAuthserverNonce, &oauthRequest.DpopPrivateJwk); err != nil { - return oauthRequest, fmt.Errorf("scan row: %w", err) + if err := rows.Scan(&oauthRequest.State, &oauthRequest.AuthServerURL, &did, &oauthRequest.Scope, &oauthRequest.RequestURI, &oauthRequest.AuthServerTokenEndpoint, &oauthRequest.PKCEVerifier, &oauthRequest.DPoPAuthServerNonce, &oauthRequest.DPoPPrivateKeyMultibase); err != nil { + return nil, fmt.Errorf("scan row: %w", err) + } + + if did != "" { + parsedDID, err := syntax.ParseDID(did) + if err != nil { + return nil, fmt.Errorf("invalid DID stored in record: %w", err) + } + oauthRequest.AccountDID = &parsedDID } - return oauthRequest, nil + return &oauthRequest, nil } - return oauthRequest, fmt.Errorf("not found") + return nil, fmt.Errorf("not found") } -func (d *DB) DeleteOauthRequest(state string) error { +func (d *DB) DeleteAuthRequestInfo(ctx context.Context, state string) error { sql := "DELETE FROM oauthrequests WHERE state = ?;" _, err := d.db.Exec(sql, state) if err != nil { diff --git a/database/oauth_sessions.go b/database/oauth_sessions.go index 48ea2d3..41c9783 100644 --- a/database/oauth_sessions.go +++ b/database/oauth_sessions.go @@ -1,26 +1,31 @@ package database import ( + "context" "database/sql" + "encoding/json" "fmt" "log/slog" - "github.com/willdot/statusphere-go/oauth" + "github.com/bluesky-social/indigo/atproto/auth/oauth" + "github.com/bluesky-social/indigo/atproto/syntax" ) func createOauthSessionsTable(db *sql.DB) error { createOauthSessionsTableSQL := `CREATE TABLE IF NOT EXISTS oauthsessions ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, - "did" TEXT, - "pdsUrl" TEXT, - "authserverIss" TEXT, + "accountDID" TEXT, + "sessionID" TEXT, + "hostURL" TEXT, + "authServerURL" TEXT, + "authServerTokenEndpoint" TEXT, + "scopes" TEXT, "accessToken" TEXT, "refreshToken" TEXT, - "dpopPdsNonce" TEXT, - "dpopAuthserverNonce" TEXT, - "dpopPrivateJwk" TEXT, - "expiration" integer, - UNIQUE(did) + "dpopAuthServerNonce" TEXT, + "dpopHostNonce" TEXT, + "dpopPrivateKeyMultibase" TEXT, + UNIQUE(accountDID) );` slog.Info("Create oauthsessions table...") @@ -37,58 +42,56 @@ func createOauthSessionsTable(db *sql.DB) error { return nil } -func (d *DB) CreateOauthSession(session oauth.Session) error { - sql := `INSERT INTO oauthsessions (did, pdsUrl, authserverIss, accessToken, refreshToken, dpopPdsNonce, dpopAuthserverNonce, dpopPrivateJwk, expiration) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(did) DO NOTHING;` // TODO: update on conflict - _, err := d.db.Exec(sql, session.Did, session.PdsUrl, session.AuthserverIss, session.AccessToken, session.RefreshToken, session.DpopPdsNonce, session.DpopAuthserverNonce, session.DpopPrivateJwk, session.Expiration) +func (d *DB) SaveSession(ctx context.Context, sess oauth.ClientSessionData) error { + scopes, err := json.Marshal(sess.Scopes) if err != nil { + return fmt.Errorf("marshalling scopes: %w", err) + } + + slog.Info("session to save", "did", sess.AccountDID.String(), "session id", sess.SessionID) + + sql := `INSERT INTO oauthsessions (accountDID, sessionID, hostURL, authServerURL, authServerTokenEndpoint, scopes, accessToken, refreshToken, dpopAuthServerNonce, dpopHostNonce, dpopPrivateKeyMultibase) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(accountDID) DO NOTHING;` // TODO: update on conflict + _, err = d.db.Exec(sql, sess.AccountDID.String(), sess.SessionID, sess.HostURL, sess.AuthServerURL, sess.AuthServerTokenEndpoint, string(scopes), sess.AccessToken, sess.RefreshToken, sess.DPoPAuthServerNonce, sess.DPoPHostNonce, sess.DPoPPrivateKeyMultibase) + if err != nil { + slog.Error("saving session", "error", err) return fmt.Errorf("exec insert oauth session: %w", err) } return nil } -func (d *DB) GetOauthSession(did string) (oauth.Session, error) { - var session oauth.Session - sql := "SELECT * FROM oauthsessions WHERE did = ?;" - rows, err := d.db.Query(sql, did) +func (d *DB) GetSession(ctx context.Context, did syntax.DID, sessionID string) (*oauth.ClientSessionData, error) { + var session oauth.ClientSessionData + sql := "SELECT hostURL, authServerURL, authServerTokenEndpoint, scopes, accessToken, refreshToken, dpopAuthServerNonce, dpopHostNonce, dpopPrivateKeyMultibase FROM oauthsessions where accountDID = ? AND sessionID = ?;" + rows, err := d.db.Query(sql, did.String(), sessionID) if err != nil { - return session, fmt.Errorf("run query to get oauth session: %w", err) + return nil, fmt.Errorf("run query to get oauth session: %w", err) } defer rows.Close() + scopes := "" for rows.Next() { - if err := rows.Scan(&session.ID, &session.Did, &session.PdsUrl, &session.AuthserverIss, &session.AccessToken, &session.RefreshToken, &session.DpopPdsNonce, &session.DpopAuthserverNonce, &session.DpopPrivateJwk, &session.Expiration); err != nil { - return session, fmt.Errorf("scan row: %w", err) + if err := rows.Scan(&session.HostURL, &session.AuthServerURL, &session.AuthServerTokenEndpoint, &scopes, &session.AccessToken, &session.RefreshToken, &session.DPoPAuthServerNonce, &session.DPoPHostNonce, &session.DPoPPrivateKeyMultibase); err != nil { + return nil, fmt.Errorf("scan row: %w", err) } + session.AccountDID = did - return session, nil - } - return session, fmt.Errorf("not found") -} - -func (d *DB) UpdateOauthSession(accessToken, refreshToken, dpopAuthServerNonce, did string, expiration int64) error { - sql := `UPDATE oauthsessions SET accessToken = ?, refreshToken = ?, dpopAuthserverNonce = ?, expiration = ? where did = ?` - _, err := d.db.Exec(sql, accessToken, refreshToken, dpopAuthServerNonce, expiration, did) - if err != nil { - return fmt.Errorf("exec update oauth session: %w", err) - } + var parsedScopes []string + err = json.Unmarshal([]byte(scopes), &parsedScopes) + if err != nil { + return nil, fmt.Errorf("parsing scopes: %w", err) + } - return nil -} + session.Scopes = parsedScopes -func (d *DB) UpdateOauthSessionDpopPdsNonce(dpopPdsServerNonce, did string) error { - sql := `UPDATE oauthsessions SET dpopPdsNonce = ? where did = ?` - _, err := d.db.Exec(sql, dpopPdsServerNonce, did) - if err != nil { - return fmt.Errorf("exec update oauth session dpop pds nonce: %w", err) + return &session, nil } - - return nil + return nil, fmt.Errorf("not found") } -func (d *DB) DeleteOauthSession(did string) error { - sql := "DELETE FROM oauthsessions WHERE did = ?;" - _, err := d.db.Exec(sql, did) +func (d *DB) DeleteSession(ctx context.Context, did syntax.DID, sessionID string) error { + sql := "DELETE FROM oauthsessions WHERE accountDID = ?;" + _, err := d.db.Exec(sql, did.String()) if err != nil { return fmt.Errorf("exec delete oauth session: %w", err) } diff --git a/go.mod b/go.mod index c6fc000..3dfdaf8 100644 --- a/go.mod +++ b/go.mod @@ -6,53 +6,29 @@ toolchain go1.24.2 require ( github.com/avast/retry-go/v4 v4.6.1 + github.com/bluesky-social/indigo v0.0.0-20250813051257-8be102876fb7 github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e github.com/glebarez/go-sqlite v1.22.0 github.com/gorilla/sessions v1.4.0 - github.com/haileyok/atproto-oauth-golang v0.0.2 github.com/joho/godotenv v1.5.1 - github.com/lestrrat-go/jwx/v2 v2.0.12 ) require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/bluesky-social/indigo v0.0.0-20250301025210-a4e0cc37e188 // indirect github.com/carlmjohnson/versioninfo v0.22.5 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-json v0.10.3 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/websocket v1.5.1 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-retryablehttp v0.7.5 // indirect - github.com/hashicorp/golang-lru v1.0.2 // indirect - github.com/ipfs/bbloom v0.0.4 // indirect - github.com/ipfs/go-block-format v0.2.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/ipfs/go-cid v0.4.1 // indirect - github.com/ipfs/go-datastore v0.6.0 // indirect - github.com/ipfs/go-ipfs-blockstore v1.3.1 // indirect - github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect - github.com/ipfs/go-ipfs-util v0.0.3 // indirect - github.com/ipfs/go-ipld-cbor v0.1.0 // indirect - github.com/ipfs/go-ipld-format v0.6.0 // indirect - github.com/ipfs/go-log v1.0.5 // indirect - github.com/ipfs/go-log/v2 v2.5.1 // indirect - github.com/ipfs/go-metrics-interface v0.0.1 // indirect - github.com/jbenet/goprocess v0.1.4 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/lestrrat-go/blackmagic v1.0.2 // indirect - github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc v1.0.4 // indirect - github.com/lestrrat-go/iter v1.0.2 // indirect - github.com/lestrrat-go/option v1.0.1 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect @@ -61,28 +37,27 @@ require ( github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-varint v0.0.7 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.54.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.17.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/segmentio/asm v1.2.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect + gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect + gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.29.0 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/sys v0.29.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/time v0.12.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/protobuf v1.36.7 // indirect lukechampine.com/blake3 v1.2.1 // indirect modernc.org/libc v1.37.6 // indirect modernc.org/mathutil v1.6.0 // indirect diff --git a/go.sum b/go.sum index 20d6a9d..dcdfc9a 100644 --- a/go.sum +++ b/go.sum @@ -1,72 +1,58 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk= github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bluesky-social/indigo v0.0.0-20250301025210-a4e0cc37e188 h1:1sQaG37xk08/rpmdhrmMkfQWF9kZbnfHm9Zav3bbSMk= -github.com/bluesky-social/indigo v0.0.0-20250301025210-a4e0cc37e188/go.mod h1:NVBwZvbBSa93kfyweAmKwOLYawdVHdwZ9s+GZtBBVLA= +github.com/bluesky-social/indigo v0.0.0-20250813051257-8be102876fb7 h1:FyoGfQFw/cTkDHdUTIYIHxfyUDgRS12K4o1mYC3ovRs= +github.com/bluesky-social/indigo v0.0.0-20250813051257-8be102876fb7/go.mod h1:n6QE1NDPFoi7PRbMUZmc2y7FibCqiVU4ePpsvhHUBR8= github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e h1:P/O6TDHs53gwgV845uDHI+Nri889ixksRrh4bCkCdxo= github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e/go.mod h1:WiYEeyJSdUwqoaZ71KJSpTblemUCpwJfh5oVXplK6T4= github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc= github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= -github.com/haileyok/atproto-oauth-golang v0.0.2 h1:61KPkLB615LQXR2f5x1v3sf6vPe6dOXqNpTYCgZ0Fz8= -github.com/haileyok/atproto-oauth-golang v0.0.2/go.mod h1:jcZ4GCjo5I5RuE/RsAXg1/b6udw7R4W+2rb/cGyTDK8= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs= @@ -75,8 +61,6 @@ github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= -github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= -github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ipfs-blockstore v1.3.1 h1:cEI9ci7V0sRNivqaOr0elDsamxXFxJMMMy7PTTDQNsQ= github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE= github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw= @@ -89,46 +73,18 @@ github.com/ipfs/go-ipld-format v0.6.0 h1:VEJlA2kQ3LqFSIm5Vu6eIlSxD/Ze90xtc4Meten github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg= github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= -github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= -github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= -github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= -github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= -github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= -github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= -github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= -github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= -github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx/v2 v2.0.12 h1:3d589+5w/b9b7S3DneICPW16AqTyYXB7VRjgluSDWeA= -github.com/lestrrat-go/jwx/v2 v2.0.12/go.mod h1:Mq4KN1mM7bp+5z/W5HS8aCNs5RKZ911G/0y2qUjAQuQ= -github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= -github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= @@ -145,59 +101,34 @@ github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7B github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0= github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= -github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= +github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= -github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= -github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= -github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= -github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4= github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA= +gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= +gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= +gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= @@ -206,115 +137,31 @@ go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2 go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw= diff --git a/home_handler.go b/home_handler.go index 4e83873..5e43502 100644 --- a/home_handler.go +++ b/home_handler.go @@ -56,9 +56,10 @@ func (s *Server) HandleHome(w http.ResponseWriter, r *http.Request) { data := HomeData{ AvailableStatus: Availablestatus, } - usersDid, ok := s.getDidFromSession(r) - if ok { - profile, err := s.getUserProfileForDid(usersDid) + + did, _ := s.currentSessionDID(r) + if did != nil { + profile, err := s.getUserProfileForDid(did.String()) if err != nil { slog.Error("getting logged in users profile", "error", err) } @@ -107,36 +108,50 @@ func (s *Server) HandleStatus(w http.ResponseWriter, r *http.Request) { return } - did, ok := s.getDidFromSession(r) - if !ok { - http.Error(w, "failed to get did from session", http.StatusBadRequest) + did, sessionID := s.currentSessionDID(r) + if did == nil { + http.Redirect(w, r, "/login", http.StatusFound) return } - oauthSession, err := s.oauthService.GetOauthSession(r.Context(), did) + slog.Info("session", "did", did.String(), "session id", sessionID) + + oauthSess, err := s.oauthClient.ResumeSession(r.Context(), *did, sessionID) if err != nil { - http.Error(w, "failed to get oauth session", http.StatusInternalServerError) + http.Error(w, "not authenticated", http.StatusUnauthorized) return } + c := oauthSess.APIClient() createdAt := time.Now() - uri, err := s.CreateNewStatus(r.Context(), oauthSession, status, createdAt) + + bodyReq := map[string]any{ + "repo": c.AccountDID.String(), + "collection": "xyz.statusphere.status", + "record": map[string]any{ + "status": status, + "createdAt": createdAt, + }, + } + var result CreateRecordResp + err = c.Post(r.Context(), "com.atproto.repo.createRecord", bodyReq, &result) if err != nil { slog.Error("failed to create new status", "error", err) + http.Redirect(w, r, "/", http.StatusFound) + return } - if uri != "" { - statusToStore := Status{ - URI: uri, - Did: did, - Status: status, - CreatedAt: createdAt.UnixMilli(), - IndexedAt: time.Now().UnixMilli(), - } - err = s.store.CreateStatus(statusToStore) - if err != nil { - slog.Error("failed to store status that has been created", "error", err) - } + statusToStore := Status{ + URI: result.URI, + Did: c.AccountDID.String(), + Status: status, + CreatedAt: createdAt.UnixMilli(), + IndexedAt: time.Now().UnixMilli(), + } + + err = s.store.CreateStatus(statusToStore) + if err != nil { + slog.Error("failed to store status that has been created", "error", err) } http.Redirect(w, r, "/", http.StatusFound) diff --git a/oauth/service.go b/oauth/service.go deleted file mode 100644 index 58fa101..0000000 --- a/oauth/service.go +++ /dev/null @@ -1,408 +0,0 @@ -package oauth - -import ( - "context" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "os" - "strings" - "time" - - atoauth "github.com/haileyok/atproto-oauth-golang" - oauthhelpers "github.com/haileyok/atproto-oauth-golang/helpers" - "github.com/lestrrat-go/jwx/v2/jwk" -) - -const ( - scope = "atproto transition:generic" -) - -type Request struct { - ID uint - AuthserverIss string - State string - Did string - PdsURL string - PkceVerifier string - DpopAuthserverNonce string - DpopPrivateJwk string -} - -type Session struct { - ID uint - Did string - PdsUrl string - AuthserverIss string - AccessToken string - RefreshToken string - DpopPdsNonce string - DpopAuthserverNonce string - DpopPrivateJwk string - Expiration int64 -} - -func (s *Session) CreatePrivateKey() (jwk.Key, error) { - privateJwk, err := oauthhelpers.ParseJWKFromBytes([]byte(s.DpopPrivateJwk)) - if err != nil { - return nil, fmt.Errorf("create private jwk: %w", err) - } - return privateJwk, nil -} - -type OAuthFlowResult struct { - AuthorizationEndpoint string - State string - DID string - RequestURI string -} - -type CallBackParams struct { - State string - Iss string - Code string -} - -type Store interface { - CreateOauthRequest(request Request) error - GetOauthRequest(state string) (Request, error) - DeleteOauthRequest(state string) error - CreateOauthSession(session Session) error - GetOauthSession(did string) (Session, error) - UpdateOauthSession(accessToken, refreshToken, dpopAuthServerNonce, did string, expiration int64) error - DeleteOauthSession(did string) error - UpdateOauthSessionDpopPdsNonce(dpopPdsServerNonce, did string) error -} - -type Service struct { - store Store - oauthClient *atoauth.Client - httpClient *http.Client - jwks *JWKS -} - -func NewService(store Store, serverBase string, httpClient *http.Client) (*Service, error) { - jwks, err := getJWKS() - if err != nil { - return nil, fmt.Errorf("getting JWKS: %w", err) - } - - oauthClient, err := createOauthClient(jwks, serverBase, httpClient) - if err != nil { - return nil, fmt.Errorf("create oauth client: %w", err) - } - - return &Service{ - store: store, - oauthClient: oauthClient, - httpClient: httpClient, - jwks: jwks, - }, nil -} - -func (s *Service) StartOAuthFlow(ctx context.Context, handle string) (*OAuthFlowResult, error) { - usersDID, err := s.resolveHandle(handle) - if err != nil { - return nil, fmt.Errorf("resolve handle: %w", err) - } - - dpopPrivateKey, err := oauthhelpers.GenerateKey(nil) - if err != nil { - return nil, fmt.Errorf("generate private key: %w", err) - } - - parResp, meta, service, err := s.makeOAuthRequest(ctx, usersDID, handle, dpopPrivateKey) - if err != nil { - return nil, fmt.Errorf("make oauth request: %w", err) - } - - dpopPrivateKeyJson, err := json.Marshal(dpopPrivateKey) - if err != nil { - return nil, fmt.Errorf("marshal dpop private key: %w", err) - } - - oauthRequst := Request{ - AuthserverIss: meta.Issuer, - State: parResp.State, - Did: usersDID, - PkceVerifier: parResp.PkceVerifier, - DpopAuthserverNonce: parResp.DpopAuthserverNonce, - DpopPrivateJwk: string(dpopPrivateKeyJson), - PdsURL: service, - } - err = s.store.CreateOauthRequest(oauthRequst) - if err != nil { - return nil, fmt.Errorf("store oauth request: %w", err) - } - - result := OAuthFlowResult{ - AuthorizationEndpoint: meta.AuthorizationEndpoint, - State: parResp.State, - DID: usersDID, - RequestURI: parResp.RequestUri, - } - - return &result, nil -} - -func (s *Service) OAuthCallback(ctx context.Context, params CallBackParams) (string, error) { - oauthRequest, err := s.store.GetOauthRequest(fmt.Sprintf("%s", params.State)) - if err != nil { - return "", fmt.Errorf("get oauth request from store: %w", err) - } - - err = s.store.DeleteOauthRequest(fmt.Sprintf("%s", params.State)) - if err != nil { - return "", fmt.Errorf("delete oauth request from store: %w", err) - } - - jwk, err := oauthhelpers.ParseJWKFromBytes([]byte(oauthRequest.DpopPrivateJwk)) - if err != nil { - return "", fmt.Errorf("parse dpop private key: %w", err) - } - - initialTokenResp, err := s.oauthClient.InitialTokenRequest(ctx, params.Code, params.Iss, oauthRequest.PkceVerifier, oauthRequest.DpopAuthserverNonce, jwk) - if err != nil { - return "", fmt.Errorf("make oauth token request: %w", err) - } - - if initialTokenResp.Scope != scope { - return "", fmt.Errorf("incorrect scope from token request") - } - - oauthSession := Session{ - Did: oauthRequest.Did, - PdsUrl: oauthRequest.PdsURL, - AuthserverIss: oauthRequest.AuthserverIss, - AccessToken: initialTokenResp.AccessToken, - RefreshToken: initialTokenResp.RefreshToken, - DpopAuthserverNonce: initialTokenResp.DpopAuthserverNonce, - DpopPrivateJwk: oauthRequest.DpopPrivateJwk, - Expiration: time.Now().Add(time.Duration(int(time.Second) * int(initialTokenResp.ExpiresIn))).UnixMilli(), - } - - err = s.store.CreateOauthSession(oauthSession) - if err != nil { - return "", fmt.Errorf("create oauth session in store: %w", err) - } - return oauthRequest.Did, nil -} - -func (s *Service) GetOauthSession(ctx context.Context, did string) (Session, error) { - session, err := s.store.GetOauthSession(did) - if err != nil { - return Session{}, fmt.Errorf("find oauth session: %w", err) - } - - // if the session expires in more than 5 minutes, return it - if session.Expiration > time.Now().Add(time.Minute*5).UnixMilli() { - return session, nil - } - - // refresh the session - privateJwk, err := oauthhelpers.ParseJWKFromBytes([]byte(session.DpopPrivateJwk)) - if err != nil { - return Session{}, fmt.Errorf("parse sessions private JWK: %w", err) - } - - resp, err := s.oauthClient.RefreshTokenRequest(ctx, session.RefreshToken, session.AuthserverIss, session.DpopAuthserverNonce, privateJwk) - if err != nil { - return Session{}, fmt.Errorf("refresh token: %w", err) - } - - expiration := time.Now().Add(time.Duration(int(time.Second) * int(resp.ExpiresIn))).UnixMilli() - - err = s.store.UpdateOauthSession(resp.AccessToken, resp.RefreshToken, resp.DpopAuthserverNonce, did, expiration) - if err != nil { - return Session{}, fmt.Errorf("update session after refresh: %w", err) - } - - session.AccessToken = resp.AccessToken - session.RefreshToken = resp.RefreshToken - session.DpopAuthserverNonce = resp.DpopAuthserverNonce - session.Expiration = expiration - - return session, nil -} - -func (s *Service) DeleteOAuthSession(did string) error { - err := s.store.DeleteOauthSession(did) - if err != nil { - return fmt.Errorf("delete oauth session from store: %w", err) - } - return nil -} - -func (s *Service) UpdateOAuthSessionDPopPDSNonce(did, newDPopNonce string) error { - return s.store.UpdateOauthSessionDpopPdsNonce(newDPopNonce, did) -} - -func (s *Service) PublicKey() []byte { - return s.jwks.public -} - -func (s *Service) PdsDpopJwt(method, url string, session Session, privateKey jwk.Key) (string, error) { - return atoauth.PdsDpopJwt(method, url, session.AuthserverIss, session.AccessToken, session.DpopPdsNonce, privateKey) -} - -func (s *Service) makeOAuthRequest(ctx context.Context, did, handle string, dpopPrivateKey jwk.Key) (*atoauth.SendParAuthResponse, *atoauth.OauthAuthorizationMetadata, string, error) { - service, err := s.resolveService(ctx, did) - if err != nil { - return nil, nil, "", err - } - - authserver, err := s.oauthClient.ResolvePdsAuthServer(ctx, service) - if err != nil { - return nil, nil, "", err - } - - meta, err := s.oauthClient.FetchAuthServerMetadata(ctx, authserver) - if err != nil { - return nil, nil, "", err - } - - resp, err := s.oauthClient.SendParAuthRequest(ctx, authserver, meta, handle, scope, dpopPrivateKey) - if err != nil { - return nil, nil, "", err - } - return resp, meta, service, nil -} - -func (s *Service) resolveHandle(handle string) (string, error) { - params := url.Values{ - "handle": []string{handle}, - } - reqUrl := "https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?" + params.Encode() - - resp, err := s.httpClient.Get(reqUrl) - if err != nil { - return "", fmt.Errorf("make http request: %w", err) - } - - defer resp.Body.Close() - - type did struct { - Did string - } - - b, err := io.ReadAll(resp.Body) - if err != nil { - return "", fmt.Errorf("read response body: %w", err) - } - - var resDid did - err = json.Unmarshal(b, &resDid) - if err != nil { - return "", fmt.Errorf("unmarshal response: %w", err) - } - - return resDid.Did, nil -} - -func (s *Service) resolveService(ctx context.Context, did string) (string, error) { - type Identity struct { - Service []struct { - ID string `json:"id"` - Type string `json:"type"` - ServiceEndpoint string `json:"serviceEndpoint"` - } `json:"service"` - } - - var url string - if strings.HasPrefix(did, "did:plc:") { - url = fmt.Sprintf("https://plc.directory/%s", did) - } else if strings.HasPrefix(did, "did:web:") { - url = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")) - } else { - return "", fmt.Errorf("did was not a supported did type") - } - - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) - if err != nil { - return "", err - } - - resp, err := s.httpClient.Do(req) - if err != nil { - return "", fmt.Errorf("do http request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return "", fmt.Errorf("could not find identity in plc registry") - } - - b, err := io.ReadAll(resp.Body) - if err != nil { - return "", fmt.Errorf("read response body: %w", err) - } - - var identity Identity - err = json.Unmarshal(b, &identity) - if err != nil { - return "", fmt.Errorf("unmarshal response: %w", err) - } - - var service string - for _, svc := range identity.Service { - if svc.ID == "#atproto_pds" { - service = svc.ServiceEndpoint - } - } - - if service == "" { - return "", fmt.Errorf("could not find atproto_pds service in identity services") - } - - return service, nil -} - -type JWKS struct { - public []byte - private jwk.Key -} - -func getJWKS() (*JWKS, error) { - jwksB64 := os.Getenv("PRIVATEJWKS") - if jwksB64 == "" { - return nil, fmt.Errorf("PRIVATEJWKS env not set") - } - - jwksB, err := base64.StdEncoding.DecodeString(jwksB64) - if err != nil { - return nil, fmt.Errorf("decode jwks env: %w", err) - } - - k, err := oauthhelpers.ParseJWKFromBytes([]byte(jwksB)) - if err != nil { - return nil, fmt.Errorf("parse JWK from bytes: %w", err) - } - - pubkey, err := k.PublicKey() - if err != nil { - return nil, fmt.Errorf("get public key from JWKS: %w", err) - } - - resp := oauthhelpers.CreateJwksResponseObject(pubkey) - b, err := json.Marshal(resp) - if err != nil { - return nil, fmt.Errorf("marshal public JWKS: %w", err) - } - - return &JWKS{ - public: b, - private: k, - }, nil -} - -func createOauthClient(jwks *JWKS, serverBase string, httpClient *http.Client) (*atoauth.Client, error) { - return atoauth.NewClient(atoauth.ClientArgs{ - Http: httpClient, - ClientJwk: jwks.private, - ClientId: fmt.Sprintf("%s/client-metadata.json", serverBase), - RedirectUri: fmt.Sprintf("%s/oauth-callback", serverBase), - }) -} diff --git a/server.go b/server.go index e3aa415..4830504 100644 --- a/server.go +++ b/server.go @@ -15,7 +15,7 @@ import ( "github.com/gorilla/sessions" - "github.com/willdot/statusphere-go/oauth" + "github.com/bluesky-social/indigo/atproto/auth/oauth" ) var ErrorNotFound = fmt.Errorf("not found") @@ -38,12 +38,13 @@ type Server struct { httpserver *http.Server sessionStore *sessions.CookieStore templates []*template.Template - oauthService *oauth.Service - store Store - httpClient *http.Client + + oauthClient *oauth.ClientApp + store Store + httpClient *http.Client } -func NewServer(host string, port int, store Store, oauthService *oauth.Service, httpClient *http.Client) (*Server, error) { +func NewServer(host string, port int, store Store, oauthClient *oauth.ClientApp, httpClient *http.Client) (*Server, error) { sessionStore := sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) homeTemplate, err := template.ParseFiles("./html/home.html") @@ -62,7 +63,7 @@ func NewServer(host string, port int, store Store, oauthService *oauth.Service, srv := &Server{ host: host, - oauthService: oauthService, + oauthClient: oauthClient, sessionStore: sessionStore, templates: templates, store: store, @@ -78,9 +79,9 @@ func NewServer(host string, port int, store Store, oauthService *oauth.Service, mux.HandleFunc("POST /logout", srv.HandleLogOut) mux.HandleFunc("/public/app.css", serveCSS) - mux.HandleFunc("/jwks.json", srv.serveJwks) - mux.HandleFunc("/client-metadata.json", srv.serveClientMetadata) - mux.HandleFunc("/oauth-callback", srv.handleOauthCallback) + mux.HandleFunc("/oauth/jwks.json", srv.serveJwks) + mux.HandleFunc("/oauth/client-metadata.json", srv.serveClientMetadata) + mux.HandleFunc("/oauth/oauth-callback", srv.handleOauthCallback) addr := fmt.Sprintf("0.0.0.0:%d", port) srv.httpserver = &http.Server{ @@ -113,7 +114,16 @@ func (s *Server) getTemplate(name string) *template.Template { func (s *Server) serveJwks(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") - _, _ = w.Write(s.oauthService.PublicKey()) + + public := s.oauthClient.Config.PublicJWKS() + b, err := json.Marshal(public) + if err != nil { + slog.Error("failed to marshal oauth public JWKS", "error", err) + http.Error(w, "marshal public JWKS", http.StatusInternalServerError) + return + } + + _, _ = w.Write(b) } //go:embed html/app.css @@ -125,19 +135,13 @@ func serveCSS(w http.ResponseWriter, r *http.Request) { } func (s *Server) serveClientMetadata(w http.ResponseWriter, r *http.Request) { - metadata := map[string]any{ - "client_id": fmt.Sprintf("%s/client-metadata.json", s.host), - "client_name": "Bsky-bookmark", - "client_uri": s.host, - "redirect_uris": []string{fmt.Sprintf("%s/oauth-callback", s.host)}, - "grant_types": []string{"authorization_code", "refresh_token"}, - "response_types": []string{"code"}, - "application_type": "web", - "dpop_bound_access_tokens": true, - "jwks_uri": fmt.Sprintf("%s/jwks.json", s.host), - "scope": "atproto transition:generic", - "token_endpoint_auth_method": "private_key_jwt", - "token_endpoint_auth_signing_alg": "ES256", + metadata := s.oauthClient.Config.ClientMetadata() + clientName := "statusphere-go" + metadata.ClientName = &clientName + metadata.ClientURI = &s.host + if s.oauthClient.Config.IsConfidential() { + jwksURI := fmt.Sprintf("%s/oauth/jwks.json", r.Host) + metadata.JWKSURI = &jwksURI } b, err := json.Marshal(metadata) @@ -150,21 +154,6 @@ func (s *Server) serveClientMetadata(w http.ResponseWriter, r *http.Request) { _, _ = w.Write(b) } -func (s *Server) getDidFromSession(r *http.Request) (string, bool) { - session, err := s.sessionStore.Get(r, "oauth-session") - if err != nil { - slog.Error("getting session", "error", err) - return "", false - } - - did, ok := session.Values["did"].(string) - if !ok { - return "", false - } - - return did, true -} - func (s *Server) getUserProfileForDid(did string) (UserProfile, error) { profile, err := s.store.GetHandleAndDisplayNameForDid(did) if err == nil { diff --git a/status.go b/status.go index ad8e4e7..a86f3a0 100644 --- a/status.go +++ b/status.go @@ -1,18 +1,5 @@ package statusphere -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "log/slog" - "net/http" - "time" - - "github.com/willdot/statusphere-go/oauth" -) - type Status struct { URI string Did string @@ -21,114 +8,8 @@ type Status struct { IndexedAt int64 } -type XRPCError struct { - ErrStr string `json:"error"` - Message string `json:"message"` -} - type CreateRecordResp struct { URI string `json:"uri"` ErrStr string `json:"error"` Message string `json:"message"` } - -func (s *Server) CreateNewStatus(ctx context.Context, oauthsession oauth.Session, status string, createdAt time.Time) (string, error) { - bodyReq := map[string]any{ - "repo": oauthsession.Did, - "collection": "xyz.statusphere.status", - "record": map[string]any{ - "status": status, - "createdAt": createdAt, - }, - } - - bodyB, err := json.Marshal(bodyReq) - if err != nil { - return "", fmt.Errorf("marshal update message request body: %w", err) - } - - r := bytes.NewReader(bodyB) - url := fmt.Sprintf("%s/xrpc/com.atproto.repo.createRecord", oauthsession.PdsUrl) - request, err := http.NewRequestWithContext(ctx, "POST", url, r) - if err != nil { - return "", fmt.Errorf("create http request: %w", err) - } - - request.Header.Add("Content-Type", "application/json") - request.Header.Add("Accept", "application/json") - request.Header.Set("Authorization", "DPoP "+oauthsession.AccessToken) - - privateKey, err := oauthsession.CreatePrivateKey() - if err != nil { - return "", fmt.Errorf("create private key: %w", err) - } - - // try a maximum of 2 times to make the request. If the first attempt fails because the server returns an unauthorized due to a new use_dpop_nonce being issued, - // then try again. Otherwise just try once. - for range 2 { - dpopJwt, err := s.oauthService.PdsDpopJwt("POST", url, oauthsession, privateKey) - if err != nil { - return "", err - } - - request.Header.Set("DPoP", dpopJwt) - - resp, err := s.httpClient.Do(request) - if err != nil { - return "", fmt.Errorf("do http request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusBadRequest && resp.StatusCode != http.StatusUnauthorized { - return "", fmt.Errorf("unexpected status code returned: %d", resp.StatusCode) - } - - var result CreateRecordResp - err = decodeResp(resp.Body, &result) - if err != nil { - // just log the error. - // if a HTTP 200 is received then the record has been created and we only use the response URI to make an optimistic write to our DB, so nothing will go wrong here. - // if a HTTP 400 then we can at least log return that it was a bad request. - // if a HTTP 401 we only do something if the error string is use_dpop_nonce - slog.Error("decode response body", "error", err) - } - - slog.Info("resp", "status", resp.StatusCode) - - if resp.StatusCode == http.StatusOK { - return result.URI, nil - } - - if resp.StatusCode == http.StatusBadRequest { - return "", fmt.Errorf("bad request: %s - %s", result.Message, result.ErrStr) - } - - if resp.StatusCode == http.StatusUnauthorized && result.ErrStr == "use_dpop_nonce" { - newNonce := resp.Header.Get("DPoP-Nonce") - oauthsession.DpopPdsNonce = newNonce - err := s.oauthService.UpdateOAuthSessionDPopPDSNonce(oauthsession.Did, newNonce) - if err != nil { - // just log the error because we can still proceed without storing it. - slog.Error("updating oauth session in store with new DPoP PDS nonce", "error", err) - } - continue - } - - return "", fmt.Errorf("received an unauthorized status code and message: %s - %s", result.ErrStr, result.Message) - } - - return "", fmt.Errorf("failed to create status record") -} - -func decodeResp(body io.Reader, result any) error { - resBody, err := io.ReadAll(body) - if err != nil { - return fmt.Errorf("failed to read response: %w", err) - } - - err = json.Unmarshal(resBody, result) - if err != nil { - return fmt.Errorf("failed to unmarshal response: %w", err) - } - return nil -}