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.
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 < toke n >
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