Update besar
This commit is contained in:
+160
-33
@@ -1,9 +1,11 @@
|
||||
package services
|
||||
// services/auth/service.go
|
||||
package auth
|
||||
|
||||
import (
|
||||
"api-service/internal/config"
|
||||
models "api-service/internal/models/auth"
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
@@ -12,8 +14,9 @@ import (
|
||||
|
||||
// AuthService handles authentication logic
|
||||
type AuthService struct {
|
||||
config *config.Config
|
||||
users map[string]*models.User // In-memory user store for demo
|
||||
config *config.Config
|
||||
users map[string]*models.User // In-memory user store for demo
|
||||
jwtSecret []byte
|
||||
}
|
||||
|
||||
// NewAuthService creates a new authentication service
|
||||
@@ -38,9 +41,16 @@ func NewAuthService(cfg *config.Config) *AuthService {
|
||||
Role: "user",
|
||||
}
|
||||
|
||||
// Get JWT secret from environment or use default
|
||||
jwtSecret := []byte(os.Getenv("JWT_SECRET"))
|
||||
if len(jwtSecret) == 0 {
|
||||
jwtSecret = []byte("your-secret-key-change-this-in-production")
|
||||
}
|
||||
|
||||
return &AuthService{
|
||||
config: cfg,
|
||||
users: users,
|
||||
config: cfg,
|
||||
users: users,
|
||||
jwtSecret: jwtSecret,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,65 +68,148 @@ func (s *AuthService) Login(username, password string) (*models.TokenResponse, e
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
token, err := s.generateToken(user)
|
||||
token, expiresIn, err := s.generateToken(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate refresh token
|
||||
refreshToken, err := s.generateRefreshToken(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.TokenResponse{
|
||||
AccessToken: token,
|
||||
TokenType: "Bearer",
|
||||
ExpiresIn: 3600, // 1 hour
|
||||
AccessToken: token,
|
||||
RefreshToken: refreshToken,
|
||||
TokenType: "Bearer",
|
||||
ExpiresIn: expiresIn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generateToken creates a new JWT token for the user
|
||||
func (s *AuthService) generateToken(user *models.User) (string, error) {
|
||||
// RefreshToken generates a new access token using a valid refresh token
|
||||
func (s *AuthService) RefreshToken(refreshTokenString string) (*models.TokenResponse, error) {
|
||||
// Parse and validate the refresh token
|
||||
token, err := jwt.Parse(refreshTokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, errors.New("unexpected signing method")
|
||||
}
|
||||
return s.jwtSecret, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid refresh token")
|
||||
}
|
||||
|
||||
if !token.Valid {
|
||||
return nil, errors.New("invalid refresh token")
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid token claims")
|
||||
}
|
||||
|
||||
// Check if it's a refresh token
|
||||
tokenType, ok := claims["type"].(string)
|
||||
if !ok || tokenType != "refresh" {
|
||||
return nil, errors.New("not a refresh token")
|
||||
}
|
||||
|
||||
// Get user ID from claims
|
||||
userID, ok := claims["user_id"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid user ID in token")
|
||||
}
|
||||
|
||||
// Find user
|
||||
var user *models.User
|
||||
for _, u := range s.users {
|
||||
if u.ID == userID {
|
||||
user = u
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
|
||||
// Generate new access token
|
||||
accessToken, expiresIn, err := s.generateToken(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate new refresh token
|
||||
newRefreshToken, err := s.generateRefreshToken(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.TokenResponse{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: newRefreshToken,
|
||||
TokenType: "Bearer",
|
||||
ExpiresIn: expiresIn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generateToken creates a new JWT access token for the user
|
||||
func (s *AuthService) generateToken(user *models.User) (string, int64, error) {
|
||||
// Create claims
|
||||
now := time.Now()
|
||||
expiresAt := now.Add(time.Hour * 1) // 1 hour expiration
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"user_id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"role": user.Role,
|
||||
"exp": time.Now().Add(time.Hour * 1).Unix(),
|
||||
"iat": time.Now().Unix(),
|
||||
"type": "access",
|
||||
"exp": expiresAt.Unix(),
|
||||
"iat": now.Unix(),
|
||||
}
|
||||
|
||||
// Create token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
// Sign token with secret key
|
||||
secretKey := []byte(s.getJWTSecret())
|
||||
return token.SignedString(secretKey)
|
||||
tokenString, err := token.SignedString(s.jwtSecret)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
return tokenString, int64(time.Hour.Seconds()), nil
|
||||
}
|
||||
|
||||
// GenerateTokenForUser generates a JWT token for a specific user
|
||||
func (s *AuthService) GenerateTokenForUser(user *models.User) (string, error) {
|
||||
// generateRefreshToken creates a new JWT refresh token for the user
|
||||
func (s *AuthService) generateRefreshToken(user *models.User) (string, error) {
|
||||
// Create claims
|
||||
now := time.Now()
|
||||
expiresAt := now.Add(time.Hour * 24 * 7) // 7 days expiration
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"user_id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"role": user.Role,
|
||||
"exp": time.Now().Add(time.Hour * 1).Unix(),
|
||||
"iat": time.Now().Unix(),
|
||||
"user_id": user.ID,
|
||||
"type": "refresh",
|
||||
"exp": expiresAt.Unix(),
|
||||
"iat": now.Unix(),
|
||||
}
|
||||
|
||||
// Create token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
// Sign token with secret key
|
||||
secretKey := []byte(s.getJWTSecret())
|
||||
return token.SignedString(secretKey)
|
||||
return token.SignedString(s.jwtSecret)
|
||||
}
|
||||
|
||||
// ValidateToken validates the JWT token
|
||||
// ValidateToken validates the JWT access token
|
||||
func (s *AuthService) ValidateToken(tokenString string) (*models.JWTClaims, error) {
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, errors.New("unexpected signing method")
|
||||
}
|
||||
return []byte(s.getJWTSecret()), nil
|
||||
return s.jwtSecret, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -132,6 +225,12 @@ func (s *AuthService) ValidateToken(tokenString string) (*models.JWTClaims, erro
|
||||
return nil, errors.New("invalid claims")
|
||||
}
|
||||
|
||||
// Check if it's an access token
|
||||
tokenType, ok := claims["type"].(string)
|
||||
if !ok || tokenType != "access" {
|
||||
return nil, errors.New("not an access token")
|
||||
}
|
||||
|
||||
return &models.JWTClaims{
|
||||
UserID: claims["user_id"].(string),
|
||||
Username: claims["username"].(string),
|
||||
@@ -140,12 +239,6 @@ func (s *AuthService) ValidateToken(tokenString string) (*models.JWTClaims, erro
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getJWTSecret returns the JWT secret key
|
||||
func (s *AuthService) getJWTSecret() string {
|
||||
// In production, this should come from environment variables
|
||||
return "your-secret-key-change-this-in-production"
|
||||
}
|
||||
|
||||
// RegisterUser registers a new user (for demo purposes)
|
||||
func (s *AuthService) RegisterUser(username, email, password, role string) error {
|
||||
if _, exists := s.users[username]; exists {
|
||||
@@ -167,3 +260,37 @@ func (s *AuthService) RegisterUser(username, email, password, role string) error
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateToken generates a JWT token for the given user data (public method)
|
||||
func (s *AuthService) GenerateToken(userID, username, email, role string) (*models.TokenResponse, error) {
|
||||
user := &models.User{
|
||||
ID: userID,
|
||||
Username: username,
|
||||
Email: email,
|
||||
Role: role,
|
||||
}
|
||||
|
||||
// Generate access token
|
||||
token, expiresIn, err := s.generateToken(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate refresh token
|
||||
refreshToken, err := s.generateRefreshToken(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.TokenResponse{
|
||||
AccessToken: token,
|
||||
RefreshToken: refreshToken,
|
||||
TokenType: "Bearer",
|
||||
ExpiresIn: expiresIn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GenerateTokenDirect generates a JWT token directly for the given user data (public method)
|
||||
func (s *AuthService) GenerateTokenDirect(userID, username, email, role string) (*models.TokenResponse, error) {
|
||||
return s.GenerateToken(userID, username, email, role)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user