+37
-107
auth_handlers.go
+37
-107
auth_handlers.go
···
2
3
import (
4
_ "embed"
5
-
"fmt"
6
"log/slog"
7
"net/http"
8
-
"net/url"
9
10
-
"github.com/gorilla/sessions"
11
-
"github.com/willdot/statusphere-go/oauth"
12
)
13
14
type LoginData struct {
···
18
19
func (s *Server) authMiddleware(next func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
20
return func(w http.ResponseWriter, r *http.Request) {
21
-
_, ok := s.getDidFromSession(r)
22
-
if !ok {
23
http.Redirect(w, r, "/login", http.StatusFound)
24
return
25
}
···
48
49
handle := r.FormValue("handle")
50
51
-
result, err := s.oauthService.StartOAuthFlow(r.Context(), handle)
52
if err != nil {
53
slog.Error("starting oauth flow", "error", err)
54
data.Error = "error logging in"
···
56
return
57
}
58
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)
85
}
86
87
func (s *Server) handleOauthCallback(w http.ResponseWriter, r *http.Request) {
88
tmpl := s.getTemplate("login.html")
89
data := LoginData{}
90
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)
131
if err != nil {
132
-
slog.Error("handling oauth callback", "error", err)
133
data.Error = "error logging in"
134
tmpl.Execute(w, data)
135
return
136
}
137
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)
151
data.Error = "error logging in"
152
tmpl.Execute(w, data)
153
return
···
157
}
158
159
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))
170
if err != nil {
171
slog.Error("deleting oauth session", "error", err)
172
}
173
}
174
175
-
session.Values = map[any]any{}
176
-
session.Options = &sessions.Options{
177
-
Path: "/",
178
-
MaxAge: -1,
179
-
HttpOnly: true,
180
}
181
182
-
err = session.Save(r, w)
183
if err != nil {
184
-
slog.Error("save session", "error", err)
185
-
http.Redirect(w, r, "/", http.StatusFound)
186
-
return
187
}
188
189
-
http.Redirect(w, r, "/", http.StatusFound)
190
}
···
2
3
import (
4
_ "embed"
5
"log/slog"
6
"net/http"
7
8
+
"github.com/bluesky-social/indigo/atproto/syntax"
9
)
10
11
type LoginData struct {
···
15
16
func (s *Server) authMiddleware(next func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
17
return func(w http.ResponseWriter, r *http.Request) {
18
+
did, _ := s.currentSessionDID(r)
19
+
if did == nil {
20
http.Redirect(w, r, "/login", http.StatusFound)
21
return
22
}
···
45
46
handle := r.FormValue("handle")
47
48
+
redirectURL, err := s.oauthClient.StartAuthFlow(r.Context(), handle)
49
if err != nil {
50
slog.Error("starting oauth flow", "error", err)
51
data.Error = "error logging in"
···
53
return
54
}
55
56
+
http.Redirect(w, r, redirectURL, http.StatusFound)
57
}
58
59
func (s *Server) handleOauthCallback(w http.ResponseWriter, r *http.Request) {
60
tmpl := s.getTemplate("login.html")
61
data := LoginData{}
62
63
+
sessData, err := s.oauthClient.ProcessCallback(r.Context(), r.URL.Query())
64
if err != nil {
65
+
slog.Error("processing OAuth callback", "error", err)
66
data.Error = "error logging in"
67
tmpl.Execute(w, data)
68
return
69
}
70
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)
77
data.Error = "error logging in"
78
tmpl.Execute(w, data)
79
return
···
83
}
84
85
func (s *Server) HandleLogOut(w http.ResponseWriter, r *http.Request) {
86
+
did, sessionID := s.currentSessionDID(r)
87
+
if did != nil {
88
+
err := s.oauthClient.Store.DeleteSession(r.Context(), *did, sessionID)
89
if err != nil {
90
slog.Error("deleting oauth session", "error", err)
91
}
92
}
93
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
100
}
101
+
http.Redirect(w, r, "/", http.StatusFound)
102
+
}
103
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)
111
if err != nil {
112
+
return nil, ""
113
+
}
114
+
sessionID, ok := sess.Values["session_id"].(string)
115
+
if !ok || sessionID == "" {
116
+
return nil, ""
117
}
118
119
+
return &did, sessionID
120
}
+19
-6
cmd/main.go
+19
-6
cmd/main.go
···
3
import (
4
"context"
5
"errors"
6
"log"
7
"log/slog"
8
"net/http"
···
13
"time"
14
15
"github.com/avast/retry-go/v4"
16
"github.com/joho/godotenv"
17
"github.com/willdot/statusphere-go"
18
"github.com/willdot/statusphere-go/database"
19
-
"github.com/willdot/statusphere-go/oauth"
20
)
21
22
const (
···
60
},
61
}
62
63
-
oauthService, err := oauth.NewService(db, host, httpClient)
64
-
if err != nil {
65
-
slog.Error("creating new oauth service", "error", err)
66
-
return
67
}
68
69
-
server, err := statusphere.NewServer(host, 8080, db, oauthService, httpClient)
70
if err != nil {
71
slog.Error("create new server", "error", err)
72
return
···
3
import (
4
"context"
5
"errors"
6
+
"fmt"
7
"log"
8
"log/slog"
9
"net/http"
···
14
"time"
15
16
"github.com/avast/retry-go/v4"
17
+
"github.com/bluesky-social/indigo/atproto/auth/oauth"
18
"github.com/joho/godotenv"
19
"github.com/willdot/statusphere-go"
20
"github.com/willdot/statusphere-go/database"
21
)
22
23
const (
···
61
},
62
}
63
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
+
)
79
}
80
+
oauthClient := oauth.NewClientApp(&config, db)
81
82
+
server, err := statusphere.NewServer(host, 8080, db, oauthClient, httpClient)
83
if err != nil {
84
slog.Error("create new server", "error", err)
85
return
+38
-18
database/oauth_requests.go
+38
-18
database/oauth_requests.go
···
1
package database
2
3
import (
4
"database/sql"
5
"fmt"
6
"log/slog"
7
8
-
"github.com/willdot/statusphere-go/oauth"
9
)
10
11
func createOauthRequestsTable(db *sql.DB) error {
12
createOauthRequestsTableSQL := `CREATE TABLE IF NOT EXISTS oauthrequests (
13
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
14
-
"authserverIss" TEXT,
15
"state" TEXT,
16
-
"did" TEXT,
17
-
"pdsUrl" TEXT,
18
"pkceVerifier" TEXT,
19
"dpopAuthserverNonce" TEXT,
20
-
"dpopPrivateJwk" TEXT,
21
-
UNIQUE(did,state)
22
);`
23
24
slog.Info("Create oauthrequests table...")
···
35
return nil
36
}
37
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)
41
if err != nil {
42
return fmt.Errorf("exec insert oauth request: %w", err)
43
}
44
45
return nil
46
}
47
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 = ?;"
51
rows, err := d.db.Query(sql, state)
52
if err != nil {
53
-
return oauthRequest, fmt.Errorf("run query to get oauth request: %w", err)
54
}
55
defer rows.Close()
56
57
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)
60
}
61
62
-
return oauthRequest, nil
63
}
64
-
return oauthRequest, fmt.Errorf("not found")
65
}
66
67
-
func (d *DB) DeleteOauthRequest(state string) error {
68
sql := "DELETE FROM oauthrequests WHERE state = ?;"
69
_, err := d.db.Exec(sql, state)
70
if err != nil {
···
1
package database
2
3
import (
4
+
"context"
5
"database/sql"
6
"fmt"
7
"log/slog"
8
9
+
"github.com/bluesky-social/indigo/atproto/auth/oauth"
10
+
"github.com/bluesky-social/indigo/atproto/syntax"
11
)
12
13
func createOauthRequestsTable(db *sql.DB) error {
14
createOauthRequestsTableSQL := `CREATE TABLE IF NOT EXISTS oauthrequests (
15
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
16
"state" TEXT,
17
+
"authServerURL" TEXT,
18
+
"accountDID" TEXT,
19
+
"scope" TEXT,
20
+
"requestURI" TEXT,
21
+
"authServerTokenEndpoint" TEXT,
22
"pkceVerifier" TEXT,
23
"dpopAuthserverNonce" TEXT,
24
+
"dpopPrivateKeyMultibase" TEXT,
25
+
UNIQUE(state)
26
);`
27
28
slog.Info("Create oauthrequests table...")
···
39
return nil
40
}
41
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)
50
if err != nil {
51
+
slog.Error("saving auth request info", "error", err)
52
return fmt.Errorf("exec insert oauth request: %w", err)
53
}
54
55
return nil
56
}
57
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 = ?;"
61
rows, err := d.db.Query(sql, state)
62
if err != nil {
63
+
return nil, fmt.Errorf("run query to get oauth request: %w", err)
64
}
65
defer rows.Close()
66
67
+
var did string
68
+
69
for rows.Next() {
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)
72
}
73
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
83
}
84
+
return nil, fmt.Errorf("not found")
85
}
86
87
+
func (d *DB) DeleteAuthRequestInfo(ctx context.Context, state string) error {
88
sql := "DELETE FROM oauthrequests WHERE state = ?;"
89
_, err := d.db.Exec(sql, state)
90
if err != nil {
+45
-42
database/oauth_sessions.go
+45
-42
database/oauth_sessions.go
···
1
package database
2
3
import (
4
"database/sql"
5
"fmt"
6
"log/slog"
7
8
-
"github.com/willdot/statusphere-go/oauth"
9
)
10
11
func createOauthSessionsTable(db *sql.DB) error {
12
createOauthSessionsTableSQL := `CREATE TABLE IF NOT EXISTS oauthsessions (
13
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
14
-
"did" TEXT,
15
-
"pdsUrl" TEXT,
16
-
"authserverIss" TEXT,
17
"accessToken" TEXT,
18
"refreshToken" TEXT,
19
-
"dpopPdsNonce" TEXT,
20
-
"dpopAuthserverNonce" TEXT,
21
-
"dpopPrivateJwk" TEXT,
22
-
"expiration" integer,
23
-
UNIQUE(did)
24
);`
25
26
slog.Info("Create oauthsessions table...")
···
37
return nil
38
}
39
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)
43
if err != nil {
44
return fmt.Errorf("exec insert oauth session: %w", err)
45
}
46
47
return nil
48
}
49
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)
54
if err != nil {
55
-
return session, fmt.Errorf("run query to get oauth session: %w", err)
56
}
57
defer rows.Close()
58
59
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)
62
}
63
64
-
return session, nil
65
-
}
66
-
return session, fmt.Errorf("not found")
67
-
}
68
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
-
}
75
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)
84
}
85
-
86
-
return nil
87
}
88
89
-
func (d *DB) DeleteOauthSession(did string) error {
90
-
sql := "DELETE FROM oauthsessions WHERE did = ?;"
91
-
_, err := d.db.Exec(sql, did)
92
if err != nil {
93
return fmt.Errorf("exec delete oauth session: %w", err)
94
}
···
1
package database
2
3
import (
4
+
"context"
5
"database/sql"
6
+
"encoding/json"
7
"fmt"
8
"log/slog"
9
10
+
"github.com/bluesky-social/indigo/atproto/auth/oauth"
11
+
"github.com/bluesky-social/indigo/atproto/syntax"
12
)
13
14
func createOauthSessionsTable(db *sql.DB) error {
15
createOauthSessionsTableSQL := `CREATE TABLE IF NOT EXISTS oauthsessions (
16
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
17
+
"accountDID" TEXT,
18
+
"sessionID" TEXT,
19
+
"hostURL" TEXT,
20
+
"authServerURL" TEXT,
21
+
"authServerTokenEndpoint" TEXT,
22
+
"scopes" TEXT,
23
"accessToken" TEXT,
24
"refreshToken" TEXT,
25
+
"dpopAuthServerNonce" TEXT,
26
+
"dpopHostNonce" TEXT,
27
+
"dpopPrivateKeyMultibase" TEXT,
28
+
UNIQUE(accountDID)
29
);`
30
31
slog.Info("Create oauthsessions table...")
···
42
return nil
43
}
44
45
+
func (d *DB) SaveSession(ctx context.Context, sess oauth.ClientSessionData) error {
46
+
scopes, err := json.Marshal(sess.Scopes)
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)
57
return fmt.Errorf("exec insert oauth session: %w", err)
58
}
59
60
return nil
61
}
62
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)
67
if err != nil {
68
+
return nil, fmt.Errorf("run query to get oauth session: %w", err)
69
}
70
defer rows.Close()
71
72
+
scopes := ""
73
for rows.Next() {
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)
76
}
77
+
session.AccountDID = did
78
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
+
}
84
85
+
session.Scopes = parsedScopes
86
87
+
return &session, nil
88
}
89
+
return nil, fmt.Errorf("not found")
90
}
91
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())
95
if err != nil {
96
return fmt.Errorf("exec delete oauth session: %w", err)
97
}
+19
-44
go.mod
+19
-44
go.mod
···
6
7
require (
8
github.com/avast/retry-go/v4 v4.6.1
9
github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e
10
github.com/glebarez/go-sqlite v1.22.0
11
github.com/gorilla/sessions v1.4.0
12
-
github.com/haileyok/atproto-oauth-golang v0.0.2
13
github.com/joho/godotenv v1.5.1
14
-
github.com/lestrrat-go/jwx/v2 v2.0.12
15
)
16
17
require (
18
github.com/beorn7/perks v1.0.1 // indirect
19
-
github.com/bluesky-social/indigo v0.0.0-20250301025210-a4e0cc37e188 // indirect
20
github.com/carlmjohnson/versioninfo v0.22.5 // indirect
21
github.com/cespare/xxhash/v2 v2.3.0 // indirect
22
-
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
23
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
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
30
github.com/google/uuid v1.6.0 // indirect
31
github.com/gorilla/securecookie v1.1.2 // indirect
32
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
38
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
50
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
github.com/mattn/go-isatty v0.0.20 // indirect
57
github.com/minio/sha256-simd v1.0.1 // indirect
58
github.com/mr-tron/base58 v1.2.0 // indirect
···
61
github.com/multiformats/go-multibase v0.2.0 // indirect
62
github.com/multiformats/go-multihash v0.2.3 // indirect
63
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
70
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
71
-
github.com/segmentio/asm v1.2.0 // indirect
72
github.com/spaolacci/murmur3 v1.1.0 // indirect
73
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
75
go.opentelemetry.io/otel v1.29.0 // indirect
76
go.opentelemetry.io/otel/metric v1.29.0 // indirect
77
go.opentelemetry.io/otel/trace v1.29.0 // indirect
78
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
84
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
85
-
google.golang.org/protobuf v1.34.2 // indirect
86
lukechampine.com/blake3 v1.2.1 // indirect
87
modernc.org/libc v1.37.6 // indirect
88
modernc.org/mathutil v1.6.0 // indirect
···
6
7
require (
8
github.com/avast/retry-go/v4 v4.6.1
9
+
github.com/bluesky-social/indigo v0.0.0-20250813051257-8be102876fb7
10
github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e
11
github.com/glebarez/go-sqlite v1.22.0
12
github.com/gorilla/sessions v1.4.0
13
github.com/joho/godotenv v1.5.1
14
)
15
16
require (
17
github.com/beorn7/perks v1.0.1 // indirect
18
github.com/carlmjohnson/versioninfo v0.22.5 // indirect
19
github.com/cespare/xxhash/v2 v2.3.0 // indirect
20
+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
21
github.com/dustin/go-humanize v1.0.1 // indirect
22
github.com/goccy/go-json v0.10.3 // indirect
23
+
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
24
+
github.com/google/go-querystring v1.1.0 // indirect
25
github.com/google/uuid v1.6.0 // indirect
26
github.com/gorilla/securecookie v1.1.2 // indirect
27
github.com/gorilla/websocket v1.5.1 // indirect
28
+
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
29
github.com/ipfs/go-cid v0.4.1 // indirect
30
+
github.com/klauspost/compress v1.18.0 // indirect
31
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
32
github.com/mattn/go-isatty v0.0.20 // indirect
33
github.com/minio/sha256-simd v1.0.1 // indirect
34
github.com/mr-tron/base58 v1.2.0 // indirect
···
37
github.com/multiformats/go-multibase v0.2.0 // indirect
38
github.com/multiformats/go-multihash v0.2.3 // indirect
39
github.com/multiformats/go-varint v0.0.7 // 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
46
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
47
github.com/spaolacci/murmur3 v1.1.0 // indirect
48
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e // indirect
49
+
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
50
+
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
51
go.opentelemetry.io/otel v1.29.0 // indirect
52
go.opentelemetry.io/otel/metric v1.29.0 // indirect
53
go.opentelemetry.io/otel/trace v1.29.0 // indirect
54
go.uber.org/atomic v1.11.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
59
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
60
+
google.golang.org/protobuf v1.36.7 // indirect
61
lukechampine.com/blake3 v1.2.1 // indirect
62
modernc.org/libc v1.37.6 // indirect
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
github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=
3
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
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
6
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=
9
github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e h1:P/O6TDHs53gwgV845uDHI+Nri889ixksRrh4bCkCdxo=
10
github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e/go.mod h1:WiYEeyJSdUwqoaZ71KJSpTblemUCpwJfh5oVXplK6T4=
11
github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc=
12
github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8=
13
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
14
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
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
19
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
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
25
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
26
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
27
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
28
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
29
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
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
32
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
33
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
34
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
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
38
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
39
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
40
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=
45
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
46
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
47
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
48
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
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
51
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
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
55
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
56
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
57
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
58
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
59
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
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
63
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
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
67
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
68
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
69
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
70
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
71
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
72
github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
···
75
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
76
github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk=
77
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
github.com/ipfs/go-ipfs-blockstore v1.3.1 h1:cEI9ci7V0sRNivqaOr0elDsamxXFxJMMMy7PTTDQNsQ=
81
github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE=
82
github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw=
···
89
github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg=
90
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
91
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
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
94
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
95
github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg=
96
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
github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=
99
github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
100
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
101
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=
108
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
109
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
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
133
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
134
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
···
145
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
146
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
147
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
148
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
149
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
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
153
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
154
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0=
155
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=
164
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
165
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
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
178
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
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
191
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
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4=
196
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=
201
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
202
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
203
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
···
206
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
207
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
208
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
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
212
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=
218
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
219
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
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
224
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=
269
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
270
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=
300
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
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
303
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=
315
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
316
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
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
319
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
320
modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw=
···
1
github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=
2
github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=
3
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
4
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
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=
7
github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e h1:P/O6TDHs53gwgV845uDHI+Nri889ixksRrh4bCkCdxo=
8
github.com/bluesky-social/jetstream v0.0.0-20250414024304-d17bd81a945e/go.mod h1:WiYEeyJSdUwqoaZ71KJSpTblemUCpwJfh5oVXplK6T4=
9
github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc=
10
github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8=
11
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
12
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
13
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
14
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
15
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
16
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
17
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
18
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
19
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
20
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
21
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
22
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
23
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
24
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
25
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
26
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
27
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
28
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
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=
36
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
37
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
38
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
39
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
40
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
41
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
42
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
43
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
44
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
45
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
46
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
47
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
48
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
49
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
50
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
51
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
52
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
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=
56
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
57
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
58
github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
···
61
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
62
github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk=
63
github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8=
64
github.com/ipfs/go-ipfs-blockstore v1.3.1 h1:cEI9ci7V0sRNivqaOr0elDsamxXFxJMMMy7PTTDQNsQ=
65
github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE=
66
github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw=
···
73
github.com/ipfs/go-ipld-format v0.6.0/go.mod h1:g4QVMTn3marU3qXchwjpKPKgJv+zF+OlaKMyhJ4LHPg=
74
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
75
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
76
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
77
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
78
github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg=
79
github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY=
80
github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o=
81
github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
82
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
83
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
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=
86
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
87
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
88
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
89
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
90
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
···
101
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
102
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
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=
106
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
107
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
108
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
109
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
110
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0=
111
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=
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=
120
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
121
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
122
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
123
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
124
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
125
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
126
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4=
127
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so=
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=
132
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
133
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
134
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
···
137
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
138
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
139
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
140
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
141
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
142
+
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
143
+
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
144
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
145
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
146
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
147
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
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=
152
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
153
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
158
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
159
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
160
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
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=
163
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
164
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
165
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
166
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
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
data := HomeData{
57
AvailableStatus: Availablestatus,
58
}
59
-
usersDid, ok := s.getDidFromSession(r)
60
-
if ok {
61
-
profile, err := s.getUserProfileForDid(usersDid)
62
if err != nil {
63
slog.Error("getting logged in users profile", "error", err)
64
}
···
107
return
108
}
109
110
-
did, ok := s.getDidFromSession(r)
111
-
if !ok {
112
-
http.Error(w, "failed to get did from session", http.StatusBadRequest)
113
return
114
}
115
116
-
oauthSession, err := s.oauthService.GetOauthSession(r.Context(), did)
117
if err != nil {
118
-
http.Error(w, "failed to get oauth session", http.StatusInternalServerError)
119
return
120
}
121
122
createdAt := time.Now()
123
-
uri, err := s.CreateNewStatus(r.Context(), oauthSession, status, createdAt)
124
if err != nil {
125
slog.Error("failed to create new status", "error", err)
126
}
127
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
-
}
140
}
141
142
http.Redirect(w, r, "/", http.StatusFound)
···
56
data := HomeData{
57
AvailableStatus: Availablestatus,
58
}
59
+
60
+
did, _ := s.currentSessionDID(r)
61
+
if did != nil {
62
+
profile, err := s.getUserProfileForDid(did.String())
63
if err != nil {
64
slog.Error("getting logged in users profile", "error", err)
65
}
···
108
return
109
}
110
111
+
did, sessionID := s.currentSessionDID(r)
112
+
if did == nil {
113
+
http.Redirect(w, r, "/login", http.StatusFound)
114
return
115
}
116
117
+
slog.Info("session", "did", did.String(), "session id", sessionID)
118
+
119
+
oauthSess, err := s.oauthClient.ResumeSession(r.Context(), *did, sessionID)
120
if err != nil {
121
+
http.Error(w, "not authenticated", http.StatusUnauthorized)
122
return
123
}
124
+
c := oauthSess.APIClient()
125
126
createdAt := time.Now()
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)
138
if err != nil {
139
slog.Error("failed to create new status", "error", err)
140
+
http.Redirect(w, r, "/", http.StatusFound)
141
+
return
142
}
143
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)
155
}
156
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
16
"github.com/gorilla/sessions"
17
18
-
"github.com/willdot/statusphere-go/oauth"
19
)
20
21
var ErrorNotFound = fmt.Errorf("not found")
···
38
httpserver *http.Server
39
sessionStore *sessions.CookieStore
40
templates []*template.Template
41
-
oauthService *oauth.Service
42
-
store Store
43
-
httpClient *http.Client
44
}
45
46
-
func NewServer(host string, port int, store Store, oauthService *oauth.Service, httpClient *http.Client) (*Server, error) {
47
sessionStore := sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
48
49
homeTemplate, err := template.ParseFiles("./html/home.html")
···
62
63
srv := &Server{
64
host: host,
65
-
oauthService: oauthService,
66
sessionStore: sessionStore,
67
templates: templates,
68
store: store,
···
78
mux.HandleFunc("POST /logout", srv.HandleLogOut)
79
80
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)
84
85
addr := fmt.Sprintf("0.0.0.0:%d", port)
86
srv.httpserver = &http.Server{
···
113
114
func (s *Server) serveJwks(w http.ResponseWriter, _ *http.Request) {
115
w.Header().Set("Content-Type", "application/json")
116
-
_, _ = w.Write(s.oauthService.PublicKey())
117
}
118
119
//go:embed html/app.css
···
125
}
126
127
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",
141
}
142
143
b, err := json.Marshal(metadata)
···
148
}
149
w.Header().Set("Content-Type", "application/json")
150
_, _ = 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
}
167
168
func (s *Server) getUserProfileForDid(did string) (UserProfile, error) {
···
15
16
"github.com/gorilla/sessions"
17
18
+
"github.com/bluesky-social/indigo/atproto/auth/oauth"
19
)
20
21
var ErrorNotFound = fmt.Errorf("not found")
···
38
httpserver *http.Server
39
sessionStore *sessions.CookieStore
40
templates []*template.Template
41
+
42
+
oauthClient *oauth.ClientApp
43
+
store Store
44
+
httpClient *http.Client
45
}
46
47
+
func NewServer(host string, port int, store Store, oauthClient *oauth.ClientApp, httpClient *http.Client) (*Server, error) {
48
sessionStore := sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
49
50
homeTemplate, err := template.ParseFiles("./html/home.html")
···
63
64
srv := &Server{
65
host: host,
66
+
oauthClient: oauthClient,
67
sessionStore: sessionStore,
68
templates: templates,
69
store: store,
···
79
mux.HandleFunc("POST /logout", srv.HandleLogOut)
80
81
mux.HandleFunc("/public/app.css", serveCSS)
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)
85
86
addr := fmt.Sprintf("0.0.0.0:%d", port)
87
srv.httpserver = &http.Server{
···
114
115
func (s *Server) serveJwks(w http.ResponseWriter, _ *http.Request) {
116
w.Header().Set("Content-Type", "application/json")
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)
127
}
128
129
//go:embed html/app.css
···
135
}
136
137
func (s *Server) serveClientMetadata(w http.ResponseWriter, r *http.Request) {
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
145
}
146
147
b, err := json.Marshal(metadata)
···
152
}
153
w.Header().Set("Content-Type", "application/json")
154
_, _ = w.Write(b)
155
}
156
157
func (s *Server) getUserProfileForDid(did string) (UserProfile, error) {
-119
status.go
-119
status.go
···
1
package statusphere
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
type Status struct {
17
URI string
18
Did string
···
21
IndexedAt int64
22
}
23
24
-
type XRPCError struct {
25
-
ErrStr string `json:"error"`
26
-
Message string `json:"message"`
27
-
}
28
-
29
type CreateRecordResp struct {
30
URI string `json:"uri"`
31
ErrStr string `json:"error"`
32
Message string `json:"message"`
33
}
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
-
}