@@ -11,6 +11,7 @@ import (
1111 "github.com/Preloading/TwitterAPIBridge/config"
1212 authcrypt "github.com/Preloading/TwitterAPIBridge/cryption"
1313 "github.com/google/uuid"
14+ "golang.org/x/crypto/bcrypt"
1415 "gorm.io/driver/mysql"
1516 "gorm.io/driver/postgres"
1617 "gorm.io/driver/sqlite"
@@ -48,18 +49,21 @@ import (
4849
4950// Token represents the schema for the tokens table
5051type Token struct {
51- UserDid string `gorm:"type:string;primaryKey;not null"`
52+ BasicAuthHash string `gorm:"type:string;"` // I am not a fan that i have to store this for basic authentication
53+ BasicAuthUsername string `gorm:"type:string;index"` // Add this field with an index
54+ UserDid string `gorm:"type:string;index;not null"`
5255 UserPDS string `gorm:"type:string;not null"`
53- TokenUUID string `gorm:"type:string;primaryKey;not null "`
56+ TokenUUID string `gorm:"type:string;primaryKey"`
5457 EncryptedAccessToken string `gorm:"type:string;not null"`
5558 EncryptedRefreshToken string `gorm:"type:string;not null"`
5659 AccessExpiry float64 `gorm:"type:float;not null"`
5760 RefreshExpiry float64 `gorm:"type:float;not null"`
61+ BasicAuthSalt string `gorm:"type:string"`
5862}
5963
6064type TwitterIDs struct {
6165 BlueskyID string `gorm:"type:string;not null"`
62- TwitterID string `gorm:"type:string;primaryKey;not null"` // Ensure this has a unique constraint
66+ TwitterID string `gorm:"type:string;primaryKey;not null"`
6367 ReposterDid * string `gorm:"type:string"`
6468 DateCreated * time.Time `gorm:"type:timestamp"`
6569}
@@ -187,6 +191,55 @@ func UpdateToken(uuid string, did string, pds string, accessToken string, refres
187191 return & token .TokenUUID , nil
188192}
189193
194+ // StoreTokenBasic stores a token using basic auth (password)
195+ func StoreTokenBasic (did string , pds string , accessToken string , refreshToken string , username string , password string , accessExpiry float64 , refreshExpiry float64 ) (* string , error ) {
196+ passwordData , err := authcrypt .GeneratePasswordHash (password )
197+ if err != nil {
198+ return nil , err
199+ }
200+
201+ // generate a new UUID for the token
202+ uuid , err := uuid .NewRandom ()
203+ if err != nil {
204+ return nil , err
205+ }
206+
207+ return UpdateTokenBasic (did , pds , accessToken , refreshToken , accessExpiry , refreshExpiry , username , password , passwordData .Hash , passwordData .Salt , uuid .String ())
208+ }
209+
210+ // UpdateTokenBasic updates or creates a token entry using basic auth
211+ func UpdateTokenBasic (did string , pds string , accessToken string , refreshToken string , accessExpiry float64 , refreshExpiry float64 , username string , password string , passwordHash string , passwordSalt string , uuid string ) (* string , error ) {
212+ encryptionKey := authcrypt .DeriveKeyFromPassword (password , passwordSalt )
213+ encryptedAccess , err := authcrypt .Encrypt (accessToken , encryptionKey )
214+ if err != nil {
215+ return nil , fmt .Errorf ("failed to encrypt access token: %v" , err )
216+ }
217+
218+ encryptedRefresh , err := authcrypt .Encrypt (refreshToken , encryptionKey )
219+ if err != nil {
220+ return nil , fmt .Errorf ("failed to encrypt refresh token: %v" , err )
221+ }
222+
223+ token := Token {
224+ UserDid : did ,
225+ UserPDS : pds ,
226+ EncryptedAccessToken : encryptedAccess ,
227+ EncryptedRefreshToken : encryptedRefresh ,
228+ AccessExpiry : accessExpiry ,
229+ RefreshExpiry : refreshExpiry ,
230+ BasicAuthHash : passwordHash ,
231+ BasicAuthSalt : passwordSalt ,
232+ BasicAuthUsername : username ,
233+ TokenUUID : uuid ,
234+ }
235+
236+ if err := db .Create (& token ).Error ; err != nil {
237+ return nil , err
238+ }
239+
240+ return & token .TokenUUID , nil
241+ }
242+
190243// GetToken retrieves account data from the database
191244// @results: accessToken, refreshToken, accessExpiry, refreshExpiry, pds, error
192245
@@ -209,6 +262,93 @@ func GetToken(did string, tokenUUID string, encryptionKey string) (*string, *str
209262 return & accessToken , & refreshToken , & token .AccessExpiry , & token .RefreshExpiry , & token .UserPDS , nil
210263}
211264
265+ // GetTokenViaBasic retrieves a token using only the password
266+ // @results: accessToken, refreshToken, accessExpiry, refreshExpiry, pds, did, hash, salt, uuid, error
267+ func GetTokenViaBasic (username string , password string ) (* string , * string , * float64 , * float64 , * string , * string , * string , * string , * string , error ) {
268+ var tokens []Token
269+ if err := db .Where ("basic_auth_username = ?" , username ).Find (& tokens ).Error ; err != nil {
270+ return nil , nil , nil , nil , nil , nil , nil , nil , nil , fmt .Errorf ("invalid credentials" )
271+ }
272+ var token Token
273+ if len (tokens ) == 0 {
274+ return nil , nil , nil , nil , nil , nil , nil , nil , nil , fmt .Errorf ("invalid credentials" )
275+ }
276+
277+ for _ , t := range tokens {
278+ if err := bcrypt .CompareHashAndPassword ([]byte (token .BasicAuthHash ), []byte (password )); err != nil {
279+ token = t
280+ break
281+ }
282+ }
283+
284+ if token .EncryptedAccessToken == "" {
285+ return nil , nil , nil , nil , nil , nil , nil , nil , nil , fmt .Errorf ("invalid credentials" )
286+ }
287+
288+ encryptionKey := authcrypt .DeriveKeyFromPassword (password , token .BasicAuthSalt )
289+
290+ accessToken , err := authcrypt .Decrypt (token .EncryptedAccessToken , encryptionKey )
291+ if err != nil {
292+ return nil , nil , nil , nil , nil , nil , nil , nil , nil , err
293+ }
294+
295+ refreshToken , err := authcrypt .Decrypt (token .EncryptedRefreshToken , encryptionKey )
296+ if err != nil {
297+ return nil , nil , nil , nil , nil , nil , nil , nil , nil , err
298+ }
299+
300+ return & accessToken , & refreshToken , & token .AccessExpiry , & token .RefreshExpiry , & token .UserPDS , & token .UserDid , & token .BasicAuthHash , & token .BasicAuthSalt , & token .TokenUUID , nil
301+ }
302+
303+ // DeleteToken deletes a token using did and uuid
304+ func DeleteToken (did string , tokenUUID string ) error {
305+ result := db .Where ("user_did = ? AND token_uuid = ?" , did , tokenUUID ).Delete (& Token {})
306+ if result .Error != nil {
307+ return result .Error
308+ }
309+ if result .RowsAffected == 0 {
310+ return fmt .Errorf ("token not found" )
311+ }
312+ return nil
313+ }
314+
315+ // DeleteTokenViaBasic deletes a token using the username and password
316+ func DeleteTokenViaBasic (username string , password string ) error {
317+ var tokens []Token
318+ if err := db .Where ("basic_auth_username = ?" , username ).Find (& tokens ).Error ; err != nil {
319+ return fmt .Errorf ("invalid credentials" )
320+ }
321+
322+ if len (tokens ) == 0 {
323+ return fmt .Errorf ("token not found" )
324+ }
325+
326+ var foundToken Token
327+ for _ , t := range tokens {
328+ if err := bcrypt .CompareHashAndPassword ([]byte (t .BasicAuthHash ), []byte (password )); err != nil {
329+ foundToken = t
330+ break
331+ }
332+ }
333+
334+ if foundToken .EncryptedAccessToken == "" {
335+ return fmt .Errorf ("invalid credentials" )
336+ }
337+
338+ result := db .Where ("basic_auth_username = ? AND basic_auth_hash = ?" ,
339+ username , foundToken .BasicAuthHash ).Delete (& Token {})
340+
341+ if result .Error != nil {
342+ return result .Error
343+ }
344+
345+ if result .RowsAffected == 0 {
346+ return fmt .Errorf ("token not found" )
347+ }
348+
349+ return nil
350+ }
351+
212352// Stores ID data in the database.
213353// @params: twitterID, blueskyID, dateCreated, reposterDid
214354// @results: error
0 commit comments