Welcome back! In the previous chapter, File System & Indexing, we built the "Librarian" capable of finding and moving files on your hard drive.
However, right now, our library has an open door policy. Anyone who connects to the server can delete your vacation photos or read your private notes.
In this chapter, we will build the Security Guard. We will learn how Filebrowser stores user identities, verifies passwords, and issues "ID badges" (tokens) so users can access files safely.
Without authentication, a web server processes every request it receives.
DELETE /important_document.pdfWe need a system to ask: "Who are you?" and "Are you allowed to do this?"
Filebrowser solves this using three main components:
database/users)
Filebrowser doesn't use a massive SQL server. It uses BoltDB, a simple key-value store that lives in a single file (filebrowser.db) on your disk.
Here is how a User is defined in the code:
// backend/database/users/users.go
type User struct {
ID uint `json:"id"`
Username string `json:"username"`
Password string `json:"password"` // This is HASHED, not plain text!
Permissions Permissions `json:"permissions"`
Locale string `json:"locale"` // e.g., "en", "fr"
}
Explanation:
$2a$10$Xk9... (the hash).Admin, Delete, Download).When you log in successfully, the server gives you a JSON Web Token (JWT).
Let's look at the most critical scenario: A user (Alice) wants to Log In.
/api/auth/login.Here is a simplified look at how the HTTP handler processes a login attempt.
// backend/http/auth.go
func loginHandler(w http.ResponseWriter, r *http.Request, d *requestContext) (int, error) {
// 1. The middleware has already fetched the user from the DB
user := d.user
// 2. Check if the user has Two-Factor Auth (OTP) enforced
if user.LockPassword && user.TOTPSecret == "" {
return http.StatusForbidden, errors.ErrNoTotpConfigured
}
// 3. Issue the "ID Badge" (Token)
return printToken(w, r, user)
}
Explanation:
d.user: Before this function runs, a helper (middleware) finds the user in the DB.printToken: This is the function that actually generates the cryptographic string (the JWT).How does the server know "Alice" is actually Alice?
The printToken function calls makeSignedTokenAPI. This is where the math happens. We use the HMAC-SHA256 algorithm to sign the token using a secret key only the server knows.
// backend/http/auth.go
func makeSignedTokenAPI(user *users.User, ...) (users.AuthToken, error) {
// 1. Define what info goes onto the "Badge" (Claims)
claims := users.MinimalAuthToken{
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(2 * time.Hour)),
Issuer: "FileBrowser Quantum",
},
}
// 2. Create the token object
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// 3. Sign it with the server's secret key
tokenString, err := token.SignedString([]byte(config.Auth.Key))
return tokenString, err
}
Explanation:
Once Alice has her cookie, every time she clicks a file, the extractToken function runs.
// backend/http/auth.go
func extractToken(r *http.Request) (string, error) {
// 1. Check for the Cookie first
cookie, err := r.Cookie("filebrowser_quantum_jwt")
if err == nil {
return cookie.Value, nil
}
// 2. Check the URL (e.g. ?auth=...)
auth := r.URL.Query().Get("auth")
if auth != "" {
return auth, nil
}
// 3. Check the Headers (Bearer Token)
authHeader := r.Header.Get("Authorization")
// ... logic to parse "Bearer <token>" ...
return "", fmt.Errorf("no token found")
}
Explanation: This function is the "Checkpoint." It looks in the user's pockets (Cookie), their hand (Query Param), and their hat (Header) to find the ID badge. If found, the server allows the request to proceed to the File System we built in Chapter 1.
Authentication answers "Who are you?". Authorization answers "What can you do?".
In backend/database/users/users.go, we see the Permissions struct:
type Permissions struct {
Admin bool `json:"admin"` // Can change settings
Modify bool `json:"modify"` // Can rename/move files
Delete bool `json:"delete"` // Can delete files
Download bool `json:"download"` // Can download files
Share bool `json:"share"` // Can create public links
}
When a user tries to delete a file, the code checks:
user.Permissions.Delete == true? (Authorization)If either is false, the "Security Guard" stops the request.
In this chapter, we secured our application:
Now that we can read files (Chapter 1) and we know who is asking for them (Chapter 2), we need a way to organize these requests into a web server.
Next Chapter: HTTP API & Routing
Generated by Code IQ