Compare commits

...

10 Commits

15 changed files with 157 additions and 104 deletions

View File

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

2
go.mod
View File

@ -7,6 +7,7 @@ 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
@ -22,7 +23,6 @@ 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 int Id int64
CreatedAt time.Time CreatedAt time.Time
LastUpdated time.Time LastUpdated time.Time
Name string Name string
@ -12,16 +12,15 @@ type UserEntity struct {
} }
type RefreshTokenEntity struct { type RefreshTokenEntity struct {
Id int Id int64
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 int32 Id int64
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 string `json:"expiresAt"` //ExpiresAt time.Time `json:"expiresAt"`
} }

View File

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

View File

@ -3,6 +3,7 @@ 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"
@ -25,13 +26,12 @@ 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.userRepo.GetByName(username) exists, err := h.users.GetUser(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.UserService.CheckPasswordForRequirements(password) err = h.users.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.userRepo.Create(username, password, domain.ScopeRecipeRead) _, err = h.users.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,27 +73,38 @@ func (h *Handler) AuthLogin(c echo.Context) error {
} }
// check if the user exists // check if the user exists
err := h.UserService.DoesUserExist(username) err := h.users.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.UserService.DoesPasswordMatchHash(username, password) err = h.users.DoesPasswordMatchHash(username, password)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return h.InternalServerErrorResponse(c, err.Error())
} }
token, err := h.generateJwt(username, h.Config.ApiUri) // TODO think about moving this down some?
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{
Success: true, BaseResponse: domain.BaseResponse{
Token: token, Success: true,
Message: "OK",
},
Token: jwt,
Type: "Bearer", Type: "Bearer",
RefreshToken: "", RefreshToken: refresh,
}) })
} }
@ -108,7 +119,7 @@ func (h *Handler) validateAdminToken(c echo.Context, password string) error {
return h.ReturnUnauthorizedResponse(c, ErrUserNotFound) return h.ReturnUnauthorizedResponse(c, ErrUserNotFound)
} }
token, err := h.generateAdminJwt("admin") token, err := h.generateJwt("admin", h.Config.ApiUri)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return h.InternalServerErrorResponse(c, err.Error())
} }
@ -116,14 +127,39 @@ func (h *Handler) validateAdminToken(c echo.Context, password string) error {
return c.JSON(http.StatusOK, token) return c.JSON(http.StatusOK, token)
} }
func (h *Handler) GenerateRefreshToken(c echo.Context) error { // This will take collect some information about the requested refresh, validate and then return a new jwt token if approved.
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 err return h.InternalServerErrorResponse(c, err.Error())
} }
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 {
@ -146,7 +182,7 @@ func (h *Handler) AddScopes(c echo.Context) error {
}) })
} }
err = h.UserService.AddScopes(request.Username, request.Scopes) err = h.users.AddScopes(request.Username, request.Scopes)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return h.InternalServerErrorResponse(c, err.Error())
} }
@ -176,7 +212,7 @@ func (h *Handler) RemoveScopes(c echo.Context) error {
}) })
} }
err = h.UserService.RemoveScopes(request.Username, request.Scopes) err = h.users.RemoveScopes(request.Username, request.Scopes)
if err != nil { if err != nil {
return h.InternalServerErrorResponse(c, err.Error()) return h.InternalServerErrorResponse(c, err.Error())
} }
@ -186,10 +222,6 @@ 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,16 +11,20 @@ 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{
Success: true, BaseResponse: domain.BaseResponse{
Message: "Hello world!", Success: true,
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{
Success: true, BaseResponse: domain.BaseResponse{
Message: fmt.Sprintf("Hello, %s", name), Success: true,
Message: fmt.Sprintf("Hello, %s", name),
},
}) })
} }
@ -28,15 +32,14 @@ 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 c.JSON(http.StatusBadRequest, domain.HelloWhoResponse{ return h.InternalServerErrorResponse(c, err.Error())
Success: false,
Error: err.Error(),
})
} }
return c.JSON(http.StatusOK, domain.HelloWhoResponse{ return c.JSON(http.StatusOK, domain.HelloWhoResponse{
Success: true, BaseResponse: domain.BaseResponse{
Message: fmt.Sprintf("Hello, %s", request.Name), Success: true,
Message: fmt.Sprintf("Hello, %s", request.Name),
},
}) })
} }

View File

@ -5,7 +5,6 @@ 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"
@ -16,19 +15,18 @@ import (
type Handler struct { type Handler struct {
Config domain.EnvConfig Config domain.EnvConfig
UserService services.UserService users services.UserService
userRepo repositories.IUserTable recipes services.Recipes
recipeRepo repositories.IRecipeTable refreshTokens services.RefreshToken
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),
userRepo: repositories.NewUserRepository(conn), users: services.NewUserService(conn),
recipeRepo: repositories.NewRecipeRepository(conn), recipes: services.NewRecipesService(conn),
refreshTokenRepo: repositories.NewRefreshTokenRepository(conn), refreshTokens: services.NewRefreshTokenService(conn),
} }
} }
@ -46,6 +44,7 @@ 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,40 +57,28 @@ 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"] = time.Now().Add(10 * time.Minute) claims["exp"] = expiresAt
claims["authorized"] = true claims["authorized"] = true
claims["username"] = username claims["username"] = username
claims["iss"] = issuer claims["iss"] = issuer
var scopes []string var scopes []string
scopes = append(scopes, domain.ScopeRecipeRead) if (username == "admin") {
claims["scopes"] = scopes scopes = append(scopes, domain.ScopeAll)
claims["scopes"] = scopes
tokenString, err := token.SignedString(secret) } else {
if err != nil { scopes = append(scopes, domain.ScopeRecipeRead)
return "", err claims["scopes"] = scopes
} }
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,7 +5,6 @@ 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, expiresAt time.Time) (int64, error) Create(username string, token string) (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, expiresAt time.Time) (int64, error) { func (rt RefreshTokenRepository) Create(username string, token string) (int64, error) {
dt := time.Now() dt := time.Now()
builder := sqlbuilder.NewInsertBuilder() builder := sqlbuilder.NewInsertBuilder()
builder.InsertInto(refreshTokenTableName) builder.InsertInto(refreshTokenTableName)
builder.Cols("Username", "Token", "ExpiresAt", "CreatedAt", "LastUpdated") builder.Cols("Username", "Token", "CreatedAt", "LastUpdated")
builder.Values(username, token, expiresAt, dt, dt) builder.Values(username, token, dt, dt)
query, args := builder.Build() query, args := builder.Build()
_, err := rt.connection.Exec(query, args...) _, err := rt.connection.Exec(query, args...)
@ -89,11 +89,10 @@ 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, &expiresAt, &createdAt, &lastUpdated) err := rows.Scan(&id, &username, &token, &createdAt, &lastUpdated)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
@ -102,7 +101,6 @@ 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,7 +3,6 @@ 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"
@ -18,7 +17,7 @@ func TestRefreshTokenCreate(t *testing.T) {
} }
client := repositories.NewRefreshTokenRepository(conn) client := repositories.NewRefreshTokenRepository(conn)
rows, err := client.Create("tester", "BadTokenDontUse", time.Now().Add(time.Hour+1)) rows, err := client.Create("tester", "BadTokenDontUse")
if err != nil { if err != nil {
t.Log(err) t.Log(err)
t.FailNow() t.FailNow()
@ -37,7 +36,7 @@ func TestRefreshTokenGetByUsername(t *testing.T) {
} }
client := repositories.NewRefreshTokenRepository(conn) client := repositories.NewRefreshTokenRepository(conn)
rows, err := client.Create("tester", "BadTokenDoNotUse", time.Now().Add(time.Hour+1)) rows, err := client.Create("tester", "BadTokenDoNotUse")
if err != nil { if err != nil {
t.Log(err) t.Log(err)
t.FailNow() t.FailNow()
@ -68,7 +67,7 @@ func TestRefreshTokenDeleteById(t *testing.T) {
} }
client := repositories.NewRefreshTokenRepository(conn) client := repositories.NewRefreshTokenRepository(conn)
_, err = client.Create("tester", "BadTokenDoNotUse", time.Now().Add(time.Hour+1)) _, err = client.Create("tester", "BadTokenDoNotUse")
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 int var id int64
var name string var name string
var hash string var hash string
var createdAt time.Time var createdAt time.Time

View File

@ -3,20 +3,25 @@ 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, expiresAt time.Time) (string, error) Create(username string) (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, jwtExpiresAt time.Time) error IsRequestValid(username, refreshToken string) 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
} }
@ -27,13 +32,26 @@ func NewRefreshTokenService(conn *sql.DB) RefreshTokenService {
} }
} }
func (rt RefreshTokenService) Create(username string, expiresAt time.Time) (string, error) { func (rt RefreshTokenService) Create(username string) (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(), expiresAt) rows, err := rt.table.Create(username, token.String())
if err != nil { if err != nil {
return "", err return "", err
} }
@ -54,19 +72,15 @@ 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, jwtExpiresAt time.Time) error { func (rt RefreshTokenService) IsRequestValid(username, refreshToken string) 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,6 +16,15 @@ 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