| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- package auth
- import (
- "errors"
- "time"
- "github.com/golang-jwt/jwt/v5"
- "gogs.dmsc.dev/arp/models"
- "golang.org/x/crypto/bcrypt"
- )
- // Token expiration: 10 years
- const TokenExpiration = 10 * 365 * 24 * time.Hour
- // JWT secret key - in production this should be loaded from environment
- var jwtSecret = []byte("your-secret-key-change-in-production")
- // Claims represents the JWT claims
- type Claims struct {
- UserID uint `json:"user_id"`
- Email string `json:"email"`
- Roles []RoleClaim `json:"roles"`
- Permissions []string `json:"permissions"`
- jwt.RegisteredClaims
- }
- // RoleClaim represents a role in the JWT
- type RoleClaim struct {
- ID uint `json:"id"`
- Name string `json:"name"`
- }
- // HashPassword hashes a password using bcrypt
- func HashPassword(password string) (string, error) {
- bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
- return string(bytes), err
- }
- // CheckPassword verifies a password against a hash
- func CheckPassword(password, hash string) bool {
- err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
- return err == nil
- }
- // GenerateToken creates a JWT token for a user
- func GenerateToken(user models.User) (string, error) {
- // Build role claims
- roles := make([]RoleClaim, len(user.Roles))
- for i, role := range user.Roles {
- roles[i] = RoleClaim{
- ID: role.ID,
- Name: role.Name,
- }
- }
- // Build permission codes (deduplicated)
- permSet := make(map[string]bool)
- for _, role := range user.Roles {
- for _, perm := range role.Permissions {
- permSet[perm.Code] = true
- }
- }
- permissions := make([]string, 0, len(permSet))
- for perm := range permSet {
- permissions = append(permissions, perm)
- }
- claims := Claims{
- UserID: user.ID,
- Email: user.Email,
- Roles: roles,
- Permissions: permissions,
- RegisteredClaims: jwt.RegisteredClaims{
- ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpiration)),
- IssuedAt: jwt.NewNumericDate(time.Now()),
- },
- }
- token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
- return token.SignedString(jwtSecret)
- }
- // ValidateToken validates a JWT token and returns the claims
- func ValidateToken(tokenString string) (*Claims, error) {
- token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
- if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
- return nil, errors.New("unexpected signing method")
- }
- return jwtSecret, nil
- })
- if err != nil {
- return nil, err
- }
- if claims, ok := token.Claims.(*Claims); ok && token.Valid {
- return claims, nil
- }
- return nil, errors.New("invalid token")
- }
- // SetJWTSecret sets the JWT secret key (for testing or configuration)
- func SetJWTSecret(secret []byte) {
- jwtSecret = secret
- }
|