tiny 88x31 lexicon for atproto
1package db
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "strings"
8
9 "github.com/bluesky-social/indigo/atproto/auth/oauth"
10 "github.com/bluesky-social/indigo/atproto/syntax"
11)
12
13func (s Store) GetSession(ctx context.Context, did syntax.DID, sessionID string) (*oauth.ClientSessionData, error) {
14 row := s.pool.QueryRow(ctx, `
15 SELECT
16 host_url,
17 authserver_url,
18 authserver_token_endpoint,
19 scopes,
20 access_token,
21 refresh_token,
22 dpop_authserver_nonce,
23 dpop_host_nonce,
24 dpop_privatekey_multibase
25 FROM sessions
26 WHERE account_did = $1 AND session_id = $2`, did.String(), sessionID)
27 var scope string
28 var csd oauth.ClientSessionData
29 csd.AccountDID = did
30 csd.SessionID = sessionID
31 err := row.Scan(&csd.HostURL,
32 &csd.AuthServerURL,
33 &csd.AuthServerTokenEndpoint,
34 &scope,
35 &csd.AccessToken,
36 &csd.RefreshToken,
37 &csd.DPoPAuthServerNonce,
38 &csd.DPoPHostNonce,
39 &csd.DPoPPrivateKeyMultibase,
40 )
41 if err != nil {
42 return nil, errors.New("error scanning: " + err.Error())
43 }
44 scopes := strings.Fields(scope)
45 csd.Scopes = scopes
46 return &csd, nil
47}
48
49func (s Store) SaveSession(ctx context.Context, sess oauth.ClientSessionData) error {
50 scope := strings.Join(sess.Scopes, " ")
51 _, err := s.pool.Exec(ctx, `
52 INSERT INTO sessions (
53 session_id,
54 account_did,
55 host_url,
56 authserver_url,
57 authserver_token_endpoint,
58 scopes,
59 access_token,
60 refresh_token,
61 dpop_authserver_nonce,
62 dpop_host_nonce,
63 dpop_privatekey_multibase
64 ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
65 ON CONFLICT (session_id)
66 DO UPDATE SET
67 account_did = EXCLUDED.account_did,
68 host_url = EXCLUDED.host_url,
69 authserver_url = EXCLUDED.authserver_url,
70 authserver_token_endpoint = EXCLUDED.authserver_token_endpoint,
71 scopes = EXCLUDED.scopes,
72 access_token = EXCLUDED.access_token,
73 refresh_token = EXCLUDED.refresh_token,
74 dpop_authserver_nonce = EXCLUDED.dpop_authserver_nonce,
75 dpop_host_nonce = EXCLUDED.dpop_host_nonce,
76 dpop_privatekey_multibase = EXCLUDED.dpop_privatekey_multibase
77 `,
78 sess.SessionID,
79 sess.AccountDID.String(),
80 sess.HostURL,
81 sess.AuthServerURL,
82 sess.AuthServerTokenEndpoint,
83 scope,
84 sess.AccessToken,
85 sess.RefreshToken,
86 sess.DPoPAuthServerNonce,
87 sess.DPoPHostNonce,
88 sess.DPoPPrivateKeyMultibase,
89 )
90 if err != nil {
91 return errors.New("failed to insert: " + err.Error())
92 }
93 return nil
94}
95
96func (s Store) DeleteSession(ctx context.Context, did syntax.DID, sessionID string) error {
97 _, err := s.pool.Exec(ctx, `DELETE FROM sessions WHERE account_did = $1 AND session_id = $2`, did.String(), sessionID)
98 if err != nil {
99 return errors.New("failed to delete: " + err.Error())
100 }
101 return nil
102}
103
104func (s Store) DeleteAllSessions(ctx context.Context, did string) error {
105 _, err := s.pool.Exec(ctx, `DELETE FROM sessions WHERE account_did = $1`, did)
106 return err
107}
108
109func (s Store) GetAuthRequestInfo(ctx context.Context, state string) (*oauth.AuthRequestData, error) {
110 row := s.pool.QueryRow(ctx, `
111 SELECT
112 authserver_url,
113 account_did,
114 scopes,
115 request_uri,
116 authserver_token_endpoint,
117 pkce_verifier,
118 dpop_authserver_nonce,
119 dpop_privatekey_multibase
120 FROM requests
121 WHERE state = $1
122 `, state)
123 var ari oauth.AuthRequestData
124 ari.State = state
125 var did string
126 var scope string
127 err := row.Scan(
128 &ari.AuthServerURL,
129 &did,
130 &scope,
131 &ari.RequestURI,
132 &ari.AuthServerTokenEndpoint,
133 &ari.PKCEVerifier,
134 &ari.DPoPAuthServerNonce,
135 &ari.DPoPPrivateKeyMultibase,
136 )
137 if err != nil {
138 return nil, errors.New("failed to scan: " + err.Error())
139 }
140 scopes := strings.Fields(scope)
141 ari.Scopes = scopes
142 sdid, err := syntax.ParseDID(did)
143 if err != nil {
144 return nil, errors.New("failed to parse did: " + err.Error())
145 }
146 ari.AccountDID = &sdid
147 return &ari, nil
148}
149
150func (s Store) SaveAuthRequestInfo(ctx context.Context, info oauth.AuthRequestData) error {
151 scope := strings.Join(info.Scopes, " ")
152 _, err := s.pool.Exec(ctx, `
153 INSERT INTO requests (
154 state,
155 authserver_url,
156 account_did,
157 scopes,
158 request_uri,
159 authserver_token_endpoint,
160 pkce_verifier,
161 dpop_authserver_nonce,
162 dpop_privatekey_multibase)
163 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
164 info.State,
165 info.AuthServerURL,
166 info.AccountDID,
167 scope,
168 info.RequestURI,
169 info.AuthServerTokenEndpoint,
170 info.PKCEVerifier,
171 info.DPoPAuthServerNonce,
172 info.DPoPPrivateKeyMultibase,
173 )
174 if err != nil {
175 return errors.New("failed to insert: " + err.Error())
176 }
177 return nil
178}
179
180func (s Store) DeleteAuthRequestInfo(ctx context.Context, state string) error {
181 _, err := s.pool.Exec(ctx, `DELETE FROM requests WHERE state = $1`, state)
182 if err != nil {
183 return errors.New("failed to delete: " + err.Error())
184 }
185 return nil
186}
187
188func (s *Store) SetDpopPdsNonce(id int, dpopnonce string) error {
189 _, err := s.pool.Exec(context.Background(), `
190 UPDATE oauthsessions SET dpop_pds_nonce = $1 WHERE id = $2
191 `, dpopnonce, id)
192 if err != nil {
193 return errors.New(fmt.Sprintf("error updating dpop nonce for id %d: %s", id, err.Error()))
194 }
195 return nil
196}