backend for xcvr appview
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

add refreshing oauth tokens

rachel-mp4 9bf062c8 b5859356

+113 -11
+12
server/internal/db/oauth.go
··· 62 62 return nil 63 63 } 64 64 65 + func (s *Store) UpdateSession(id int, session *types.Session, ctx context.Context) error { 66 + _, err := s.pool.Exec(ctx, `UPDATE oauthsessions 67 + SET (dpop_auth_server_nonce, access_token, refresh_token) 68 + VALUES ($1, $2, $3) 69 + WHERE id = $4 70 + `, session.DpopAuthServerNonce, session.AccessToken, session.RefreshToken, id) 71 + if err != nil { 72 + return errors.New("error updating session: " + err.Error()) 73 + } 74 + return nil 75 + } 76 + 65 77 func (s *Store) GetOauthRequest(state string, ctx context.Context) (*types.OAuthRequest, error) { 66 78 row := s.pool.QueryRow(ctx, ` 67 79 SELECT
+27
server/internal/handler/oauthHandlers.go
··· 12 12 "rvcx/internal/oauth" 13 13 14 14 "github.com/gorilla/sessions" 15 + // aog "github.com/haileyok/atproto-oauth-golang" 15 16 "github.com/haileyok/atproto-oauth-golang/helpers" 16 17 ) 17 18 ··· 29 30 encoder := json.NewEncoder(w) 30 31 encoder.Encode(ro) 31 32 } 33 + 34 + // func (h *Handler) newOAuthLogin(w http.ResponseWriter, r *http.Request) { 35 + // err := r.ParseForm() 36 + // if err != nil { 37 + // h.badRequest(w, err) 38 + // return 39 + // } 40 + // clientID := oauth.GetClientMetadata().ClientId 41 + // callbackUrl := oauth.GetClientMetadata().RedirectUris[0] 42 + // k, err := oauth.GetJWKS() 43 + // if err != nil { 44 + // h.serverError(w, err) 45 + // return 46 + // } 47 + // cli, err := aog.NewClient(aog.ClientArgs{ 48 + // ClientJwk: *k, 49 + // ClientId: clientID, 50 + // RedirectUri: callbackUrl, 51 + // }) 52 + // if err != nil { 53 + // h.serverError(w, err) 54 + // return 55 + // } 56 + // cli.RefreshTokenRequest 57 + // handle := r.FormValue("handle") 58 + // } 32 59 33 60 func (h *Handler) oauthLogin(w http.ResponseWriter, r *http.Request) { 34 61 err := r.ParseForm()
+39 -4
server/internal/oauth/clientmapper.go
··· 1 1 package oauth 2 2 3 3 import ( 4 + "context" 5 + "errors" 4 6 "sync" 5 7 "time" 6 8 ) 7 9 8 10 type ClientMap struct { 11 + svc *Service 9 12 clients map[int]*OauthXRPCClient 10 13 expiry map[int]time.Time 14 + texp map[int]time.Time 11 15 mu sync.Mutex 12 16 } 13 17 14 - func NewClientMap() *ClientMap { 18 + func NewClientMap(service *Service) *ClientMap { 15 19 return &ClientMap{ 20 + svc: service, 16 21 clients: make(map[int]*OauthXRPCClient, 10), 17 22 expiry: make(map[int]time.Time, 10), 23 + texp: make(map[int]time.Time, 10), 24 + mu: sync.Mutex{}, 18 25 } 19 26 } 20 27 21 - func (c *ClientMap) Map(id int) *OauthXRPCClient { 28 + func (c *ClientMap) Map(id int, ctx context.Context) (cli *OauthXRPCClient, refreshed bool, err error) { 22 29 c.mu.Lock() 23 30 defer c.mu.Unlock() 24 - return c.clients[id] 31 + cli = c.clients[id] 32 + if cli == nil { 33 + return 34 + } 35 + 36 + texp := c.texp[id] 37 + expiry := c.expiry[id] 38 + if time.Now().After(expiry) { 39 + c.Delete(id) 40 + err = errors.New("client has expired") 41 + return 42 + } 43 + if texp.Sub(time.Now()) <= 5*time.Minute { 44 + var newexp time.Time 45 + newexp, err = c.svc.RefreshToken(ctx, cli.session) 46 + if err != nil { 47 + err = errors.New("failed to refresh expired token: " + err.Error()) 48 + return 49 + } 50 + refreshed = true 51 + c.texp[id] = newexp 52 + } 53 + return 25 54 } 26 55 27 56 func (c *ClientMap) Append(id int, client *OauthXRPCClient, expiration time.Time) { ··· 29 58 defer c.mu.Unlock() 30 59 c.clients[id] = client 31 60 c.expiry[id] = expiration 61 + c.texp[id] = time.Now() 62 + 32 63 } 33 64 34 65 func (c *ClientMap) Cleanup() { ··· 40 71 if !ok { 41 72 delete(c.expiry, id) 42 73 delete(c.clients, id) 74 + delete(c.texp, id) 43 75 continue 44 76 } 45 77 if client == nil { 46 78 delete(c.expiry, id) 47 79 delete(c.clients, id) 80 + delete(c.texp, id) 48 81 continue 49 82 } 50 - if expiry.After(now) { 83 + if now.After(expiry) { 51 84 delete(c.expiry, id) 52 85 delete(c.clients, id) 86 + delete(c.texp, id) 53 87 continue 54 88 } 55 89 } ··· 60 94 defer c.mu.Unlock() 61 95 delete(c.clients, id) 62 96 delete(c.expiry, id) 97 + delete(c.texp, id) 63 98 }
+4
server/internal/oauth/oauthclient.go
··· 41 41 } 42 42 } 43 43 44 + func (c *OauthXRPCClient) GetSession() *types.Session { 45 + return c.session 46 + } 47 + 44 48 func (c *OauthXRPCClient) getOauthSessionAuthArgs() (*oauth.XrpcAuthedRequestArgs, error) { 45 49 s := c.session 46 50 privateJwk, err := helpers.ParseJWKFromBytes([]byte(s.DpopPrivKey))
+16
server/internal/oauth/service.go
··· 158 158 } 159 159 return &oauthSession, nil 160 160 } 161 + 162 + func (s *Service) RefreshToken(ctx context.Context, session *types.Session) (newexpiry time.Time, err error) { 163 + jwk, err := helpers.ParseJWKFromBytes([]byte(session.DpopPrivKey)) 164 + if err != nil { 165 + return 166 + } 167 + resp, err := s.oauth.RefreshTokenRequest(ctx, session.RefreshToken, session.AuthserverIss, session.DpopAuthServerNonce, jwk) 168 + if err != nil { 169 + return 170 + } 171 + session.AccessToken = resp.AccessToken 172 + session.DpopAuthServerNonce = resp.DpopAuthserverNonce 173 + session.RefreshToken = resp.RefreshToken 174 + newexpiry = time.Now().Add(time.Duration(resp.ExpiresIn) * time.Second) 175 + return 176 + }
+15 -7
server/internal/recordmanager/recordmanager.go
··· 26 26 broadcaster LexBroadcaster 27 27 } 28 28 29 - func New(log *log.Logger, db *db.Store, myClient *oauth.PasswordClient) *RecordManager { 30 - clientmap := oauth.NewClientMap() 29 + func New(log *log.Logger, db *db.Store, myClient *oauth.PasswordClient, service *oauth.Service) *RecordManager { 30 + clientmap := oauth.NewClientMap(service) 31 31 return &RecordManager{log, db, myClient, clientmap, nil} 32 32 } 33 33 ··· 36 36 } 37 37 38 38 func (rm *RecordManager) getClient(id int, ctx context.Context) (*oauth.OauthXRPCClient, error) { 39 - client := rm.clientmap.Map(id) 40 - if client == nil { 41 - client, err := rm.resetClient(id, ctx) 39 + cli, refreshed, err := rm.clientmap.Map(id, ctx) 40 + if cli == nil { 41 + cli, err = rm.resetClient(id, ctx) 42 42 if err != nil { 43 43 return nil, err 44 44 } 45 - return client, nil 45 + return cli, nil 46 46 } 47 - return client, nil 47 + 48 + if err != nil { 49 + return nil, errors.New("error getting client: " + err.Error()) 50 + } 51 + if refreshed { 52 + rm.db.UpdateSession(id, cli.GetSession(), ctx) 53 + } 54 + 55 + return cli, nil 48 56 } 49 57 50 58 func (rm *RecordManager) resetClient(id int, ctx context.Context) (*oauth.OauthXRPCClient, error) {