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