Filebright uses Laravel Sanctum for API token-based authentication. This provides a secure, stateless authentication system perfect for SPA (Single Page Application) architectures.
How Sanctum works
Laravel Sanctum issues API tokens that are stored client-side and sent with each request. The backend validates these tokens to authenticate users without sessions or cookies.
User registration
New users can create an account through the registration form.
Fill registration form
The user provides:
- Name: Full name (2-70 characters, letters/spaces/hyphens/apostrophes only)
- Email: Valid email address (unique in the system)
- Password: Minimum 8 characters with letters, numbers, and symbols
- Password confirmation: Must match the password
// AuthView.vue:18-23
const registerForm = reactive({
name: "",
email: "",
password: "",
password_confirmation: "",
});
Submit to API
The form data is sent to /api/register:// AuthController.php:13-43
public function register(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'min:2', 'max:70', 'regex:/^[a-zA-Z\s\-\']+$/'],
'email' => 'required|string|email|max:255|unique:users',
'password' => [
'required',
'string',
'confirmed',
Password::min(8)
->letters()
->numbers()
->symbols(),
'max:128'
],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
'token' => $token,
'token_type' => 'Bearer',
'user' => $user
], 201);
}
Receive authentication token
Upon successful registration:
- A new user record is created in the database with a hashed password
- A Sanctum API token is generated and returned
- The frontend stores the token and user data
- The user is automatically logged in and redirected to the dashboard
// AuthView.vue:46-52
const data = await apiClient.post(endpoint, payload);
if (data?.token) {
authStore.setToken(data.token);
authStore.setUser(data.user);
router.push("/dashboard");
}
Password requirements
Filebright enforces strong password policies:
- Minimum length: 8 characters
- Must contain:
- At least one letter (uppercase or lowercase)
- At least one number
- At least one symbol (e.g., !@#$%^&*)
- Maximum length: 128 characters
- Confirmation: Must match the password confirmation field
Passwords are hashed using bcrypt before storage. Plain-text passwords are never stored in the database.
Validation errors
If validation fails, the API returns a 422 status with detailed errors:
{
"message": "The given data was invalid.",
"errors": {
"email": ["The email has already been taken."],
"password": ["The password must contain at least one symbol."]
}
}
The frontend displays the first error to the user:
// AuthView.vue:54-59
if (err.errors) {
const firstErrorKey = Object.keys(err.errors)[0];
error.value = err.errors[firstErrorKey][0];
} else {
error.value = err.message || "Authentication failed";
}
User login
Existing users authenticate with their email and password.
Enter credentials
The user provides:
- Email: Their registered email address
- Password: Their account password
// AuthView.vue:13-16
const loginForm = reactive({
email: "",
password: "",
});
Verify credentials
The backend validates the credentials:// AuthController.php:45-67
public function login(Request $request)
{
$request->validate([
'email' => 'required|string|email',
'password' => 'required|string',
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
'token' => $token,
'token_type' => 'Bearer',
'user' => $user
]);
}
Receive token and access dashboard
On successful login:
- A new API token is generated (invalidating any previous tokens is optional)
- The token and user data are returned to the frontend
- The user is redirected to the dashboard
Invalid credentials
If the email doesn’t exist or the password is incorrect, a validation error is returned:
{
"message": "The given data was invalid.",
"errors": {
"email": ["The provided credentials are incorrect."]
}
}
The error message doesn’t reveal whether the email exists or the password was wrong. This prevents attackers from enumerating valid email addresses.
Session management
Token storage
The authentication token is stored in the browser’s localStorage:
// Frontend token storage (implied from Vue store pattern)
localStorage.setItem('auth_token', token);
Authenticated requests
All API requests to protected endpoints include the token in the Authorization header:
// Example request with Bearer token
fetch('/api/documents', {
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}
})
Token validation
Sanctum validates the token on every protected request:
// api.php:12-23 - Protected routes
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', function (Request $request) {
return $request->user();
});
Route::post('/logout', [AuthController::class, 'logout']);
Route::get('/documents', [DocumentController::class, 'index']);
Route::post('/upload', [DocumentController::class, 'upload']);
Route::delete('/documents/{id}', [DocumentController::class, 'destroy']);
Route::patch('/documents/{id}/rename', [DocumentController::class, 'rename']);
Route::post('/chat', [ChatController::class, 'chat']);
});
If the token is:
- Valid: The request proceeds and
$request->user() returns the authenticated user
- Invalid/Missing: A 401 Unauthorized response is returned
- Expired: Sanctum tokens don’t expire by default, but you can configure expiration if needed
Current user endpoint
The frontend can fetch the current user’s data:
// api.php:13-15
Route::get('/user', function (Request $request) {
return $request->user();
});
This is useful for:
- Verifying the token is still valid
- Refreshing user data
- Checking authentication status on page load
Logout
Users can log out to invalidate their current session token.
User clicks logout
The frontend sends a POST request to /api/logout with the current token.
Token revocation
The backend deletes the current access token from the database:// AuthController.php:69-76
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();
return response()->json([
'message' => 'Logged out successfully'
]);
}
Clear client-side data
The frontend:
- Removes the token from localStorage
- Clears user data from the store
- Redirects to the login page
Logout only revokes the current token. If the user has logged in from multiple devices, those tokens remain valid. To revoke all tokens, use $user->tokens()->delete().
Security features
Password hashing
Passwords are hashed using bcrypt (Laravel’s default):
// AuthController.php:33
'password' => Hash::make($request->password),
Bcrypt is:
- One-way: Impossible to reverse the hash to get the original password
- Salted: Each password gets a unique salt to prevent rainbow table attacks
- Adaptive: Computational cost can be increased as hardware improves
Token security
Sanctum tokens are:
- Random: Generated using cryptographically secure random bytes
- Hashed: Only the hash is stored in the database
- Revocable: Can be deleted to invalidate access
HTTPS enforcement
In production, always use HTTPS to protect tokens in transit. Sanctum doesn’t enforce this, but it’s critical for security.
Tokens sent over HTTP can be intercepted by attackers. Configure your web server to redirect all HTTP traffic to HTTPS in production.
CORS configuration
For SPA architectures where frontend and backend are on different domains, configure CORS properly:
// config/sanctum.php - Stateful domains (for cookie-based auth)
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1')),
Filebright uses token-based auth, so CORS configuration in config/cors.php should allow your frontend domain.
Rate limiting
Consider implementing rate limiting on authentication endpoints to prevent brute-force attacks:
// Example rate limiting (not in current codebase)
Route::post('/login', [AuthController::class, 'login'])
->middleware('throttle:5,1'); // 5 attempts per minute
Frontend authentication flow
Auth guard
The frontend uses route guards to protect authenticated pages:
// Implied router guard pattern
if (!authStore.isAuthenticated) {
router.push('/auth');
}
Auto-login on page load
When the app loads:
- Check if a token exists in localStorage
- If yes, attempt to fetch the current user
- If successful, consider the user authenticated
- If failed, clear the token and redirect to login
Token expiration handling
If a request returns 401 Unauthorized:
- Clear the stored token
- Redirect to login
- Optionally show a “Session expired” message
Best practices
- Use HTTPS in production to protect tokens from interception
- Implement token refresh for long-lived sessions (not currently implemented)
- Add rate limiting to prevent brute-force attacks
- Log authentication events for security auditing
- Validate email addresses by sending confirmation emails (optional enhancement)
- Implement password reset functionality (not currently implemented)
- Use strong password policies to enforce security
Potential enhancements
Filebright’s authentication system could be extended with:
- Email verification: Require users to verify their email before accessing the app
- Password reset: Allow users to reset forgotten passwords via email
- Two-factor authentication: Add an extra security layer with TOTP codes
- Token expiration: Auto-expire tokens after a period of inactivity
- Remember me: Issue longer-lived tokens for trusted devices
- OAuth integration: Allow login via Google, GitHub, etc.
- Account deletion: Let users permanently delete their accounts and data
The current implementation provides a solid foundation for a secure authentication system. The enhancements above can be added as the product matures.