diff --git a/internal/repositories/refreshTokens.go b/internal/repositories/refreshTokens.go index 58e5a97..16e3648 100644 --- a/internal/repositories/refreshTokens.go +++ b/internal/repositories/refreshTokens.go @@ -15,6 +15,9 @@ const ( ) type RefreshTokenTable interface { + Create(username string, token string, expiresAt time.Time) (int64, error) + GetByUsername(name string) (domain.RefreshTokenEntity, error) + DeleteById(id int64) (int64, error) } type RefreshTokenRepository struct { @@ -63,7 +66,7 @@ func (rt RefreshTokenRepository) GetByUsername(name string) (domain.RefreshToken return data[0], nil } -func (rt RefreshTokenRepository) DeleteById(id int) (int64, error) { +func (rt RefreshTokenRepository) DeleteById(id int64) (int64, error) { builder := sqlbuilder.NewDeleteBuilder() builder.DeleteFrom(refreshTokenTableName) builder.Where( @@ -71,19 +74,19 @@ func (rt RefreshTokenRepository) DeleteById(id int) (int64, error) { ) query, args := builder.Build() - _, err := rt.connection.Exec(query, args...) + rows, err := rt.connection.Exec(query, args...) if err != nil { return -1, err } - return 1, nil + return rows.RowsAffected() } func (rd RefreshTokenRepository) processRows(rows *sql.Rows) []domain.RefreshTokenEntity { items := []domain.RefreshTokenEntity{} for rows.Next() { - var id int + var id int64 var username string var token string var expiresAt time.Time diff --git a/internal/services/refreshTokenService.go b/internal/services/refreshTokenService.go new file mode 100644 index 0000000..a23c06d --- /dev/null +++ b/internal/services/refreshTokenService.go @@ -0,0 +1,72 @@ +package services + +import ( + "database/sql" + "errors" + "time" + + "git.jamestombleson.com/jtom38/go-cook/internal/domain" + "git.jamestombleson.com/jtom38/go-cook/internal/repositories" + "github.com/google/uuid" +) + +type RefreshToken interface { + Create(username string, expiresAt time.Time) (string, error) + GetByName(name string) (domain.RefreshTokenEntity, error) + Delete(id int64) (int64, error) + IsRequestValid(username, refreshToken string, jwtExpiresAt time.Time) error +} + +type RefreshTokenService struct { + table repositories.RefreshTokenTable +} + +func NewRefreshTokenService(conn *sql.DB) RefreshTokenService { + return RefreshTokenService{ + table: repositories.NewRefreshTokenRepository(conn), + } +} + +func (rt RefreshTokenService) Create(username string, expiresAt time.Time) (string, error) { + token, err := uuid.NewV7() + if err != nil { + return "", err + } + + rows, err := rt.table.Create(username, token.String(), expiresAt) + if err != nil { + return "", err + } + + if rows != 1 { + return "", errors.New("expected one row but got none") + } + return token.String(), nil +} + +// Find the saved refresh token for a user and return it if it exists +func (rt RefreshTokenService) GetByName(name string) (domain.RefreshTokenEntity, error) { + return rt.table.GetByUsername(name) +} + +// This will request that a object is removed from the database +func (rt RefreshTokenService) Delete(id int64) (int64, error) { + return rt.table.DeleteById(id) +} + +func (rt RefreshTokenService) IsRequestValid(username, refreshToken string, jwtExpiresAt time.Time) error { + token, err := rt.GetByName(username) + if err != nil { + return err + } + + if (token.Token != refreshToken) { + return errors.New("the refresh token given does not match") + } + + if (token.ExpiresAt != jwtExpiresAt) { + return errors.New("the time when the jwt token expires does not match what was given") + } + + return nil +} \ No newline at end of file