hackathon/db/token.go

79 lines
1.7 KiB
Go

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
}
e := new(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("invalid token: session expired")
}
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)
}