Kotchasan Framework Documentation
Authentication and Security Management
Authentication and Security Management
Kotchasan Framework provides a comprehensive authentication and security management system that supports multiple authentication methods and protects against various security threats.
Table of Contents
- Login Class - Login Management
- Password Class - Password and Encryption Management
- Session Class - Session Management
- JWT Middleware - JWT Authentication
- AuthFactory - Authentication Factory
- CSRF Protection
- Real-world Usage Examples
Login Class - Login Management
Using Login Class
use Kotchasan\Login;
use Kotchasan\Http\Request;
// Create Login instance
$request = new Request();
$login = Login::create($request);
// Check login status
$user = Login::isMember();
if ($user) {
echo "User: " . $user['username'];
} else {
echo "Not logged in";
}
// Check Admin privileges
$admin = Login::isAdmin();
if ($admin) {
echo "Has Admin privileges";
}Setting up Login System
class CustomLogin extends Login
{
/**
* Check login credentials against database
*/
public function checkLogin($loginParams)
{
// Check username
$user = $this->db()
->select()
->from('users')
->where(['username', $loginParams['username']])
->first();
if (!$user) {
self::$login_input = 'username';
return 'User not found';
}
// Check password
if (!password_verify($loginParams['password'], $user->password)) {
self::$login_input = 'password';
return 'Invalid password';
}
// Check user status
if ($user->status == 0) {
return 'Account is suspended';
}
// Update last login information
$this->db()->update('users')
->set([
'last_login' => date('Y-m-d H:i:s'),
'login_ip' => $_SERVER['REMOTE_ADDR']
])
->where(['id', $user->id])
->execute();
// Return user information
return [
'id' => $user->id,
'username' => $user->username,
'email' => $user->email,
'name' => $user->name,
'status' => $user->status,
'permissions' => explode(',', $user->permissions)
];
}
/**
* Logout functionality
*/
public function logout(Request $request)
{
// Clear session data
$key = static::sessionKey();
unset($_SESSION[$key]);
// Clear remember me cookie if exists
if (isset($_COOKIE['remember_me'])) {
setcookie('remember_me', '', time() - 3600, '/');
}
// Log logout activity
$this->logActivity('logout');
self::$login_message = 'Logout successful';
self::$login_params = [];
}
/**
* Password reset functionality
*/
public function forgot(Request $request)
{
$email = $request->post('email')->email();
if (empty($email)) {
self::$login_message = 'Please enter email address';
return;
}
// Check email in system
$user = $this->db()
->select()
->from('users')
->where(['email', $email])
->first();
if (!$user) {
self::$login_message = 'Email not found in system';
return;
}
// Generate reset token
$resetToken = Password::uniqid(32);
$expiry = date('Y-m-d H:i:s', strtotime('+1 hour'));
// Save reset token
$this->db()->update('users')
->set([
'reset_token' => $resetToken,
'reset_expires' => $expiry
])
->where(['id', $user->id])
->execute();
// Send reset email
$this->sendResetEmail($user, $resetToken);
self::$login_message = 'Password reset link sent to your email';
}
}Permission Checking
// Check specific permissions
$user = Login::isMember();
$hasPermission = Login::checkStatus($user, [1, 2, 3]); // Allowed statuses
// Usage in Controller
class AdminController
{
public function index()
{
// Check Admin privileges
$admin = Login::isAdmin();
if (!$admin) {
// Redirect to login page
header('Location: /login');
exit;
}
// Show Admin page
return $this->view->render('admin/dashboard', [
'user' => $admin
]);
}
public function users()
{
// Check specific permissions
$user = Login::isMember();
if (!Login::checkStatus($user, [1, 2])) { // Admin and Manager
throw new \Exception('Access denied');
}
// Show user list
return $this->userService->getUsers();
}
}Password Class - Password and Encryption Management
Data Encryption and Decryption
use Kotchasan\Password;
// Data encryption
$data = "secret data";
$secretKey = "my-secret-key";
$encrypted = Password::encode($data, $secretKey);
// Data decryption
try {
$decrypted = Password::decode($encrypted, $secretKey);
echo $decrypted; // "secret data"
} catch (Exception $e) {
echo "Decryption failed: " . $e->getMessage();
}
// Password hashing
$password = "user-password123";
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// Password verification
if (password_verify($password, $hashedPassword)) {
echo "Password is correct";
}Token Generation and API Signature
// Generate unique ID
$shortId = Password::uniqid(8); // 8 characters
$longId = Password::uniqid(32); // 32 characters
$defaultId = Password::uniqid(); // 13 characters (default)
// Generate API signature
$apiParams = [
'user_id' => 123,
'action' => 'get_data',
'timestamp' => time()
];
$apiSecret = 'api-secret-key';
$signature = Password::generateSign($apiParams, $apiSecret);
// Validate API signature
function validateApiSignature($params, $receivedSignature, $secret)
{
$expectedSignature = Password::generateSign($params, $secret);
return hash_equals($expectedSignature, $receivedSignature);
}Real-world Usage
class UserService
{
/**
* Create new user account
*/
public function createUser($userData)
{
// Hash password
$userData['password'] = password_hash($userData['password'], PASSWORD_DEFAULT);
// Generate activation token
$userData['activation_token'] = Password::uniqid(32);
$userData['created_at'] = date('Y-m-d H:i:s');
// Save new user
$userId = $this->db()->insert('users')->values($userData)->execute();
// Send activation email
$this->sendActivationEmail($userData);
return $userId;
}
/**
* Change password
*/
public function changePassword($userId, $oldPassword, $newPassword)
{
// Get user data
$user = $this->db()->select()->from('users')->where(['id', $userId])->first();
// Verify old password
if (!password_verify($oldPassword, $user->password)) {
throw new \Exception('Old password is incorrect');
}
// Check new password strength
if (strlen($newPassword) < 8) {
throw new \Exception('Password must be at least 8 characters');
}
// Hash new password
$hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
// Update password
$this->db()->update('users')
->set(['password' => $hashedPassword, 'password_changed_at' => date('Y-m-d H:i:s')])
->where(['id', $userId])
->execute();
return true;
}
}Session Class - Session Management
Database-based Session Setup
use Kotchasan\Session;
// Use database-based sessions
$session = new Session();
// Create session table
$session->createTable();
// Set session handler
session_set_save_handler(
[$session, '_open'],
[$session, '_close'],
[$session, '_read'],
[$session, '_write'],
[$session, '_destroy'],
[$session, '_gc']
);
// Start session
session_start();Session Usage
// Store data in session
$session = new Session();
$session->setSession('user_id', 123);
$session->setSession('username', 'john_doe');
$session->setSession('cart', ['item1', 'item2']);
// Retrieve data from session
$userId = $session->session('user_id');
$username = $session->session('username', 'anonymous');
$cart = $session->session('cart', []);
// Standard usage
$_SESSION['custom_data'] = 'some value';
$customData = $_SESSION['custom_data'] ?? 'default';Secure Session Configuration
// Secure session settings
ini_set('session.use_only_cookies', 1);
ini_set('session.use_strict_mode', 1);
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1); // HTTPS only
ini_set('session.cookie_samesite', 'Lax');
// Session cookie parameters
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => true, // HTTPS only
'httponly' => true, // XSS protection
'samesite' => 'Lax' // CSRF protection
]);
// Regenerate session ID
function regenerateSessionId()
{
if (session_status() === PHP_SESSION_ACTIVE) {
session_regenerate_id(true);
}
}
// Check session expiration
$maxLifetime = 360; // 0)
{
if (isset($_SESSION['last_activity'])) {
if (time() - $_SESSION['last_activity'] > $maxLifetime) {
session_destroy();
return false;
}
}
$_SESSION['last_activity'] = time();
return true;
}JWT Middleware - JWT Authentication
Using JWT Middleware
use Kotchasan\Http\Middleware\JwtMiddleware;
// Create JWT Middleware
$secretKey = 'your-secret-key-here';
$jwtMiddleware = new JwtMiddleware($secretKey);
// Set options
$options = [
'algorithm' => 'HS256',
'tokenParam' => 'token', // Accept token from query parameter
'required' => true // Token is required
];
$jwtMiddleware = new JwtMiddleware($secretKey, $options);
// Usage in Controller
class ApiController
{
public function getData(Request $request)
{
$jwt = new JwtMiddleware('secret-key');
$response = $jwt->handle($request);
if ($response instanceof Response) {
// Invalid token
return $response;
}
// Get user data from token
$userId = $request->getAttribute('authenticated_user');
$payload = $request->getAttribute('jwt_payload');
return [
'user_id' => $userId,
'data' => $this->getUserData($userId)
];
}
}Creating and Validating JWT Tokens
// Create JWT Token
$jwt = new JwtMiddleware('secret-key');
// Create token with user ID
$token = $jwt->generateToken(123, 3600); // user ID = 123, expires in 1 hour
// Create token with custom payload
$payload = [
'user_id' => 123,
'username' => 'john_doe',
'role' => 'admin',
'permissions' => ['read', 'write', 'delete']
];
$token = $jwt->generateToken($payload, 7200); // expires in 2 hours
// Decode JWT Token
try {
$decoded = $jwt->decodeToken($token);
echo "User ID: " . $decoded['sub'];
echo "Expires: " . date('Y-m-d H:i:s', $decoded['exp']);
} catch (Exception $e) {
echo "Invalid token: " . $e->getMessage();
}Using JWT in API System
class AuthApiController
{
/**
* Login and create JWT token
*/
public function login(Request $request)
{
$username = $request->post('username')->username();
$password = $request->post('password')->password();
// Validate login credentials
$user = $this->validateLogin($username, $password);
if (!$user) {
return Response::makeUnauthorized(['error' => 'Invalid credentials']);
}
// Create JWT token
$jwt = new JwtMiddleware(self::$cfg->jwt_secret);
$payload = [
'sub' => $user['id'],
'username' => $user['username'],
'role' => $user['role'],
'iat' => time(),
'exp' => time() + 3600 // expires in 1 hour
];
$accessToken = $jwt->generateToken($payload);
$refreshToken = Password::uniqid(40);
// Save refresh token
$this->saveRefreshToken($user['id'], $refreshToken);
return Response::makeOk([
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'token_type' => 'Bearer',
'expires_in' => 3600
]);
}
/**
* Refresh JWT token
*/
public function refreshToken(Request $request)
{
$refreshToken = $request->post('refresh_token')->toString();
// Validate refresh token
$user = $this->validateRefreshToken($refreshToken);
if (!$user) {
return Response::makeUnauthorized(['error' => 'Invalid refresh token']);
}
// Create new access token
$jwt = new JwtMiddleware(self::$cfg->jwt_secret);
$newAccessToken = $jwt->generateToken([
'sub' => $user['id'],
'username' => $user['username'],
'role' => $user['role']
], 3600);
// Create new refresh token
$newRefreshToken = Password::uniqid(40);
$this->updateRefreshToken($user['id'], $newRefreshToken);
return Response::makeOk([
'access_token' => $newAccessToken,
'refresh_token' => $newRefreshToken,
'token_type' => 'Bearer',
'expires_in' => 3600
]);
}
}AuthFactory - Authentication Factory
Using AuthFactory
use Kotchasan\Http\Auth\AuthFactory;
// Password management
$password = 'user-password123';
$hashedPassword = AuthFactory::hashPassword($password);
$isValid = AuthFactory::verifyPassword($password, $hashedPassword);
// Generate random token
$token = AuthFactory::generateToken(32); // 32 characters
$shortToken = AuthFactory::generateToken(16); // 16 characters
// Create and validate JWT token
$payload = ['user_id' => 123, 'role' => 'admin'];
$secretKey = 'jwt-secret-key';
$jwtToken = AuthFactory::createJwtToken($payload, $secretKey);
$decodedPayload = AuthFactory::validateJwtToken($jwtToken, $secretKey);Creating Authentication Middleware
// Basic Authentication
$basicAuth = AuthFactory::createBasicAuth(function($username, $password) {
// Validate username and password
return $this->validateUser($username, $password);
}, 'Admin Area');
// JWT Authentication
$jwtAuth = AuthFactory::createJwtAuth('secret-key', 'HS256');
// Bearer Token Authentication
$bearerAuth = AuthFactory::createBearerTokenAuth(function($token) {
// Validate bearer token
return $this->validateBearerToken($token);
});
// Digest Authentication
$digestAuth = AuthFactory::createDigestAuth(function($username) {
// Return password hash for username
return $this->getUserPasswordHash($username);
}, 'Secure Area');
// Authorization (Role-based)
$authorization = AuthFactory::createAuthorization(['admin', 'manager']);Secure Hash Comparison
// Secure hash comparison preventing timing attacks
$hash1 = hash('sha256', 'data1');
$hash2 = hash('sha256', 'data2');
// Insecure way
if ($hash1 === $hash2) {
// Vulnerable to timing attacks
}
// Secure way
if (AuthFactory::compareHashes($hash1, $hash2)) {
// Protected against timing attacks
}CSRF Protection
Using CSRF Protection
// Generate CSRF token
$csrfToken = AuthFactory::generateCsrfToken();
$customToken = AuthFactory::generateCsrfToken('custom_csrf_key');
// Validate CSRF token
$isValid = AuthFactory::validateCsrfToken($_POST['csrf_token']);
$customIsValid = AuthFactory::validateCsrfToken($_POST['custom_token'], 'custom_csrf_key');
// Use in forms
echo '<input type="hidden" name="csrf_token" value="' . htmlspecialchars($csrfToken) . '">';CSRF Protection in Controllers
class FormController
{
public function showForm()
{
// Generate CSRF token for form
$csrfToken = AuthFactory::generateCsrfToken();
return $this->view->render('form', [
'csrf_token' => $csrfToken
]);
}
public function processForm(Request $request)
{
// Validate CSRF token
$token = $request->post('csrf_token')->toString();
if (!AuthFactory::validateCsrfToken($token)) {
return Response::makeForbidden(['error' => 'Invalid CSRF token']);
}
// Process form
$data = [
'name' => $request->post('name')->toString(),
'email' => $request->post('email')->email()
];
$result = $this->processData($data);
return Response::makeOk(['message' => 'Form processed successfully']);
}
}CSRF Protection for APIs
class ApiCsrfController
{
/**
* Get CSRF token for SPA/AJAX
*/
public function getCsrfToken(Request $request)
{
$token = AuthFactory::generateCsrfToken('api_csrf_token');
return Response::makeOk([
'csrf_token' => $token,
'expires_in' => 3600
]);
}
/*
* Validate CSRF token in API calls
*/
public function validateApiCsrf(Request $request)
{
$token = $request->getHeaderLine('X-CSRF-Token')
?: $request->post('csrf_token')->toString();
if (!AuthFactory::validateCsrfToken($token, 'api_csrf_token')) {
return Response::makeForbidden([
'error' => 'CSRF token validation failed',
'code' => 'INVALID_CSRF_TOKEN'
]);
}
return null; // Validation passed
}
}Real-world Usage Examples
Comprehensive Authentication System
use Kotchasan\Database;
class AuthenticationSystem
{
private $db;
private $jwtSecret;
public function __construct()
{
$this->db = Kotchasan\Database::create();
$this->jwtSecret = Config::get('jwt_secret');
}
/**
* Comprehensive login system
*/
public function login(Request $request)
{
try {
// Validate CSRF token
$csrfToken = $request->post('csrf_token')->toString();
if (!AuthFactory::validateCsrfToken($csrfToken)) {
return $this->errorResponse('Invalid CSRF token', 403);
}
// Check rate limiting
$clientIp = $request->server('REMOTE_ADDR');
if (!$this->checkRateLimit($clientIp, 'login', 5, 300)) {
return $this->errorResponse('Too many login attempts', 429);
}
$username = $request->post('username')->username();
$password = $request->post('password')->password();
$rememberMe = $request->post('remember_me')->toBool();
// Validate credentials
$user = $this->validateCredentials($username, $password);
if (!$user) {
$this->logFailedLogin($username, $clientIp);
return $this->errorResponse('Invalid credentials', 401);
}
// Check account status
if ($user['status'] !== 'active') {
return $this->errorResponse('Account is not active', 403);
}
// Create session
session_regenerate_id(true);
$_SESSION['user'] = [
'id' => $user['id'],
'username' => $user['username'],
'role' => $user['role'],
'login_time' => time()
];
// Create JWT token
$jwtToken = null;
if ($request->post('api_access')->toBool()) {
$jwt = new JwtMiddleware($this->jwtSecret);
$jwtToken = $jwt->generateToken([
'sub' => $user['id'],
'username' => $user['username'],
'role' => $user['role']
], 3600);
}
// Handle Remember Me
if ($rememberMe) {
$this->setRememberMeCookie($user['id']);
}
// Log successful login
$this->logSuccessfulLogin($user['id'], $clientIp);
return $this->successResponse([
'message' => 'Login successful',
'user' => [
'id' => $user['id'],
'username' => $user['username'],
'role' => $user['role']
],
'jwt_token' => $jwtToken,
'csrf_token' => AuthFactory::generateCsrfToken()
]);
} catch (Exception $e) {
return $this->errorResponse('Login failed: ' . $e->getMessage(), 500);
}
}
/**
* Validate credentials
*/
private function validateCredentials($username, $password)
{
// Find user
$user = $this->db->select()
->from('users')
->where(['username', $username])
->orWhere(['email', $username])
->first();
if (!$user) {
return false;
}
// Verify password
if (!AuthFactory::verifyPassword($password, $user->password)) {
return false;
}
return $user->toArray();
}
/**
* Handle Remember Me Cookie
*/
private function setRememberMeCookie($userId)
{
$token = AuthFactory::generateToken(32);
$expires = time() + (30 24 60 60); // 30 days
// Save remember token in database
$this->db->insert('remember_tokens')
->values([
'user_id' => $userId,
'token' => hash('sha256', $token),
'expires_at' => date('Y-m-d H:i:s', $expires),
'created_at' => date('Y-m-d H:i:s')
])
->execute();
// Set cookie
setcookie('remember_me', $token, $expires, '/', '', true, true);
}
/**
* Check Rate Limiting
*/
private function checkRateLimit($identifier, $action, $limit, $window)
{
$key = "rate_limit:{$action}:{$identifier}";
$attempts = $_SESSION[$key] ?? [];
// Remove old attempts
$cutoff = time() - $window;
$attempts = array_filter($attempts, function($time) use ($cutoff) {
return $time > $cutoff;
});
// Check limit
if (count($attempts) >= $limit) {
return false;
}
// Record new attempt
$attempts[] = time();
$_SESSION[$key] = $attempts;
return true;
}
/**
* Log successful login
*/
private function logSuccessfulLogin($userId, $ip)
{
$this->db->insert('login_logs')
->values([
'user_id' => $userId,
'ip_address' => $ip,
'status' => 'success',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'created_at' => date('Y-m-d H:i:s')
])
->execute();
}
/**
* Log failed login
*/
private function logFailedLogin($username, $ip)
{
$this->db->insert('login_logs')
->values([
'username' => $username,
'ip_address' => $ip,
'status' => 'failed',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'created_at' => date('Y-m-d H:i:s')
])
->execute();
}
}Multi-layered Security System
class SecuritySystem
{
/**
* Comprehensive security middleware
*/
public function securityMiddleware(Request $request, callable $next)
{
// 1. Validate IP access (whitelist/blacklist)
if (!$this->validateIpAccess($request->server('REMOTE_ADDR'))) {
return Response::makeForbidden(['error' => 'IP not allowed']);
}
// 2. Validate User Agent
if (!$this->validateUserAgent($request->server('HTTP_USER_AGENT'))) {
return Response::makeForbidden(['error' => 'Invalid user agent']);
}
// 3. Validate Referer for state-changing requests
if (in_array($request->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
if (!$this->validateReferer($request)) {
return Response::makeForbidden(['error' => 'Invalid referer']);
}
}
// 4. Validate CSRF token for state-changing requests
if ($request->isMethod('POST')) {
$csrfToken = $request->getHeaderLine('X-CSRF-Token')
?: $request->post('csrf_token')->toString();
if (!AuthFactory::validateCsrfToken($csrfToken)) {
return Response::makeForbidden(['error' => 'CSRF validation failed']);
}
}
// 5. Validate JWT token for API requests
if (strpos($request->getHeaderLine('Accept'), 'application/json') !== false) {
$jwt = new JwtMiddleware(Config::get('jwt_secret'));
$response = $jwt->handle($request);
if ($response instanceof Response) {
return $response;
}
}
// 6. Check permissions
$user = $request->getAttribute('authenticated_user');
if (!$this->hasPermission($user, $request)) {
return Response::makeForbidden(['error' => 'Insufficient permissions']);
}
// 7. Rate limiting
if (!$this->checkGlobalRateLimit($request)) {
return Response::make(['error' => 'Rate limit exceeded'], 429);
}
// All checks passed
return $next($request);
}
/**
* Check access permissions
*/
private function hasPermission($userId, Request $request)
{
if (!$userId) {
return false; // Must be logged in
}
$route = $request->getAttribute('route');
$action = $request->getMethod() . ' ' . $route;
// Check permissions from database
$hasPermission = $this->db->select('COUNT()')
->from('user_permissions')
->where(['user_id', $userId])
->where(['permission', $action])
->where(['status', 'active'])
->value();
return $hasPermission > 0;
}
/**
* Validate IP access
*/
private function validateIpAccess($clientIp)
{
// Check IP blacklist
$blacklisted = $this->db->select('COUNT()')
->from('ip_blacklist')
->where(['ip_address', $clientIp])
->where(['status', 'active'])
->value();
if ($blacklisted > 0) {
return false;
}
// Check IP whitelist (if any)
$whitelistCount = $this->db->select('COUNT()')
->from('ip_whitelist')
->where(['status', 'active'])
->value();
if ($whitelistCount > 0) {
$whitelisted = $this->db->select('COUNT()')
->from('ip_whitelist')
->where(['ip_address', $clientIp])
->where(['status', 'active'])
->value();
return $whitelisted > 0;
}
return true;
}
/**
* Brute Force Attack Prevention
*/
public function bruteForcePrevention($identifier, $action, $maxAttempts = 5, $blockTime = 300)
{
$key = "brute_force:{$action}:{$identifier}";
$data = $_SESSION[$key] ?? ['attempts' => 0, 'last_attempt' => 0];
// Check if blocked
if ($data['attempts'] >= $maxAttempts) {
$timeSinceBlock = time() - $data['last_attempt'];
if ($timeSinceBlock < $blockTime) {
$remainingTime = $blockTime - $timeSinceBlock;
throw new \Exception("Account blocked. Try again in {$remainingTime} seconds.");
} else {
// Block time expired, reset counter
$data = ['attempts' => 0, 'last_attempt' => 0];
}
}
// Increment attempt counter
$data['attempts']++;
$data['last_attempt'] = time();
$_SESSION[$key] = $data;
return $data['attempts'];
}
}Kotchasan Framework provides a comprehensive authentication and security management system that supports the development of secure and high-performance applications with protection against various security threats.