Compare commits

..

No commits in common. "7d6e7d46cdc5fb10e5cbdc0feca2a176c7f3fbd8" and "029710ad31db0ef01f9b8d618c0879e3c7ecbcb8" have entirely different histories.

15 changed files with 104 additions and 157 deletions

View File

@ -1,7 +1,5 @@
# Brings the database up to the current migration
migrate-up: migrate-up:
GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./cmd/gocook.db goose -dir ./internal/migrations up GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./gocook.db goose -dir ./api/migrations up
# Rolls back one migration at a time
migrate-down: migrate-down:
GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./cmd/gocook.db goose -dir ./internal/migrations down GOOSE_DRIVER=sqlite3 GOOSE_DBSTRING=./gocook.db goose -dir ./api/migrations down

2
go.mod
View File

@ -7,7 +7,6 @@ require (
github.com/glebarez/go-sqlite v1.22.0 github.com/glebarez/go-sqlite v1.22.0
github.com/go-playground/validator v9.31.0+incompatible github.com/go-playground/validator v9.31.0+incompatible
github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
github.com/huandu/go-sqlbuilder v1.25.0 github.com/huandu/go-sqlbuilder v1.25.0
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/labstack/echo-jwt/v4 v4.2.0 github.com/labstack/echo-jwt/v4 v4.2.0
@ -23,6 +22,7 @@ require (
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/huandu/xstrings v1.3.2 // indirect github.com/huandu/xstrings v1.3.2 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect github.com/labstack/gommon v0.4.2 // indirect

View File

@ -3,7 +3,7 @@ package domain
import "time" import "time"
type UserEntity struct { type UserEntity struct {
Id int64 Id int
CreatedAt time.Time CreatedAt time.Time
LastUpdated time.Time LastUpdated time.Time
Name string Name string
@ -12,15 +12,16 @@ type UserEntity struct {
} }
type RefreshTokenEntity struct { type RefreshTokenEntity struct {
Id int64 Id int
Username string Username string
Token string Token string
ExpiresAt time.Time
CreatedAt time.Time CreatedAt time.Time
LastUpdated time.Time LastUpdated time.Time
} }
type RecipeEntity struct { type RecipeEntity struct {
Id int64 Id int32
CreatedAt time.Time CreatedAt time.Time
LastUpdated time.Time LastUpdated time.Time
Title string Title string

View File

@ -12,5 +12,5 @@ type UpdateScopesRequest struct {
type RefreshTokenRequest struct { type RefreshTokenRequest struct {
Username string `json:"username"` Username string `json:"username"`
RefreshToken string `json:"refreshToken"` RefreshToken string `json:"refreshToken"`
//ExpiresAt time.Time `json:"expiresAt"` ExpiresAt string `json:"expiresAt"`
} }

View File

@ -1,22 +1,10 @@
package domain package domain
type LoginResponse struct { type LoginResponse struct {
BaseResponse
Token string `json:"token"`
Type string `json:"type"`
RefreshToken string `json:"refreshToken"`
}
// This /
// /auth/refreshToken
type TokenRefreshResponse struct {
ErrorResponse
}
type BaseResponse struct {
Success bool `json:"success"` Success bool `json:"success"`
Message string `json:"message"` Token string `json:"token"`
Type string `json:"type"`
RefreshToken string `json:"refreshToken"`
} }
type ErrorResponse struct { type ErrorResponse struct {
@ -25,6 +13,7 @@ type ErrorResponse struct {
} }
type HelloWhoResponse struct { type HelloWhoResponse struct {
BaseResponse Success bool `json:"success"`
Error string `json:"error"` Error string `json:"error"`
Message string `json:"message"`
} }

View File

@ -3,7 +3,6 @@ package v1
import ( import (
"errors" "errors"
"net/http" "net/http"
"time"
"git.jamestombleson.com/jtom38/go-cook/internal/domain" "git.jamestombleson.com/jtom38/go-cook/internal/domain"
"git.jamestombleson.com/jtom38/go-cook/internal/repositories" "git.jamestombleson.com/jtom38/go-cook/internal/repositories"
@ -26,12 +25,13 @@ func (h *Handler) AuthRegister(c echo.Context) error {
password := c.FormValue("password") password := c.FormValue("password")
//username := c.QueryParam("username") //username := c.QueryParam("username")
exists, err := h.users.GetUser(username) exists, err := h.userRepo.GetByName(username)
if err != nil { if err != nil {
// if we have an err, validate that if its not user not found. // if we have an err, validate that if its not user not found.
// if the user is not found, we can use that name // if the user is not found, we can use that name
if err.Error() != repositories.ErrUserNotFound { if err.Error() != repositories.ErrUserNotFound {
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
Message: err.Error(), Message: err.Error(),
Success: true, Success: true,
}) })
@ -42,7 +42,7 @@ func (h *Handler) AuthRegister(c echo.Context) error {
} }
//password := c.QueryParam("password") //password := c.QueryParam("password")
err = h.users.CheckPasswordForRequirements(password) err = h.UserService.CheckPasswordForRequirements(password)
if err != nil { if err != nil {
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
Success: false, Success: false,
@ -50,7 +50,7 @@ func (h *Handler) AuthRegister(c echo.Context) error {
}) })
} }
_, err = h.users.Create(username, password, domain.ScopeRecipeRead) _, err = h.userRepo.Create(username, password, domain.ScopeRecipeRead)
if err != nil { if err != nil {
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{ return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
Success: false, Success: false,
@ -73,38 +73,27 @@ func (h *Handler) AuthLogin(c echo.Context) error {
} }
// check if the user exists // check if the user exists
err := h.users.DoesUserExist(username) err := h.UserService.DoesUserExist(username)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return h.InternalServerErrorResponse(c, err.Error())
} }
// make sure the hash matches // make sure the hash matches
err = h.users.DoesPasswordMatchHash(username, password) err = h.UserService.DoesPasswordMatchHash(username, password)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return h.InternalServerErrorResponse(c, err.Error())
} }
// TODO think about moving this down some? token, err := h.generateJwt(username, h.Config.ApiUri)
expiresAt := time.Now().Add(time.Hour * 48)
jwt, err := h.generateJwtWithExp(username, h.Config.ApiUri, expiresAt)
if err != nil {
return h.InternalServerErrorResponse(c, err.Error())
}
refresh, err := h.refreshTokens.Create(username)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return h.InternalServerErrorResponse(c, err.Error())
} }
return c.JSON(http.StatusOK, domain.LoginResponse{ return c.JSON(http.StatusOK, domain.LoginResponse{
BaseResponse: domain.BaseResponse{ Success: true,
Success: true, Token: token,
Message: "OK",
},
Token: jwt,
Type: "Bearer", Type: "Bearer",
RefreshToken: refresh, RefreshToken: "",
}) })
} }
@ -119,7 +108,7 @@ func (h *Handler) validateAdminToken(c echo.Context, password string) error {
return h.ReturnUnauthorizedResponse(c, ErrUserNotFound) return h.ReturnUnauthorizedResponse(c, ErrUserNotFound)
} }
token, err := h.generateJwt("admin", h.Config.ApiUri) token, err := h.generateAdminJwt("admin")
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return h.InternalServerErrorResponse(c, err.Error())
} }
@ -127,39 +116,14 @@ func (h *Handler) validateAdminToken(c echo.Context, password string) error {
return c.JSON(http.StatusOK, token) return c.JSON(http.StatusOK, token)
} }
// This will take collect some information about the requested refresh, validate and then return a new jwt token if approved. func (h *Handler) GenerateRefreshToken(c echo.Context) error {
func (h *Handler) RefreshJwtToken(c echo.Context) error {
// Check the context for the refresh token // Check the context for the refresh token
var request domain.RefreshTokenRequest var request domain.RefreshTokenRequest
err := (&echo.DefaultBinder{}).BindBody(c, &request) err := (&echo.DefaultBinder{}).BindBody(c, &request)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return err
} }
h.refreshTokenRepo.Create()
err = h.refreshTokens.IsRequestValid(request.Username, request.RefreshToken)
if err != nil {
return h.InternalServerErrorResponse(c, err.Error())
}
jwt, err := h.generateJwtWithExp(request.Username, h.Config.ApiUri, time.Now().Add(time.Hour * 48))
if err!= nil {
return h.InternalServerErrorResponse(c, err.Error())
}
newRefreshToken, err := h.refreshTokens.Create(request.Username)
if err != nil {
return h.InternalServerErrorResponse(c, err.Error())
}
return c.JSON(http.StatusOK, domain.LoginResponse{
BaseResponse: domain.BaseResponse{
Success: true,
Message: "OK",
},
Token: jwt,
Type: "Bearer",
RefreshToken: newRefreshToken,
})
} }
func (h *Handler) AddScopes(c echo.Context) error { func (h *Handler) AddScopes(c echo.Context) error {
@ -182,7 +146,7 @@ func (h *Handler) AddScopes(c echo.Context) error {
}) })
} }
err = h.users.AddScopes(request.Username, request.Scopes) err = h.UserService.AddScopes(request.Username, request.Scopes)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return h.InternalServerErrorResponse(c, err.Error())
} }
@ -212,7 +176,7 @@ func (h *Handler) RemoveScopes(c echo.Context) error {
}) })
} }
err = h.users.RemoveScopes(request.Username, request.Scopes) err = h.UserService.RemoveScopes(request.Username, request.Scopes)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return h.InternalServerErrorResponse(c, err.Error())
} }
@ -222,6 +186,10 @@ func (h *Handler) RemoveScopes(c echo.Context) error {
}) })
} }
func (h *Handler) RefreshJwtToken(c echo.Context) error {
return nil
}
func (h *Handler) getJwtToken(c echo.Context) (JwtToken, error) { func (h *Handler) getJwtToken(c echo.Context) (JwtToken, error) {
// Make sure that the request came with a jwtToken // Make sure that the request came with a jwtToken
token, ok := c.Get("user").(*jwt.Token) token, ok := c.Get("user").(*jwt.Token)

View File

@ -11,20 +11,16 @@ import (
func (h *Handler) DemoHello(c echo.Context) error { func (h *Handler) DemoHello(c echo.Context) error {
return c.JSON(http.StatusOK, domain.HelloWhoResponse{ return c.JSON(http.StatusOK, domain.HelloWhoResponse{
BaseResponse: domain.BaseResponse{ Success: true,
Success: true, Message: "Hello world!",
Message: "Hello world!",
},
}) })
} }
func (h *Handler) HelloWho(c echo.Context) error { func (h *Handler) HelloWho(c echo.Context) error {
name := c.Param("who") name := c.Param("who")
return c.JSON(http.StatusOK, domain.HelloWhoResponse{ return c.JSON(http.StatusOK, domain.HelloWhoResponse{
BaseResponse: domain.BaseResponse{ Success: true,
Success: true, Message: fmt.Sprintf("Hello, %s", name),
Message: fmt.Sprintf("Hello, %s", name),
},
}) })
} }
@ -32,14 +28,15 @@ func (h *Handler) HelloBody(c echo.Context) error {
request := domain.HelloBodyRequest{} request := domain.HelloBodyRequest{}
err := (&echo.DefaultBinder{}).BindBody(c, &request) err := (&echo.DefaultBinder{}).BindBody(c, &request)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return c.JSON(http.StatusBadRequest, domain.HelloWhoResponse{
Success: false,
Error: err.Error(),
})
} }
return c.JSON(http.StatusOK, domain.HelloWhoResponse{ return c.JSON(http.StatusOK, domain.HelloWhoResponse{
BaseResponse: domain.BaseResponse{ Success: true,
Success: true, Message: fmt.Sprintf("Hello, %s", request.Name),
Message: fmt.Sprintf("Hello, %s", request.Name),
},
}) })
} }

View File

@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"git.jamestombleson.com/jtom38/go-cook/internal/domain" "git.jamestombleson.com/jtom38/go-cook/internal/domain"
"git.jamestombleson.com/jtom38/go-cook/internal/repositories"
"git.jamestombleson.com/jtom38/go-cook/internal/services" "git.jamestombleson.com/jtom38/go-cook/internal/services"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
@ -15,18 +16,19 @@ import (
type Handler struct { type Handler struct {
Config domain.EnvConfig Config domain.EnvConfig
users services.UserService UserService services.UserService
recipes services.Recipes userRepo repositories.IUserTable
refreshTokens services.RefreshToken recipeRepo repositories.IRecipeTable
refreshTokenRepo repositories.RefreshTokenRepository
} }
func NewHandler(conn *sql.DB, cfg domain.EnvConfig) *Handler { func NewHandler(conn *sql.DB, cfg domain.EnvConfig) *Handler {
return &Handler{ return &Handler{
Config: cfg, Config: cfg,
UserService: services.NewUserService(conn),
users: services.NewUserService(conn), userRepo: repositories.NewUserRepository(conn),
recipes: services.NewRecipesService(conn), recipeRepo: repositories.NewRecipeRepository(conn),
refreshTokens: services.NewRefreshTokenService(conn), refreshTokenRepo: repositories.NewRefreshTokenRepository(conn),
} }
} }
@ -44,7 +46,6 @@ func (h *Handler) Register(v1 *echo.Group) {
auth.Use(echojwt.WithConfig(jwtConfig)) auth.Use(echojwt.WithConfig(jwtConfig))
auth.POST("/scopes/add", h.AddScopes) auth.POST("/scopes/add", h.AddScopes)
auth.POST("/scopes/remove", h.RemoveScopes) auth.POST("/scopes/remove", h.RemoveScopes)
auth.POST("/refreshToken", h.RefreshJwtToken)
demo := v1.Group("/demo") demo := v1.Group("/demo")
demo.GET("/hello", h.DemoHello) demo.GET("/hello", h.DemoHello)

View File

@ -57,28 +57,40 @@ func (j JwtToken) hasScope(scope string) error {
} }
func (h *Handler) generateJwt(username, issuer string) (string, error) { func (h *Handler) generateJwt(username, issuer string) (string, error) {
return h.generateJwtWithExp(username, issuer, time.Now().Add(10 * time.Minute))
}
func (h *Handler) generateJwtWithExp(username, issuer string, expiresAt time.Time) (string, error) {
secret := []byte(h.Config.JwtSecret) secret := []byte(h.Config.JwtSecret)
// Anyone who wants to decrypt the key needs to use the same method // Anyone who wants to decrypt the key needs to use the same method
token := jwt.New(jwt.SigningMethodHS256) token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims) claims := token.Claims.(jwt.MapClaims)
claims["exp"] = expiresAt claims["exp"] = time.Now().Add(10 * time.Minute)
claims["authorized"] = true claims["authorized"] = true
claims["username"] = username claims["username"] = username
claims["iss"] = issuer claims["iss"] = issuer
var scopes []string var scopes []string
if (username == "admin") { scopes = append(scopes, domain.ScopeRecipeRead)
scopes = append(scopes, domain.ScopeAll) claims["scopes"] = scopes
claims["scopes"] = scopes
} else { tokenString, err := token.SignedString(secret)
scopes = append(scopes, domain.ScopeRecipeRead) if err != nil {
claims["scopes"] = scopes return "", err
} }
return tokenString, nil
}
func (h *Handler) generateAdminJwt(username string) (string, error) {
secret := []byte(h.Config.JwtSecret)
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["exp"] = time.Now().Add(10 * time.Minute)
claims["authorized"] = true
claims["username"] = username
var scopes []string
scopes = append(scopes, domain.ScopeAll)
claims["scopes"] = scopes
tokenString, err := token.SignedString(secret) tokenString, err := token.SignedString(secret)
if err != nil { if err != nil {

View File

@ -5,6 +5,7 @@ CREATE TABLE RefreshTokens (
ID INTEGER PRIMARY KEY AUTOINCREMENT, ID INTEGER PRIMARY KEY AUTOINCREMENT,
Username TEXT NOT NULL, Username TEXT NOT NULL,
Token TEXT NOT NULL, Token TEXT NOT NULL,
ExpiresAt DATETIME NOT NULL,
CreatedAt DATETIME NOT NULL, CreatedAt DATETIME NOT NULL,
LastUpdated DATETIME NOT NULL LastUpdated DATETIME NOT NULL
) )

View File

@ -15,7 +15,7 @@ const (
) )
type RefreshTokenTable interface { type RefreshTokenTable interface {
Create(username string, token string) (int64, error) Create(username string, token string, expiresAt time.Time) (int64, error)
GetByUsername(name string) (domain.RefreshTokenEntity, error) GetByUsername(name string) (domain.RefreshTokenEntity, error)
DeleteById(id int64) (int64, error) DeleteById(id int64) (int64, error)
} }
@ -30,12 +30,12 @@ func NewRefreshTokenRepository(conn *sql.DB) RefreshTokenRepository {
} }
} }
func (rt RefreshTokenRepository) Create(username string, token string) (int64, error) { func (rt RefreshTokenRepository) Create(username string, token string, expiresAt time.Time) (int64, error) {
dt := time.Now() dt := time.Now()
builder := sqlbuilder.NewInsertBuilder() builder := sqlbuilder.NewInsertBuilder()
builder.InsertInto(refreshTokenTableName) builder.InsertInto(refreshTokenTableName)
builder.Cols("Username", "Token", "CreatedAt", "LastUpdated") builder.Cols("Username", "Token", "ExpiresAt", "CreatedAt", "LastUpdated")
builder.Values(username, token, dt, dt) builder.Values(username, token, expiresAt, dt, dt)
query, args := builder.Build() query, args := builder.Build()
_, err := rt.connection.Exec(query, args...) _, err := rt.connection.Exec(query, args...)
@ -89,10 +89,11 @@ func (rd RefreshTokenRepository) processRows(rows *sql.Rows) []domain.RefreshTok
var id int64 var id int64
var username string var username string
var token string var token string
var expiresAt time.Time
var createdAt time.Time var createdAt time.Time
var lastUpdated time.Time var lastUpdated time.Time
err := rows.Scan(&id, &username, &token, &createdAt, &lastUpdated) err := rows.Scan(&id, &username, &token, &expiresAt, &createdAt, &lastUpdated)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
@ -101,6 +102,7 @@ func (rd RefreshTokenRepository) processRows(rows *sql.Rows) []domain.RefreshTok
Id: id, Id: id,
Username: username, Username: username,
Token: token, Token: token,
ExpiresAt: expiresAt,
CreatedAt: createdAt, CreatedAt: createdAt,
LastUpdated: lastUpdated, LastUpdated: lastUpdated,
}) })

View File

@ -3,6 +3,7 @@ package repositories_test
import ( import (
"database/sql" "database/sql"
"testing" "testing"
"time"
"git.jamestombleson.com/jtom38/go-cook/internal/repositories" "git.jamestombleson.com/jtom38/go-cook/internal/repositories"
_ "github.com/glebarez/go-sqlite" _ "github.com/glebarez/go-sqlite"
@ -17,7 +18,7 @@ func TestRefreshTokenCreate(t *testing.T) {
} }
client := repositories.NewRefreshTokenRepository(conn) client := repositories.NewRefreshTokenRepository(conn)
rows, err := client.Create("tester", "BadTokenDontUse") rows, err := client.Create("tester", "BadTokenDontUse", time.Now().Add(time.Hour+1))
if err != nil { if err != nil {
t.Log(err) t.Log(err)
t.FailNow() t.FailNow()
@ -36,7 +37,7 @@ func TestRefreshTokenGetByUsername(t *testing.T) {
} }
client := repositories.NewRefreshTokenRepository(conn) client := repositories.NewRefreshTokenRepository(conn)
rows, err := client.Create("tester", "BadTokenDoNotUse") rows, err := client.Create("tester", "BadTokenDoNotUse", time.Now().Add(time.Hour+1))
if err != nil { if err != nil {
t.Log(err) t.Log(err)
t.FailNow() t.FailNow()
@ -67,7 +68,7 @@ func TestRefreshTokenDeleteById(t *testing.T) {
} }
client := repositories.NewRefreshTokenRepository(conn) client := repositories.NewRefreshTokenRepository(conn)
_, err = client.Create("tester", "BadTokenDoNotUse") _, err = client.Create("tester", "BadTokenDoNotUse", time.Now().Add(time.Hour+1))
if err != nil { if err != nil {
t.Log(err) t.Log(err)
t.FailNow() t.FailNow()

View File

@ -133,7 +133,7 @@ func (ur UserRepository) processRows(rows *sql.Rows) []domain.UserEntity {
items := []domain.UserEntity{} items := []domain.UserEntity{}
for rows.Next() { for rows.Next() {
var id int64 var id int
var name string var name string
var hash string var hash string
var createdAt time.Time var createdAt time.Time

View File

@ -3,25 +3,20 @@ package services
import ( import (
"database/sql" "database/sql"
"errors" "errors"
"time"
"git.jamestombleson.com/jtom38/go-cook/internal/domain" "git.jamestombleson.com/jtom38/go-cook/internal/domain"
"git.jamestombleson.com/jtom38/go-cook/internal/repositories" "git.jamestombleson.com/jtom38/go-cook/internal/repositories"
"github.com/google/uuid" "github.com/google/uuid"
) )
const (
ErrUnexpectedAmountOfRowsUpdated = "got a unexpected of rows updated"
)
type RefreshToken interface { type RefreshToken interface {
Create(username string) (string, error) Create(username string, expiresAt time.Time) (string, error)
GetByName(name string) (domain.RefreshTokenEntity, error) GetByName(name string) (domain.RefreshTokenEntity, error)
Delete(id int64) (int64, error) Delete(id int64) (int64, error)
IsRequestValid(username, refreshToken string) error IsRequestValid(username, refreshToken string, jwtExpiresAt time.Time) error
} }
// A new jwt token can be made if the user has the correct refresh token for the user.
// It will also require the old JWT token so the expire time is pulled and part of the validation
type RefreshTokenService struct { type RefreshTokenService struct {
table repositories.RefreshTokenTable table repositories.RefreshTokenTable
} }
@ -32,26 +27,13 @@ func NewRefreshTokenService(conn *sql.DB) RefreshTokenService {
} }
} }
func (rt RefreshTokenService) Create(username string) (string, error) { func (rt RefreshTokenService) Create(username string, expiresAt time.Time) (string, error) {
//if a refresh token already exists for a user, reuse
existingToken, err := rt.GetByName(username)
if err == nil {
rowsRemoved, err := rt.Delete(existingToken.Id)
if err != nil {
return "", err
}
if rowsRemoved != 1 {
return "", errors.New(ErrUnexpectedAmountOfRowsUpdated)
}
}
token, err := uuid.NewV7() token, err := uuid.NewV7()
if err != nil { if err != nil {
return "", err return "", err
} }
rows, err := rt.table.Create(username, token.String()) rows, err := rt.table.Create(username, token.String(), expiresAt)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -72,15 +54,19 @@ func (rt RefreshTokenService) Delete(id int64) (int64, error) {
return rt.table.DeleteById(id) return rt.table.DeleteById(id)
} }
func (rt RefreshTokenService) IsRequestValid(username, refreshToken string) error { func (rt RefreshTokenService) IsRequestValid(username, refreshToken string, jwtExpiresAt time.Time) error {
token, err := rt.GetByName(username) token, err := rt.GetByName(username)
if err != nil { if err != nil {
return err return err
} }
if token.Token != refreshToken { if (token.Token != refreshToken) {
return errors.New("the refresh token given does not match") 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 return nil
} }

View File

@ -16,15 +16,6 @@ Content-Type: application/x-www-form-urlencoded
password=lol password=lol
### Try to refresh the token
POST http://localhost:1323/api/v1/auth/refreshToken
Content-Type: application/json
Authorization: Bearer
{
"username": "test",
"refreshToken": ""
}
### Add Scope to test user ### Add Scope to test user
POST http://localhost:1323/api/v1/auth/scopes/add POST http://localhost:1323/api/v1/auth/scopes/add