initial commit
This commit is contained in:
commit
e8da0f7874
15 files changed
+722
No files matched your search
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user