initial commit
This commit is contained in:
commit
e8da0f7874
15 files changed
+722
No files matched your search
@@ -0,0 +1,2 @@
|
||||
go.sum
|
||||
_env
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
FROM golang:alpine
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN go build -o testjwtauth .
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/testjwtauth"]
|
||||
@@ -0,0 +1,24 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
DSN = getEnv("DSN")
|
||||
SMTPHost = getEnv("SMTP_HOST")
|
||||
SMTPEmail = getEnv("SMTP_EMAIL")
|
||||
SMTPPassword = getEnv("SMTP_PASSWORD")
|
||||
MockSMTPRecipient = getEnv("MOCK_SMTP_RECIPIENT")
|
||||
JWTSecret = getEnv("JWT_SECRET")
|
||||
)
|
||||
|
||||
func getEnv(k string) string {
|
||||
v := os.Getenv(k)
|
||||
if v == "" {
|
||||
log.Fatalf("enviroment variable %s is required", k)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
c "desq.com.ru/testjwtauth/config"
|
||||
"desq.com.ru/testjwtauth/models"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func initDB() (*gorm.DB, error) {
|
||||
db, err := gorm.Open(postgres.Open(c.DSN), &gorm.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db.AutoMigrate(&models.RefreshToken{})
|
||||
|
||||
return db, nil
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
module desq.com.ru/testjwtauth
|
||||
|
||||
go 1.23.4
|
||||
|
||||
require (
|
||||
github.com/gofiber/contrib/jwt v1.0.10
|
||||
github.com/gofiber/fiber/v2 v2.52.5
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/google/uuid v1.6.0
|
||||
golang.org/x/crypto v0.30.0
|
||||
gorm.io/driver/postgres v1.5.11
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.7.1 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.57.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
@@ -0,0 +1,86 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"desq.com.ru/testjwtauth/config"
|
||||
"desq.com.ru/testjwtauth/models"
|
||||
"desq.com.ru/testjwtauth/utils"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func CreateToken(DB *gorm.DB, userID uuid.UUID, IP string, expires int64) (string, string, error) {
|
||||
RT, err := utils.GenerateToken(32)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to generate refresh token")
|
||||
}
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"userId": userID,
|
||||
"ip": IP,
|
||||
"sub": RT,
|
||||
"exp": expires,
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)
|
||||
|
||||
t, err := token.SignedString([]byte(config.JWTSecret))
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to sign token")
|
||||
}
|
||||
|
||||
RTEncoded, err := utils.EncodeToken(RT)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to encode refresh token")
|
||||
}
|
||||
|
||||
if err := DB.Create(&models.RefreshToken{
|
||||
UserID: userID,
|
||||
Token: RTEncoded,
|
||||
Valid: true,
|
||||
}).Error; err != nil {
|
||||
return "", "", fmt.Errorf("failed to create refresh token")
|
||||
}
|
||||
|
||||
return t, RT, nil
|
||||
}
|
||||
|
||||
func RefreshToken(DB *gorm.DB, userID uuid.UUID, claims jwt.MapClaims, RT string, IP string) error {
|
||||
if claims["sub"].(string) != RT {
|
||||
return fmt.Errorf("refresh token does not belong to this token")
|
||||
}
|
||||
|
||||
validRTs := []models.RefreshToken{}
|
||||
if err := DB.Where("user_id = ? AND valid = ?", userID, true).Find(&validRTs).Error; err != nil {
|
||||
return fmt.Errorf("failed to retrieve valid tokens")
|
||||
}
|
||||
|
||||
var matchedRT *models.RefreshToken
|
||||
for i := range validRTs {
|
||||
if utils.CompareToken(validRTs[i].Token, RT) {
|
||||
matchedRT = &validRTs[i]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if matchedRT == nil || !matchedRT.Valid {
|
||||
return fmt.Errorf("token is invalid")
|
||||
}
|
||||
|
||||
matchedRT.Valid = false
|
||||
if err := DB.Save(&matchedRT).Error; err != nil {
|
||||
return fmt.Errorf("failed to invalidate refresh token")
|
||||
}
|
||||
|
||||
if claims["ip"].(string) != IP {
|
||||
if err := utils.NotifyEmail(IP, config.MockSMTPRecipient); err != nil {
|
||||
fmt.Printf("Failed to send email: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
c "desq.com.ru/testjwtauth/config"
|
||||
"desq.com.ru/testjwtauth/models"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestCreateToken(t *testing.T) {
|
||||
DB, err := gorm.Open(postgres.Open(c.DSN), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
DB.AutoMigrate(&models.RefreshToken{})
|
||||
userID := uuid.New()
|
||||
IP := "127.0.0.1"
|
||||
expires := time.Now().Add(time.Hour * 24).Unix()
|
||||
type args struct {
|
||||
DB *gorm.DB
|
||||
userID uuid.UUID
|
||||
IP string
|
||||
expires int64
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "CreateToken",
|
||||
args: args{
|
||||
DB: DB,
|
||||
userID: userID,
|
||||
IP: IP,
|
||||
expires: expires,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, _, err := CreateToken(tt.args.DB, tt.args.userID, tt.args.IP, tt.args.expires)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("CreateToken() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshToken(t *testing.T) {
|
||||
DB, err := gorm.Open(postgres.Open(c.DSN), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
DB.AutoMigrate(&models.RefreshToken{})
|
||||
userID := uuid.New()
|
||||
IP := "127.0.0.1"
|
||||
expires := time.Now().Add(time.Hour * 24)
|
||||
_, RT, err := CreateToken(DB, userID, IP, expires.Unix())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate token pair: %v", err)
|
||||
}
|
||||
claims := jwt.MapClaims{
|
||||
"userId": userID,
|
||||
"ip": IP,
|
||||
"sub": RT,
|
||||
"exp": expires,
|
||||
}
|
||||
type args struct {
|
||||
DB *gorm.DB
|
||||
userID uuid.UUID
|
||||
claims jwt.MapClaims
|
||||
RT string
|
||||
IP string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "RefreshToken",
|
||||
args: args{
|
||||
DB: DB,
|
||||
userID: userID,
|
||||
claims: claims,
|
||||
RT: RT,
|
||||
IP: IP,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := RefreshToken(tt.args.DB, tt.args.userID, tt.args.claims, tt.args.RT, tt.args.IP); (err != nil) != tt.wantErr {
|
||||
t.Errorf("RefreshToken() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"desq.com.ru/testjwtauth/config"
|
||||
"desq.com.ru/testjwtauth/routes"
|
||||
|
||||
jwtware "github.com/gofiber/contrib/jwt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
DB, err := initDB()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to estabilish database connection: %v", err)
|
||||
}
|
||||
|
||||
app := fiber.New()
|
||||
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
c.Locals("db", DB)
|
||||
|
||||
return c.Next()
|
||||
})
|
||||
|
||||
app.Post("/me/:id/token", routes.CreateToken)
|
||||
app.Use(jwtware.New(jwtware.Config{
|
||||
SigningKey: jwtware.SigningKey{Key: []byte(config.JWTSecret)},
|
||||
}))
|
||||
app.Post("/me/token/refresh", routes.RefreshToken)
|
||||
|
||||
app.Listen(":8080")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type RefreshToken struct {
|
||||
ID int
|
||||
UserID uuid.UUID
|
||||
Token string
|
||||
Valid bool
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"desq.com.ru/testjwtauth/handlers"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func setRTCookie(c *fiber.Ctx, RT string, expires time.Time) {
|
||||
c.Cookie(&fiber.Cookie{
|
||||
Name: "refreshToken",
|
||||
Value: RT,
|
||||
HTTPOnly: true,
|
||||
Expires: expires,
|
||||
})
|
||||
}
|
||||
|
||||
func CreateToken(c *fiber.Ctx) error {
|
||||
DB := c.Locals("db").(*gorm.DB)
|
||||
|
||||
uID := c.Params("id")
|
||||
userID, err := uuid.Parse(uID)
|
||||
if err != nil {
|
||||
return c.Status(500).SendString(err.Error())
|
||||
}
|
||||
|
||||
expires := time.Now().Add(time.Hour * 24)
|
||||
|
||||
T, RT, err := handlers.CreateToken(DB, userID, c.IP(), expires.Unix())
|
||||
if err != nil {
|
||||
return c.Status(500).SendString(err.Error())
|
||||
}
|
||||
|
||||
setRTCookie(c, RT, expires)
|
||||
|
||||
return c.Status(201).JSON(fiber.Map{
|
||||
"token": T,
|
||||
})
|
||||
}
|
||||
|
||||
func RefreshToken(c *fiber.Ctx) error {
|
||||
DB := c.Locals("db").(*gorm.DB)
|
||||
claims := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)
|
||||
|
||||
RT := c.Cookies("refreshToken")
|
||||
if RT == "" {
|
||||
return c.Status(400).SendString("Refresh cookie must be set")
|
||||
}
|
||||
|
||||
uID := claims["userId"].(string)
|
||||
|
||||
userID, err := uuid.Parse(uID)
|
||||
if err != nil {
|
||||
return c.Status(500).SendString(err.Error())
|
||||
}
|
||||
|
||||
err = handlers.RefreshToken(DB, userID, claims, RT, c.IP())
|
||||
if err != nil {
|
||||
return c.Status(401).SendString(err.Error())
|
||||
}
|
||||
|
||||
expires := time.Now().Add(time.Hour * 24)
|
||||
|
||||
t, RT, err := handlers.CreateToken(DB, userID, c.IP(), expires.Unix())
|
||||
if err != nil {
|
||||
return c.Status(500).SendString(err.Error())
|
||||
}
|
||||
|
||||
setRTCookie(c, RT, expires)
|
||||
|
||||
return c.Status(200).JSON(fiber.Map{
|
||||
"token": t,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"desq.com.ru/testjwtauth/config"
|
||||
"desq.com.ru/testjwtauth/handlers"
|
||||
"desq.com.ru/testjwtauth/models"
|
||||
jwtware "github.com/gofiber/contrib/jwt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func setupFiber(t *testing.T) *fiber.App {
|
||||
DB, err := gorm.Open(postgres.Open(config.DSN), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
DB.AutoMigrate(&models.RefreshToken{})
|
||||
app := fiber.New()
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
c.Locals("db", DB)
|
||||
|
||||
return c.Next()
|
||||
})
|
||||
app.Post("/me/:id/token", CreateToken)
|
||||
app.Use(jwtware.New(jwtware.Config{
|
||||
SigningKey: jwtware.SigningKey{Key: []byte(config.JWTSecret)},
|
||||
}))
|
||||
app.Post("/me/token/refresh", RefreshToken)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func TestCreateToken(t *testing.T) {
|
||||
userID := uuid.New().String()
|
||||
tests := []struct {
|
||||
description string
|
||||
route string
|
||||
expectedError bool
|
||||
expectedCode int
|
||||
expectedBody string
|
||||
}{
|
||||
{
|
||||
description: "CreateTokenRoute",
|
||||
route: "/me/" + userID + "/token",
|
||||
expectedError: false,
|
||||
expectedCode: 201,
|
||||
},
|
||||
}
|
||||
app := setupFiber(t)
|
||||
for _, test := range tests {
|
||||
req, _ := http.NewRequest("POST", test.route, nil)
|
||||
res, err := app.Test(req, -1)
|
||||
assert.Equalf(t, test.expectedError, err != nil, test.description)
|
||||
if test.expectedError {
|
||||
continue
|
||||
}
|
||||
assert.Equalf(t, test.expectedCode, res.StatusCode, test.description)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshToken(t *testing.T) {
|
||||
DB, err := gorm.Open(postgres.Open(config.DSN), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to connect to database: %v", err)
|
||||
}
|
||||
userID := uuid.New()
|
||||
IP := "127.0.0.1"
|
||||
expires := time.Now().Add(time.Hour * 24)
|
||||
token, RT, err := handlers.CreateToken(DB, userID, IP, expires.Unix())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate token pair: %v", err)
|
||||
}
|
||||
RTCookie := &http.Cookie{
|
||||
Name: "refreshToken",
|
||||
Value: RT,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
Expires: expires,
|
||||
}
|
||||
tests := []struct {
|
||||
description string
|
||||
route string
|
||||
RTCookie *http.Cookie
|
||||
token string
|
||||
expectedError bool
|
||||
expectedCode int
|
||||
}{
|
||||
{
|
||||
description: "WithNoRefreshToken",
|
||||
route: "/me/token/refresh",
|
||||
RTCookie: &http.Cookie{},
|
||||
token: token,
|
||||
expectedError: false,
|
||||
expectedCode: 400,
|
||||
},
|
||||
{
|
||||
description: "WithNoToken",
|
||||
route: "/me/token/refresh",
|
||||
RTCookie: RTCookie,
|
||||
token: "",
|
||||
expectedError: false,
|
||||
expectedCode: 400,
|
||||
},
|
||||
{
|
||||
description: "WithInvalidToken",
|
||||
route: "/me/token/refresh",
|
||||
RTCookie: RTCookie,
|
||||
token: "InvalidToken",
|
||||
expectedError: false,
|
||||
expectedCode: 401,
|
||||
},
|
||||
{
|
||||
description: "WithValidTokenPair",
|
||||
route: "/me/token/refresh",
|
||||
RTCookie: RTCookie,
|
||||
token: token,
|
||||
expectedError: false,
|
||||
expectedCode: 200,
|
||||
},
|
||||
}
|
||||
app := setupFiber(t)
|
||||
for _, test := range tests {
|
||||
req, _ := http.NewRequest("POST", test.route, nil)
|
||||
req.Header.Add("Authorization", "Bearer "+test.token)
|
||||
req.AddCookie(test.RTCookie)
|
||||
res, err := app.Test(req, -1)
|
||||
assert.Equalf(t, test.expectedError, err != nil, test.description)
|
||||
if test.expectedError {
|
||||
continue
|
||||
}
|
||||
assert.Equalf(t, test.expectedCode, res.StatusCode, test.description)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
|
||||
c "desq.com.ru/testjwtauth/config"
|
||||
)
|
||||
|
||||
func NotifyEmail(m string, r string) error {
|
||||
a := smtp.PlainAuth("", c.SMTPEmail, c.SMTPPassword, c.SMTPHost)
|
||||
|
||||
message := "From: " + c.SMTPEmail + "\n" +
|
||||
"To: " + r + "\n" +
|
||||
"Subject: Access from an unknown IP\n\n" +
|
||||
m
|
||||
|
||||
if err := smtp.SendMail(fmt.Sprintf("%s:%d", c.SMTPHost, 587), a, c.SMTPEmail, []string{r}, []byte(message)); err != nil {
|
||||
return fmt.Errorf("failed to send email: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"desq.com.ru/testjwtauth/config"
|
||||
)
|
||||
|
||||
func TestNotifyEmail(t *testing.T) {
|
||||
type args struct {
|
||||
m string
|
||||
r string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "WithValidEmail",
|
||||
args: args{
|
||||
m: "TestNotifyEmail",
|
||||
r: config.MockSMTPRecipient,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "WithInvalidEmail",
|
||||
args: args{
|
||||
m: "TestNotifyEmail",
|
||||
r: "",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := NotifyEmail(tt.args.m, tt.args.r); (err != nil) != tt.wantErr {
|
||||
t.Errorf("NotifyEmail() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func GenerateToken(l int) (string, error) {
|
||||
bytes := make([]byte, l)
|
||||
|
||||
_, err := rand.Read(bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token := base64.StdEncoding.EncodeToString(bytes)
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func EncodeToken(token string) (string, error) {
|
||||
encodedToken, err := bcrypt.GenerateFromPassword([]byte(token), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(encodedToken), nil
|
||||
}
|
||||
|
||||
func CompareToken(hash string, token string) bool {
|
||||
eq := bcrypt.CompareHashAndPassword([]byte(hash), []byte(token))
|
||||
|
||||
return eq == nil
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerateToken(t *testing.T) {
|
||||
type args struct {
|
||||
l int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "GenerateRefreshToken",
|
||||
args: args{
|
||||
l: 32,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := GenerateToken(tt.args.l)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GenerateToken() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeToken(t *testing.T) {
|
||||
token, err := GenerateToken(32)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate refresh token")
|
||||
}
|
||||
type args struct {
|
||||
token string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "EncodeToken",
|
||||
args: args{
|
||||
token: token,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := EncodeToken(tt.args.token)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("EncodeToken() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user