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