package db import ( "context" "crypto/rand" "database/sql" "encoding/base64" "errors" "fmt" "time" "github.com/hhhapz/codequest/models" "github.com/mattn/go-sqlite3" ) func (db *DB) CreateToken(ctx context.Context, user *models.User) (*models.Token, error) { tokenString := createToken(48) now := time.Now() token := &models.Token{ Token: tokenString, UserID: user.ID, CreatedAt: models.NewTime(now), } for { err := token.Insert(ctx, db.DB) if err == nil { return token, nil } var e sqlite3.Error if errors.As(err, &e) && e.ExtendedCode == sqlite3.ErrConstraintUnique { token.Token = createToken(48) continue } return nil, fmt.Errorf("could not create token: %v", err) } } var ErrInvalidToken = errors.New("no results found") func (db *DB) UserByToken(ctx context.Context, token string) (*models.User, error) { user, err := models.UserByToken(ctx, db.DB, token) if err == sql.ErrNoRows { return nil, models.NewUserError("the provided token(%q) does not exist", token) } if err != nil { return nil, err } return user, nil } func (db *DB) RevokeToken(ctx context.Context, token string) error { _, err := models.DeleteToken(ctx, db.DB, token) return err } func (db *DB) RevokeUserTokens(ctx context.Context, user *models.User) (int64, error) { res, err := models.DeleteTokens(ctx, db.DB, user.ID) if err != nil { return 0, err } return res.RowsAffected() } func createToken(size int) string { token := make([]byte, size*3/4) _, err := rand.Read(token) if err != nil { // Not being able to generate random bytes means something far // worse is wrong. panic(err) } return base64.URLEncoding.EncodeToString(token) }