+3
-3
appview/cache/session/store.go
+3
-3
appview/cache/session/store.go
···
102
102
}
103
103
104
104
func (s *SessionStore) GetRequestByState(ctx context.Context, state string) (*OAuthRequest, error) {
105
-
didKey, err := s.getRequestKey(ctx, state)
105
+
didKey, err := s.getRequestKeyFromState(ctx, state)
106
106
if err != nil {
107
107
return nil, err
108
108
}
···
127
127
}
128
128
129
129
func (s *SessionStore) DeleteRequestByState(ctx context.Context, state string) error {
130
-
didKey, err := s.getRequestKey(ctx, state)
130
+
didKey, err := s.getRequestKeyFromState(ctx, state)
131
131
if err != nil {
132
132
return err
133
133
}
134
134
135
-
err = s.cache.Del(ctx, fmt.Sprintf(stateKey, "state")).Err()
135
+
err = s.cache.Del(ctx, fmt.Sprintf(stateKey, state)).Err()
136
136
if err != nil {
137
137
return err
138
138
}
+9
-5
appview/oauth/handler/handler.go
+9
-5
appview/oauth/handler/handler.go
···
13
13
"github.com/lestrrat-go/jwx/v2/jwk"
14
14
"github.com/posthog/posthog-go"
15
15
"tangled.sh/icyphox.sh/atproto-oauth/helpers"
16
+
sessioncache "tangled.sh/tangled.sh/core/appview/cache/session"
16
17
"tangled.sh/tangled.sh/core/appview/config"
17
18
"tangled.sh/tangled.sh/core/appview/db"
18
19
"tangled.sh/tangled.sh/core/appview/idresolver"
···
32
33
config *config.Config
33
34
pages *pages.Pages
34
35
idResolver *idresolver.Resolver
36
+
sess *sessioncache.SessionStore
35
37
db *db.DB
36
38
store *sessions.CookieStore
37
39
oauth *oauth.OAuth
···
44
46
pages *pages.Pages,
45
47
idResolver *idresolver.Resolver,
46
48
db *db.DB,
49
+
sess *sessioncache.SessionStore,
47
50
store *sessions.CookieStore,
48
51
oauth *oauth.OAuth,
49
52
enforcer *rbac.Enforcer,
···
54
57
pages: pages,
55
58
idResolver: idResolver,
56
59
db: db,
60
+
sess: sess,
57
61
store: store,
58
62
oauth: oauth,
59
63
enforcer: enforcer,
···
158
162
return
159
163
}
160
164
161
-
err = db.SaveOAuthRequest(o.db, db.OAuthRequest{
165
+
err = o.sess.SaveRequest(r.Context(), sessioncache.OAuthRequest{
162
166
Did: resolved.DID.String(),
163
167
PdsUrl: resolved.PDSEndpoint(),
164
168
Handle: handle,
···
186
190
func (o *OAuthHandler) callback(w http.ResponseWriter, r *http.Request) {
187
191
state := r.FormValue("state")
188
192
189
-
oauthRequest, err := db.GetOAuthRequestByState(o.db, state)
193
+
oauthRequest, err := o.sess.GetRequestByState(r.Context(), state)
190
194
if err != nil {
191
195
log.Println("failed to get oauth request:", err)
192
196
o.pages.Notice(w, "login-msg", "Failed to authenticate. Try again later.")
···
194
198
}
195
199
196
200
defer func() {
197
-
err := db.DeleteOAuthRequestByState(o.db, state)
201
+
err := o.sess.DeleteRequestByState(r.Context(), state)
198
202
if err != nil {
199
203
log.Println("failed to delete oauth request for state:", state, err)
200
204
}
···
263
267
return
264
268
}
265
269
266
-
err = o.oauth.SaveSession(w, r, oauthRequest, tokenResp)
270
+
err = o.oauth.SaveSession(w, r, *oauthRequest, tokenResp)
267
271
if err != nil {
268
272
log.Println("failed to save session:", err)
269
273
o.pages.Notice(w, "login-msg", "Failed to authenticate. Try again later.")
···
295
299
}
296
300
297
301
log.Println("session cleared successfully")
298
-
http.Redirect(w, r, "/", http.StatusFound)
302
+
o.pages.HxRedirect(w, "/login")
299
303
}
300
304
301
305
func pubKeyFromJwk(jwks string) (jwk.Key, error) {
+28
-35
appview/oauth/oauth.go
+28
-35
appview/oauth/oauth.go
···
10
10
"github.com/gorilla/sessions"
11
11
oauth "tangled.sh/icyphox.sh/atproto-oauth"
12
12
"tangled.sh/icyphox.sh/atproto-oauth/helpers"
13
+
sessioncache "tangled.sh/tangled.sh/core/appview/cache/session"
13
14
"tangled.sh/tangled.sh/core/appview/config"
14
-
"tangled.sh/tangled.sh/core/appview/db"
15
15
"tangled.sh/tangled.sh/core/appview/oauth/client"
16
16
xrpc "tangled.sh/tangled.sh/core/appview/xrpcclient"
17
17
)
18
18
19
-
type OAuthRequest struct {
20
-
ID uint
21
-
AuthserverIss string
22
-
State string
23
-
Did string
24
-
PdsUrl string
25
-
PkceVerifier string
26
-
DpopAuthserverNonce string
27
-
DpopPrivateJwk string
28
-
}
29
-
30
19
type OAuth struct {
31
-
Store *sessions.CookieStore
32
-
Db *db.DB
33
-
Config *config.Config
20
+
store *sessions.CookieStore
21
+
config *config.Config
22
+
sess *sessioncache.SessionStore
34
23
}
35
24
36
-
func NewOAuth(db *db.DB, config *config.Config) *OAuth {
25
+
func NewOAuth(config *config.Config, sess *sessioncache.SessionStore) *OAuth {
37
26
return &OAuth{
38
-
Store: sessions.NewCookieStore([]byte(config.Core.CookieSecret)),
39
-
Db: db,
40
-
Config: config,
27
+
store: sessions.NewCookieStore([]byte(config.Core.CookieSecret)),
28
+
config: config,
29
+
sess: sess,
41
30
}
42
31
}
43
32
44
-
func (o *OAuth) SaveSession(w http.ResponseWriter, r *http.Request, oreq db.OAuthRequest, oresp *oauth.TokenResponse) error {
33
+
func (o *OAuth) Stores() *sessions.CookieStore {
34
+
return o.store
35
+
}
36
+
37
+
func (o *OAuth) SaveSession(w http.ResponseWriter, r *http.Request, oreq sessioncache.OAuthRequest, oresp *oauth.TokenResponse) error {
45
38
// first we save the did in the user session
46
-
userSession, err := o.Store.Get(r, SessionName)
39
+
userSession, err := o.store.Get(r, SessionName)
47
40
if err != nil {
48
41
return err
49
42
}
···
58
51
}
59
52
60
53
// then save the whole thing in the db
61
-
session := db.OAuthSession{
54
+
session := sessioncache.OAuthSession{
62
55
Did: oreq.Did,
63
56
Handle: oreq.Handle,
64
57
PdsUrl: oreq.PdsUrl,
···
70
63
Expiry: time.Now().Add(time.Duration(oresp.ExpiresIn) * time.Second).Format(time.RFC3339),
71
64
}
72
65
73
-
return db.SaveOAuthSession(o.Db, session)
66
+
return o.sess.SaveSession(r.Context(), session)
74
67
}
75
68
76
69
func (o *OAuth) ClearSession(r *http.Request, w http.ResponseWriter) error {
77
-
userSession, err := o.Store.Get(r, SessionName)
70
+
userSession, err := o.store.Get(r, SessionName)
78
71
if err != nil || userSession.IsNew {
79
72
return fmt.Errorf("error getting user session (or new session?): %w", err)
80
73
}
81
74
82
75
did := userSession.Values[SessionDid].(string)
83
76
84
-
err = db.DeleteOAuthSessionByDid(o.Db, did)
77
+
err = o.sess.DeleteSession(r.Context(), did)
85
78
if err != nil {
86
79
return fmt.Errorf("error deleting oauth session: %w", err)
87
80
}
···
91
84
return userSession.Save(r, w)
92
85
}
93
86
94
-
func (o *OAuth) GetSession(r *http.Request) (*db.OAuthSession, bool, error) {
95
-
userSession, err := o.Store.Get(r, SessionName)
87
+
func (o *OAuth) GetSession(r *http.Request) (*sessioncache.OAuthSession, bool, error) {
88
+
userSession, err := o.store.Get(r, SessionName)
96
89
if err != nil || userSession.IsNew {
97
90
return nil, false, fmt.Errorf("error getting user session (or new session?): %w", err)
98
91
}
···
100
93
did := userSession.Values[SessionDid].(string)
101
94
auth := userSession.Values[SessionAuthenticated].(bool)
102
95
103
-
session, err := db.GetOAuthSessionByDid(o.Db, did)
96
+
session, err := o.sess.GetSession(r.Context(), did)
104
97
if err != nil {
105
98
return nil, false, fmt.Errorf("error getting oauth session: %w", err)
106
99
}
···
119
112
120
113
oauthClient, err := client.NewClient(
121
114
self.ClientID,
122
-
o.Config.OAuth.Jwks,
115
+
o.config.OAuth.Jwks,
123
116
self.RedirectURIs[0],
124
117
)
125
118
···
133
126
}
134
127
135
128
newExpiry := time.Now().Add(time.Duration(resp.ExpiresIn) * time.Second).Format(time.RFC3339)
136
-
err = db.RefreshOAuthSession(o.Db, did, resp.AccessToken, resp.RefreshToken, newExpiry)
129
+
err = o.sess.RefreshSession(r.Context(), did, resp.AccessToken, resp.RefreshToken, newExpiry)
137
130
if err != nil {
138
131
return nil, false, fmt.Errorf("error refreshing oauth session: %w", err)
139
132
}
···
155
148
}
156
149
157
150
func (a *OAuth) GetUser(r *http.Request) *User {
158
-
clientSession, err := a.Store.Get(r, SessionName)
151
+
clientSession, err := a.store.Get(r, SessionName)
159
152
160
153
if err != nil || clientSession.IsNew {
161
154
return nil
···
169
162
}
170
163
171
164
func (a *OAuth) GetDid(r *http.Request) string {
172
-
clientSession, err := a.Store.Get(r, SessionName)
165
+
clientSession, err := a.store.Get(r, SessionName)
173
166
174
167
if err != nil || clientSession.IsNew {
175
168
return ""
···
189
182
190
183
client := &oauth.XrpcClient{
191
184
OnDpopPdsNonceChanged: func(did, newNonce string) {
192
-
err := db.UpdateDpopPdsNonce(o.Db, did, newNonce)
185
+
err := o.sess.UpdateNonce(r.Context(), did, newNonce)
193
186
if err != nil {
194
187
log.Printf("error updating dpop pds nonce: %v", err)
195
188
}
···
234
227
return []string{fmt.Sprintf("%s/oauth/callback", c)}
235
228
}
236
229
237
-
clientURI := o.Config.Core.AppviewHost
230
+
clientURI := o.config.Core.AppviewHost
238
231
clientID := fmt.Sprintf("%s/oauth/client-metadata.json", clientURI)
239
232
redirectURIs := makeRedirectURIs(clientURI)
240
233
241
-
if o.Config.Core.Dev {
234
+
if o.config.Core.Dev {
242
235
clientURI = fmt.Sprintf("http://127.0.0.1:3000")
243
236
redirectURIs = makeRedirectURIs(clientURI)
244
237
+1
-3
appview/state/router.go
+1
-3
appview/state/router.go
···
97
97
98
98
r.Get("/", s.Timeline)
99
99
100
-
r.With(middleware.AuthMiddleware(s.oauth)).Post("/logout", s.Logout)
101
-
102
100
r.Route("/knots", func(r chi.Router) {
103
101
r.Use(middleware.AuthMiddleware(s.oauth))
104
102
r.Get("/", s.Knots)
···
156
154
157
155
func (s *State) OAuthRouter() http.Handler {
158
156
store := sessions.NewCookieStore([]byte(s.config.Core.CookieSecret))
159
-
oauth := oauthhandler.New(s.config, s.pages, s.idResolver, s.db, store, s.oauth, s.enforcer, s.posthog)
157
+
oauth := oauthhandler.New(s.config, s.pages, s.idResolver, s.db, s.sess, store, s.oauth, s.enforcer, s.posthog)
160
158
return oauth.Router()
161
159
}
162
160
+9
-8
appview/state/state.go
+9
-8
appview/state/state.go
···
20
20
"github.com/posthog/posthog-go"
21
21
"tangled.sh/tangled.sh/core/api/tangled"
22
22
"tangled.sh/tangled.sh/core/appview"
23
+
"tangled.sh/tangled.sh/core/appview/cache"
24
+
"tangled.sh/tangled.sh/core/appview/cache/session"
23
25
"tangled.sh/tangled.sh/core/appview/config"
24
26
"tangled.sh/tangled.sh/core/appview/db"
25
27
"tangled.sh/tangled.sh/core/appview/idresolver"
···
37
39
enforcer *rbac.Enforcer
38
40
tidClock syntax.TIDClock
39
41
pages *pages.Pages
42
+
sess *session.SessionStore
40
43
idResolver *idresolver.Resolver
41
44
posthog posthog.Client
42
45
jc *jetstream.JetstreamClient
···
65
68
res = idresolver.DefaultResolver()
66
69
}
67
70
68
-
oauth := oauth.NewOAuth(d, config)
71
+
cache := cache.New(config.Redis.Addr)
72
+
sess := session.New(cache)
73
+
74
+
oauth := oauth.NewOAuth(config, sess)
69
75
70
76
posthog, err := posthog.NewWithConfig(config.Posthog.ApiKey, posthog.Config{Endpoint: config.Posthog.Endpoint})
71
77
if err != nil {
···
104
110
enforcer,
105
111
clock,
106
112
pgs,
113
+
sess,
107
114
res,
108
115
posthog,
109
116
jc,
···
118
125
return c.Next().String()
119
126
}
120
127
121
-
func (s *State) Logout(w http.ResponseWriter, r *http.Request) {
122
-
s.oauth.ClearSession(r, w)
123
-
w.Header().Set("HX-Redirect", "/login")
124
-
w.WriteHeader(http.StatusSeeOther)
125
-
}
126
-
127
128
func (s *State) Timeline(w http.ResponseWriter, r *http.Request) {
128
129
user := s.oauth.GetUser(r)
129
130
···
176
177
177
178
return
178
179
case http.MethodPost:
179
-
session, err := s.oauth.Store.Get(r, oauth.SessionName)
180
+
session, err := s.oauth.Stores().Get(r, oauth.SessionName)
180
181
if err != nil || session.IsNew {
181
182
log.Println("unauthorized attempt to generate registration key")
182
183
http.Error(w, "Forbidden", http.StatusUnauthorized)