fix add auth midlleware
This commit is contained in:
parent
b9fb8015e3
commit
aa8c326b25
22
.env
22
.env
@ -5,7 +5,7 @@ HTTP_PORT="8080"
|
||||
|
||||
# MongoDB settings
|
||||
MONGO_HOST="127.0.0.1"
|
||||
MONGO_PORT="27021"
|
||||
MONGO_PORT="27020"
|
||||
MONGO_USER="test"
|
||||
MONGO_PASSWORD="test"
|
||||
MONGO_DB="admin"
|
||||
@ -21,6 +21,26 @@ PUBLIC_CURVE_KEY="-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAEbnIvjIMle4rqVol6K
|
||||
|
||||
PRIVATE_CURVE_KEY="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIKn0BKwF3vZvODgWAnUIwQhd8de5oZhY48gc23EWfrfs\n-----END PRIVATE KEY-----"
|
||||
|
||||
JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2B
|
||||
iw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikH
|
||||
oKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAEC
|
||||
gYAOphnVPXbk6lpYzdkLC1Xn5EOEuNfOLLURLxBnPWozZo26r/Mtahu/9mYhrYlv
|
||||
PP8r6mxta3VIil8iOdZyOLa/4d1LPd+UehgEXIJEiYXLtn7RS5eUnoPuQxssfs1k
|
||||
OWjdN8p6SzppleegFTvGRX4KM3cDLfSphOk8JuBCrpSSYQJBAOdqizTSrdKMTuVe
|
||||
c7Jk1JOJkyFuFs+N5zeryyeFGH7IpRdWy0rkWMxIUAi8Ap1vYVBPHv4tDOo3sy5X
|
||||
VLc/knkCQQCE62pg+0TmsrhO/2Pgog6MLBkzlzXYMRp/01HbmznwYF+ejfPnzLkz
|
||||
hnUlxRUNK3lhXM/7H6oAjvqF2R72u/OPAkEAterkmdbQfEZ+MwNoEiH/lie9OLdx
|
||||
SSI1VGdBYcTYN7qFRW6eizYstBJYkDU0HQ0Uw+we4hMKJwk4W0KdvxxDiQJAeqlB
|
||||
V1QqBneBbK10PzVuFV8QtrJhJyxRVwrtbKq38iMNuqUnI4+ijXEUpJFWVvv6nKXo
|
||||
7McQvEk12dU/JNTX8wJAOlAtSNjp9tVwpMpC0w2St1eKc1L2SknjeohA5ldoBz8sGeZsPhTU3eHSD1neAZXLKN5K68z3zFBr20ubY9nyLw==
|
||||
-----END RSA PRIVATE KEY-----"
|
||||
|
||||
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAE=\n-----END PUBLIC KEY-----"
|
||||
|
||||
JWT_ISSUER="pena-auth-service"
|
||||
|
||||
JWT_AUDIENCE="pena"
|
||||
# SIGN_SECRET="group"
|
||||
|
||||
SIGN_SECRET="secret"
|
||||
|
||||
@ -6,8 +6,9 @@ services:
|
||||
ports:
|
||||
- "27020:27017"
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
|
||||
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
|
||||
- MONGO_INITDB_ROOT_USERNAME=test
|
||||
- MONGO_INITDB_ROOT_PASSWORD=test
|
||||
- MONGO_INITDB_AUTH_MECHANISM=SCRAM-SHA-1
|
||||
volumes:
|
||||
- mongo_data:/data/db
|
||||
|
||||
|
||||
1
go.mod
1
go.mod
@ -6,6 +6,7 @@ require (
|
||||
github.com/caarlos0/env/v8 v8.0.0
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/gofiber/fiber/v2 v2.51.0
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/pioz/faker v1.7.3
|
||||
github.com/rs/xid v1.5.0
|
||||
|
||||
2
go.sum
2
go.sum
@ -15,6 +15,8 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ=
|
||||
github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"codeword/internal/worker/purge_worker"
|
||||
"codeword/internal/worker/recovery_worker"
|
||||
"codeword/pkg/closer"
|
||||
"codeword/utils"
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/twmb/franz-go/pkg/kgo"
|
||||
@ -95,8 +96,11 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
||||
DiscountClient: discountRpcClient,
|
||||
})
|
||||
|
||||
jwtUtil := utils.NewJWT(&cfg)
|
||||
authMiddleware := utils.NewAuthenticator(jwtUtil)
|
||||
|
||||
recoveryController := recovery.NewRecoveryController(logger, recoveryService, cfg.DefaultRedirectionURL)
|
||||
promoCodeController := promocode.NewPromoCodeController(logger, promoService)
|
||||
promoCodeController := promocode.NewPromoCodeController(promocode.Deps{Logger: logger, PromoCodeService: promoService, AuthMiddleware: authMiddleware})
|
||||
|
||||
recoveryWC := recovery_worker.NewRecoveryWC(recovery_worker.Deps{
|
||||
Logger: logger,
|
||||
|
||||
@ -9,15 +9,23 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
Logger *zap.Logger
|
||||
PromoCodeService *services.PromoCodeService
|
||||
AuthMiddleware func(*fiber.Ctx) error
|
||||
}
|
||||
|
||||
type PromoCodeController struct {
|
||||
logger *zap.Logger
|
||||
promoCodeService *services.PromoCodeService
|
||||
authMiddleware func(*fiber.Ctx) error
|
||||
}
|
||||
|
||||
func NewPromoCodeController(logger *zap.Logger, promoCodeService *services.PromoCodeService) *PromoCodeController {
|
||||
func NewPromoCodeController(deps Deps) *PromoCodeController {
|
||||
return &PromoCodeController{
|
||||
logger: logger,
|
||||
promoCodeService: promoCodeService,
|
||||
logger: deps.Logger,
|
||||
promoCodeService: deps.PromoCodeService,
|
||||
authMiddleware: deps.AuthMiddleware,
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,20 +97,27 @@ func (p *PromoCodeController) GetList(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
|
||||
err := p.authMiddleware(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err})
|
||||
}
|
||||
|
||||
userID := c.Locals(models.AuthJWTDecodedUserIDKey).(string)
|
||||
|
||||
if userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "failed to get jwt payload"})
|
||||
}
|
||||
|
||||
var req models.ActivateReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
if req.UserID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "userid is required"})
|
||||
}
|
||||
|
||||
if req.Codeword == "" && req.FastLink == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "codeword or fastlink is required"})
|
||||
}
|
||||
|
||||
greetings, err := p.promoCodeService.ActivatePromo(c.Context(), &req)
|
||||
greetings, err := p.promoCodeService.ActivatePromo(c.Context(), &req, userID)
|
||||
if err != nil {
|
||||
p.logger.Error("Failed to activate promocode", zap.Error(err))
|
||||
|
||||
@ -113,10 +128,7 @@ func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
resp := models.ActivateResp{
|
||||
Greetings: greetings,
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(resp)
|
||||
return c.Status(fiber.StatusOK).JSON(models.ActivateResp{Greetings: greetings})
|
||||
}
|
||||
|
||||
func (p *PromoCodeController) Delete(c *fiber.Ctx) error {
|
||||
|
||||
@ -35,6 +35,10 @@ type Config struct {
|
||||
KafkaTopic string `env:"KAFKA_TOPIC_TARIFF"`
|
||||
DiscountServiceAddress string `env:"DISCOUNT_ADDRESS"`
|
||||
RecoveryUrl string `env:"RECOVERY_URL"`
|
||||
PrivateKey string `env:"JWT_PRIVATE_KEY"`
|
||||
PublicKey string `env:"JWT_PUBLIC_KEY,required"`
|
||||
Issuer string `env:"JWT_ISSUER,required"`
|
||||
Audience string `env:"JWT_AUDIENCE,required"`
|
||||
}
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
|
||||
@ -9,3 +9,6 @@ type RefreshResponse struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
}
|
||||
|
||||
const AuthJWTDecodedUserIDKey = "userID"
|
||||
const AuthJWTDecodedAccessTokenKey = "access-token"
|
||||
|
||||
@ -59,7 +59,6 @@ type GetPromoCodesListResp struct {
|
||||
}
|
||||
|
||||
type ActivateReq struct {
|
||||
UserID string `json:"userID"` // для кого активировать нужно для кафки
|
||||
Codeword string `json:"codeword"`
|
||||
FastLink string `json:"fastLink"`
|
||||
}
|
||||
|
||||
@ -86,14 +86,14 @@ func (s *PromoCodeService) GetPromoCodesList(ctx context.Context, req *models.Ge
|
||||
// разделяется ли она или они всегда вместе, если разделяются то что-то из этого может быть пустым либо все заполеннное,
|
||||
// соответсвенно надо сделать соответствующие проверки до записи в кафку и до отправки в дискаунт сервис
|
||||
|
||||
func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.ActivateReq) (string, error) {
|
||||
func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.ActivateReq, userID string) (string, error) {
|
||||
promoCode, err := s.promoCodeRepo.ActivatePromo(ctx, req)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to activate promocode", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = s.statsRepo.UpdateStatistics(ctx, promoCode.ID.Hex(), req.UserID)
|
||||
err = s.statsRepo.UpdateStatistics(ctx, promoCode.ID.Hex(), userID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed add in stats", zap.Error(err))
|
||||
return "", err
|
||||
@ -118,7 +118,7 @@ func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.Activa
|
||||
Deleted: promoCode.Delete,
|
||||
CreatedAt: promoCode.CreatedAt,
|
||||
}
|
||||
if err := s.kafka.Send(ctx, req.UserID, fakeTariff); err != nil {
|
||||
if err := s.kafka.Send(ctx, userID, fakeTariff); err != nil {
|
||||
s.logger.Error("Failed to send fake tariff to Kafka", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
@ -130,7 +130,7 @@ func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.Activa
|
||||
Description: "",
|
||||
Condition: &discount.DiscountCondition{
|
||||
Coupon: &promoCode.Codeword,
|
||||
User: &req.UserID,
|
||||
User: &userID,
|
||||
},
|
||||
Target: &discount.DiscountCalculationTarget{
|
||||
Factor: promoCode.Bonus.Discount.Factor,
|
||||
|
||||
@ -2,6 +2,7 @@ package e2e
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/tests/helpers"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@ -420,24 +421,30 @@ func TestGetPromoCodesList(t *testing.T) {
|
||||
// ActivatePromoCode
|
||||
func TestActivatePromoCode(t *testing.T) {
|
||||
client := fiber.AcquireClient()
|
||||
|
||||
jwtUtil := helpers.InitializeJWT()
|
||||
token, tokenErr := jwtUtil.Create(ExampleUserID)
|
||||
fmt.Println(token)
|
||||
if isNoError := assert.NoError(t, tokenErr); !isNoError {
|
||||
return
|
||||
}
|
||||
t.Run("ActivatePromoCode-success codeword", func(t *testing.T) {
|
||||
reqBody := models.ActivateReq{
|
||||
UserID: ExampleUserID,
|
||||
Codeword: "example",
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
fmt.Println(string(resBody))
|
||||
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
fmt.Println(statusCode)
|
||||
var response models.ActivateResp
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
@ -446,13 +453,12 @@ func TestActivatePromoCode(t *testing.T) {
|
||||
|
||||
t.Run("ActivatePromoCode-success fastLink", func(t *testing.T) {
|
||||
reqBody := models.ActivateReq{
|
||||
UserID: ExampleUserID,
|
||||
FastLink: fastLink,
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
@ -473,7 +479,7 @@ func TestActivatePromoCode(t *testing.T) {
|
||||
}
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
@ -489,13 +495,8 @@ func TestActivatePromoCode(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("ActivatePromoCode-missing codeword and fastlink", func(t *testing.T) {
|
||||
reqBody := models.ActivateReq{
|
||||
UserID: ExampleUserID,
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(nil)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
@ -512,13 +513,12 @@ func TestActivatePromoCode(t *testing.T) {
|
||||
|
||||
t.Run("ActivatePromoCode-promocode not found", func(t *testing.T) {
|
||||
reqBody := models.ActivateReq{
|
||||
UserID: ExampleUserID,
|
||||
Codeword: "none",
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.Error(t, errs[0])
|
||||
|
||||
38
tests/helpers/jwt.go
Normal file
38
tests/helpers/jwt.go
Normal file
@ -0,0 +1,38 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"codeword/internal/initialize"
|
||||
"codeword/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func InitializeJWT() *utils.JWT {
|
||||
publicKey := strings.Replace(`-----BEGIN PUBLIC KEY-----
|
||||
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm69
|
||||
80fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6B
|
||||
dA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y
|
||||
+3GyaOY536H47qyXAgMBAAE=
|
||||
-----END PUBLIC KEY-----`, "\t", "", -1)
|
||||
|
||||
privateKey := strings.Replace(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2B
|
||||
iw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikH
|
||||
oKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAEC
|
||||
gYAOphnVPXbk6lpYzdkLC1Xn5EOEuNfOLLURLxBnPWozZo26r/Mtahu/9mYhrYlv
|
||||
PP8r6mxta3VIil8iOdZyOLa/4d1LPd+UehgEXIJEiYXLtn7RS5eUnoPuQxssfs1k
|
||||
OWjdN8p6SzppleegFTvGRX4KM3cDLfSphOk8JuBCrpSSYQJBAOdqizTSrdKMTuVe
|
||||
c7Jk1JOJkyFuFs+N5zeryyeFGH7IpRdWy0rkWMxIUAi8Ap1vYVBPHv4tDOo3sy5X
|
||||
VLc/knkCQQCE62pg+0TmsrhO/2Pgog6MLBkzlzXYMRp/01HbmznwYF+ejfPnzLkz
|
||||
hnUlxRUNK3lhXM/7H6oAjvqF2R72u/OPAkEAterkmdbQfEZ+MwNoEiH/lie9OLdx
|
||||
SSI1VGdBYcTYN7qFRW6eizYstBJYkDU0HQ0Uw+we4hMKJwk4W0KdvxxDiQJAeqlB
|
||||
V1QqBneBbK10PzVuFV8QtrJhJyxRVwrtbKq38iMNuqUnI4+ijXEUpJFWVvv6nKXo
|
||||
7McQvEk12dU/JNTX8wJAOlAtSNjp9tVwpMpC0w2St1eKc1L2SknjeohA5ldoBz8sGeZsPhTU3eHSD1neAZXLKN5K68z3zFBr20ubY9nyLw==
|
||||
-----END RSA PRIVATE KEY-----`, "\t", "", -1)
|
||||
|
||||
return utils.NewJWT(&initialize.Config{
|
||||
PrivateKey: privateKey,
|
||||
PublicKey: publicKey,
|
||||
Audience: "pena",
|
||||
Issuer: "pena-auth-service",
|
||||
})
|
||||
}
|
||||
46
utils/authenticator.go
Normal file
46
utils/authenticator.go
Normal file
@ -0,0 +1,46 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
prefix = "Bearer "
|
||||
)
|
||||
|
||||
func NewAuthenticator(jwtUtil *JWT) func(*fiber.Ctx) error {
|
||||
return func(c *fiber.Ctx) error {
|
||||
if jwtUtil == nil {
|
||||
return fmt.Errorf("jwt util is nil")
|
||||
}
|
||||
|
||||
if err := authenticate(c, jwtUtil); err != nil {
|
||||
return fmt.Errorf("authentication error:%d", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func authenticate(c *fiber.Ctx, jwtUtil *JWT) error {
|
||||
authHeader := c.Get("Authorization")
|
||||
if authHeader == "" || !strings.HasPrefix(authHeader, prefix) {
|
||||
return fmt.Errorf("failed to parse jws from request header: %s", authHeader)
|
||||
}
|
||||
|
||||
jws := strings.TrimPrefix(authHeader, prefix)
|
||||
|
||||
userID, validateErr := jwtUtil.Validate(jws)
|
||||
if validateErr != nil {
|
||||
return validateErr
|
||||
}
|
||||
|
||||
c.Locals(models.AuthJWTDecodedUserIDKey, userID)
|
||||
c.Locals(models.AuthJWTDecodedAccessTokenKey, jws)
|
||||
|
||||
return nil
|
||||
}
|
||||
89
utils/jwt.go
Normal file
89
utils/jwt.go
Normal file
@ -0,0 +1,89 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"codeword/internal/initialize"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"time"
|
||||
)
|
||||
|
||||
type JWT struct {
|
||||
privateKey []byte
|
||||
publicKey []byte
|
||||
algorithm *jwt.SigningMethodRSA
|
||||
expiresIn time.Duration
|
||||
issuer string
|
||||
audience string
|
||||
}
|
||||
|
||||
func NewJWT(configuration *initialize.Config) *JWT {
|
||||
return &JWT{
|
||||
privateKey: []byte(configuration.PrivateKey),
|
||||
publicKey: []byte(configuration.PublicKey),
|
||||
issuer: configuration.Issuer,
|
||||
audience: configuration.Audience,
|
||||
algorithm: jwt.SigningMethodRS256,
|
||||
expiresIn: 15 * time.Minute,
|
||||
}
|
||||
}
|
||||
|
||||
func (j *JWT) Create(id string) (string, error) {
|
||||
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(j.privateKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse private key on <Create> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"id": id,
|
||||
"exp": now.Add(j.expiresIn).Unix(),
|
||||
"aud": j.audience,
|
||||
"iss": j.issuer,
|
||||
}
|
||||
|
||||
token, err := jwt.NewWithClaims(j.algorithm, claims).SignedString(privateKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to sing on <Create> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (j *JWT) Validate(tokenString string) (string, error) {
|
||||
key, err := jwt.ParseRSAPublicKeyFromPEM(j.publicKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse rsa public key on <Validate> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
parseCallback := func(token *jwt.Token) (any, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %s", token.Header["alg"])
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(
|
||||
tokenString,
|
||||
parseCallback,
|
||||
jwt.WithAudience(j.audience),
|
||||
jwt.WithIssuer(j.issuer),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse jwt token on <Validate> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok || !token.Valid {
|
||||
return "", errors.New("token is invalid on <Validate> of <JWT>")
|
||||
}
|
||||
|
||||
data, ok := claims["id"].(string)
|
||||
if !ok {
|
||||
return "", errors.New("data is empty or not a string on <Validate> of <JWT>")
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user