Skip to main content

Overview

DefDrive uses JSON Web Tokens (JWT) for stateless authentication and bcrypt for secure password hashing. The authentication system ensures that only registered users can access protected resources.

JWT Tokens

72-hour expiration with HS256 signing

Password Security

bcrypt hashing with default cost factor

Stateless Auth

No server-side session storage required

Bearer Token

Standard Authorization header format

User Registration

When a user signs up, their password is securely hashed using bcrypt before being stored in the database.

Password Hashing

The system uses bcrypt with the default cost factor (currently 10) to hash passwords:
// From controllers/user.go:33-38
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"})
    return
}
user.Password = string(hashedPassword)
Why bcrypt? bcrypt is designed to be slow, making brute-force attacks computationally expensive. The default cost of 10 means 2^10 (1,024) iterations.

User Model

The User model stores hashed passwords along with account limits:
// From models/user.go:7-18
type User struct {
    gorm.Model
    Name     string
    Email    string
    Username string `gorm:"unique"`
    Password string
    
    MaxFiles   int   `gorm:"default:100"`        // default 100 files
    MaxStorage int64 `gorm:"default:1073741824"` // default 1GB

    Files []File `gorm:"foreignKey:UserID;references:ID"`
}

Login Flow

The authentication flow consists of three main steps: credential validation, password verification, and token generation.

1. Credential Validation

The system first looks up the user by username:
// From controllers/user.go:64-68
var user models.User
if err := uc.DB.Where("username = ?", loginRequest.Username).First(&user).Error; err != nil {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"})
    return
}

2. Password Verification

The provided password is compared with the stored bcrypt hash:
// From controllers/user.go:70-74
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(loginRequest.Password)); err != nil {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid username or password"})
    return
}
The same error message is returned for both invalid username and invalid password to prevent user enumeration attacks.

3. JWT Token Generation

Upon successful authentication, a JWT token is generated with user claims:
// From controllers/user.go:76-87
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
    "userID":   user.ID,
    "username": user.Username,
    "exp":      time.Now().Add(time.Hour * 72).Unix(),
})

tokenString, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))
if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
    return
}
Tokens are valid for 72 hours from the time of issuance. After expiration, users must log in again.

Token Validation

The AuthRequired middleware validates JWT tokens on protected routes.

Authorization Header Format

Tokens must be sent in the Authorization header using the Bearer scheme:
Authorization: Bearer <token>

Validation Process

// From middleware/auth.go:14-29
func AuthRequired() gin.HandlerFunc {
    return func(c *gin.Context) {
        // Get authorization header
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header is required"})
            c.Abort()
            return
        }

        // Check if the header has the Bearer format
        headerParts := strings.Split(authHeader, " ")
        if len(headerParts) != 2 || headerParts[0] != "Bearer" {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header format must be Bearer {token}"})
            c.Abort()
            return
        }
        // ...
    }
}

Token Parsing and Validation

The middleware validates the token signature and extracts user claims:
// From middleware/auth.go:35-46
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
    // Ensure the token method conforms to "SigningMethodHMAC"
    if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
        return nil, jwt.NewValidationError("unexpected signing method", jwt.ValidationErrorSignatureInvalid)
    }
    return []byte(os.Getenv("JWT_SECRET")), nil
})

if err != nil || !token.Valid {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
    c.Abort()
    return
}

User Context

After successful validation, the user ID is stored in the request context for use in controllers:
// From middleware/auth.go:49-56
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
    c.Set("userID", uint(claims["userID"].(float64)))
} else {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims"})
    c.Abort()
    return
}

Security Considerations

Secret Key Management

The JWT_SECRET environment variable must be kept secure and never committed to version control. Use a strong, randomly generated secret (at least 32 characters).

HTTPS Required

Always use HTTPS in production to prevent token interception. JWT tokens transmitted over HTTP can be stolen by attackers.

Token Storage

Clients should store tokens securely (e.g., httpOnly cookies or secure storage). Avoid storing tokens in localStorage if possible due to XSS risks.

Password Requirements

While bcrypt provides strong hashing, consider implementing password complexity requirements at the application level.

API Endpoints

Sign Up

POST /api/signup
Content-Type: application/json

{
  "name": "John Doe",
  "email": "[email protected]",
  "username": "johndoe",
  "password": "securepassword123"
}
Reference: controllers/user.go:25-50

Login

POST /api/login
Content-Type: application/json

{
  "username": "johndoe",
  "password": "securepassword123"
}
Response:
{
  "message": "Login successful",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": 1,
    "username": "johndoe",
    "email": "[email protected]",
    "name": "John Doe"
  }
}
Reference: controllers/user.go:53-99

Protected Routes

All routes requiring authentication use the AuthRequired() middleware:
GET /api/files
Authorization: Bearer <token>
Reference: middleware/auth.go:13-60

Access Control

Learn about file access restrictions

File Management

Understand file uploads and ownership

User Limits

Explore storage and file quotas

Build docs developers (and LLMs) love