Security API

Comprehensive security features including authentication, authorization, encryption, and security best practices for Forge Engine applications.

Security Overview

Security is a critical aspect of any web application. Forge Engine provides a comprehensive security layer that includes authentication, authorization, encryption, CSRF protection, input sanitization, and more. This guide covers all security-related APIs and best practices.

Authentication

Secure user authentication with multiple providers, session management, and token-based authentication.

Authorization

Role-based access control (RBAC), permissions, policies, and resource-level authorization.

Encryption

Data encryption, hashing, secure key management, and cryptographic operations.

Protection

CSRF protection, XSS prevention, SQL injection prevention, and security headers.

Authentication

Auth Manager

Central authentication management with multiple providers and strategies.

use Forge\Security\AuthManager;
use Forge\Security\Providers\DatabaseAuthProvider;
use Forge\Security\Providers\TokenAuthProvider;

$authManager = new AuthManager();

/**
 * Register authentication providers
 */
$authManager->registerProvider('database', new DatabaseAuthProvider());
$authManager->registerProvider('token', new TokenAuthProvider());

/**
 * Authenticate user with credentials
 */
$user = $authManager->attempt([
    'email' => 'user@example.com',
    'password' => 'password123'
], 'database');

if ($user) {
    // Authentication successful
    echo "Welcome, {$user->name}!";
} else {
    // Authentication failed
    echo "Invalid credentials";
}

/**
 * Authenticate with token
 */
$user = $authManager->attempt([
    'token' => $request->bearerToken()
], 'token');

/**
 * Check if user is authenticated
 */
if ($authManager->check()) {
    $currentUser = $authManager->user();
}

/**
 * Get current user
 */
$user = $authManager->user();

/**
 * Logout user
 */
$authManager->logout();

/**
 * Get authentication guard
 */
$guard = $authManager->guard('web');
$guard = $authManager->guard('api');

Session-based Authentication

Traditional session-based authentication with secure session management.

use Forge\Security\SessionManager;
use Forge\Security\Providers\SessionAuthProvider;

$sessionManager = new SessionManager();
$sessionAuth = new SessionAuthProvider($sessionManager);

/**
 * Start secure session
 */
$sessionManager->start([
    'name' => 'forge_session',
    'lifetime' => 7200, // 2 hours
    'path' => '/',
    'domain' => '.example.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'strict'
]);

/**
 * Login user and create session
 */
public function login(Request $request): Response
{
    $credentials = $request->validate([
        'email' => 'required|email',
        'password' => 'required|string'
    ]);
    
    $user = $this->auth->attempt($credentials);
    
    if ($user) {
        // Regenerate session ID for security
        $this->session->regenerate();
        
        // Store user data in session
        $this->session->put('user_id', $user->id);
        $this->session->put('user_data', [
            'id' => $user->id,
            'email' => $user->email,
            'name' => $user->name,
            'roles' => $user->roles->pluck('name')->toArray()
        ]);
        
        // Set additional security flags
        $this->session->put('ip_address', $request->ip());
        $this->session->put('user_agent', $request->userAgent());
        $this->session->put('login_time', time());
        
        return redirect('/dashboard')->with('success', 'Login successful');
    }
    
    return back()->withErrors(['email' => 'Invalid credentials']);
}

/**
 * Remember me functionality
 */
public function loginWithRemember(Request $request): Response
{
    $credentials = $request->validate([
        'email' => 'required|email',
        'password' => 'required|string',
        'remember' => 'boolean'
    ]);
    
    $remember = $request->boolean('remember', false);
    
    if ($this->auth->attempt($credentials, $remember)) {
        if ($remember) {
            // Create remember token
            $token = $this->createRememberToken($request->user());
            
            // Set secure cookie
            $this->cookie->queue('remember_token', $token, 60 * 24 * 30); // 30 days
        }
        
        return redirect('/dashboard');
    }
    
    return back()->withErrors(['email' => 'Invalid credentials']);
}

/**
 * Session security checks
 */
public function validateSession(Request $request): bool
{
    $sessionData = $this->session->get('user_data');
    
    if (!$sessionData) {
        return false;
    }
    
    // Check IP address (optional - can be problematic with mobile users)
    if ($this->session->get('ip_address') !== $request->ip()) {
        Log::warning('Session IP mismatch', [
            'expected' => $this->session->get('ip_address'),
            'actual' => $request->ip()
        ]);
        
        // Optionally invalidate session
        // return false;
    }
    
    // Check user agent
    if ($this->session->get('user_agent') !== $request->userAgent()) {
        Log::warning('Session user agent mismatch');
        return false;
    }
    
    // Check session timeout
    $loginTime = $this->session->get('login_time', 0);
    $timeout = config('session.lifetime', 7200);
    
    if (time() - $loginTime > $timeout) {
        $this->session->invalidate();
        return false;
    }
    
    return true;
}

Token-based Authentication

JWT and API token authentication for stateless applications.

use Forge\Security\TokenManager;
use Forge\Security\JWTManager;
use Firebase\JWT\JWT;

$tokenManager = new TokenManager();
$jwtManager = new JWTManager();

/**
 * Generate API token
 */
public function createApiToken(User $user): string
{
    $token = bin2hex(random_bytes(32));
    
    // Store token in database
    ApiToken::create([
        'user_id' => $user->id,
        'token' => hash('sha256', $token),
        'name' => 'API Token',
        'abilities' => ['read', 'write'],
        'expires_at' => now()->addDays(30)
    ]);
    
    return $token;
}

/**
 * Generate JWT token
 */
public function createJwtToken(User $user): string
{
    $payload = [
        'iss' => 'forge-engine', // Issuer
        'sub' => $user->id,      // Subject
        'aud' => 'api-users',    // Audience
        'iat' => time(),         // Issued at
        'exp' => time() + 3600,  // Expiration (1 hour)
        'nbf' => time(),         // Not before
        'jti' => uniqid(),       // JWT ID
        'data' => [
            'user_id' => $user->id,
            'email' => $user->email,
            'roles' => $user->roles->pluck('name')->toArray()
        ]
    ];
    
    return JWT::encode($payload, config('app.jwt_secret'), 'HS256');
}

/**
 * Refresh JWT token
 */
public function refreshJwtToken(string $refreshToken): ?string
{
    try {
        // Validate refresh token
        $refreshData = JWT::decode($refreshToken, config('app.jwt_refresh_secret'), ['HS256']);
        
        // Get user
        $user = User::find($refreshData->sub);
        if (!$user) {
            return null;
        }
        
        // Generate new access token
        return $this->createJwtToken($user);
        
    } catch (\Exception $e) {
        Log::error('JWT refresh failed', ['error' => $e->getMessage()]);
        return null;
    }
}

/**
 * Validate API token
 */
public function validateApiToken(string $token): ?User
{
    $hashedToken = hash('sha256', $token);
    
    $apiToken = ApiToken::where('token', $hashedToken)
        ->where('expires_at', '>', now())
        ->first();
    
    if ($apiToken) {
        // Update last used timestamp
        $apiToken->update(['last_used_at' => now()]);
        
        return $apiToken->user;
    }
    
    return null;
}

/**
 * Validate JWT token
 */
public function validateJwtToken(string $token): ?array
{
    try {
        $decoded = JWT::decode($token, config('app.jwt_secret'), ['HS256']);
        
        // Check expiration
        if ($decoded->exp < time()) {
            return null;
        }
        
        // Check if token is blacklisted
        if (Cache::has("blacklist:{$decoded->jti}")) {
            return null;
        }
        
        return (array) $decoded;
        
    } catch (\Exception $e) {
        Log::error('JWT validation failed', ['error' => $e->getMessage()]);
        return null;
    }
}

/**
 * Revoke token
 */
public function revokeToken(string $token): void
{
    // For JWT, add to blacklist
    try {
        $decoded = JWT::decode($token, config('app.jwt_secret'), ['HS256']);
        $ttl = $decoded->exp - time();
        
        if ($ttl > 0) {
            Cache::put("blacklist:{$decoded->jti}", true, $ttl);
        }
    } catch (\Exception $e) {
        // Token is already invalid
    }
    
    // For API tokens, delete from database
    $hashedToken = hash('sha256', $token);
    ApiToken::where('token', $hashedToken)->delete();
}

Authorization

Permission-based Authorization

Fine-grained permission system for resource access control.

use Forge\Security\Authorization\PermissionManager;
use Forge\Security\Authorization\Permission;

$permissionManager = new PermissionManager();

/**
 * Define permissions
 */
$permissions = [
    new Permission('users.create', 'Create new users'),
    new Permission('users.read', 'View user information'),
    new Permission('users.update', 'Update user information'),
    new Permission('users.delete', 'Delete users'),
    new Permission('posts.create', 'Create new posts'),
    new Permission('posts.update', 'Update posts'),
    new Permission('posts.delete', 'Delete posts'),
    new Permission('posts.publish', 'Publish posts'),
    new Permission('admin.access', 'Access admin panel'),
    new Permission('settings.manage', 'Manage application settings')
];

foreach ($permissions as $permission) {
    $permissionManager->registerPermission($permission);
}

/**
 * Check user permissions
 */
public function createUser(Request $request): Response
{
    // Check if user has permission
    if (!$this->auth->user()->can('users.create')) {
        return response()->json(['error' => 'Insufficient permissions'], 403);
    }
    
    // Alternative syntax
    if ($this->auth->user()->cannot('users.create')) {
        return response()->json(['error' => 'Insufficient permissions'], 403);
    }
    
    // Check multiple permissions
    if (!$this->auth->user()->hasAnyPermission(['users.create', 'admin.access'])) {
        return response()->json(['error' => 'Insufficient permissions'], 403);
    }
    
    // Check all permissions
    if (!$this->auth->user()->hasAllPermissions(['users.create', 'users.read'])) {
        return response()->json(['error' => 'Insufficient permissions'], 403);
    }
    
    // Create user
    $user = User::create($request->validated());
    
    return response()->json($user, 201);
}

/**
 * Resource-specific permissions
 */
public function updatePost(Request $request, Post $post): Response
{
    $user = $this->auth->user();
    
    // Check if user owns the post
    if ($post->user_id === $user->id) {
        // User can update their own posts
        $post->update($request->validated());
        return response()->json($post);
    }
    
    // Check if user has global update permission
    if ($user->can('posts.update')) {
        $post->update($request->validated());
        return response()->json($post);
    }
    
    return response()->json(['error' => 'Unauthorized'], 403);
}

/**
 * Permission inheritance
 */
class PermissionManager
{
    private array $permissionHierarchy = [
        'admin' => [
            'users.*',
            'posts.*',
            'settings.*'
        ],
        'editor' => [
            'posts.create',
            'posts.update',
            'posts.publish'
        ],
        'author' => [
            'posts.create',
            'posts.update.own'
        ]
    ];
    
    public function hasPermission(User $user, string $permission): bool
    {
        // Check direct permission
        if ($user->permissions->contains('name', $permission)) {
            return true;
        }
        
        // Check inherited permissions
        foreach ($user->roles as $role) {
            if ($this->roleHasPermission($role, $permission)) {
                return true;
            }
        }
        
        return false;
    }
    
    private function roleHasPermission(Role $role, string $permission): bool
    {
        $rolePermissions = $this->permissionHierarchy[$role->name] ?? [];
        
        foreach ($rolePermissions as $rolePermission) {
            if ($this->matchesPermission($rolePermission, $permission)) {
                return true;
            }
        }
        
        return false;
    }
    
    private function matchesPermission(string $rolePermission, string $permission): bool
    {
        // Support wildcards
        $pattern = str_replace('*', '.*', $rolePermission);
        return preg_match("/^$pattern$/", $permission) === 1;
    }
}

Role-based Authorization

Role-based access control (RBAC) for simplified permission management.

use Forge\Security\Authorization\RoleManager;
use Forge\Security\Authorization\Role;

$roleManager = new RoleManager();

/**
 * Define roles
 */
$roles = [
    new Role('super_admin', 'Super Administrator', 'Full system access'),
    new Role('admin', 'Administrator', 'Administrative access'),
    new Role('editor', 'Editor', 'Content management access'),
    new Role('author', 'Author', 'Content creation access'),
    new Role('user', 'User', 'Standard user access'),
    new Role('guest', 'Guest', 'Limited read-only access')
];

foreach ($roles as $role) {
    $roleManager->registerRole($role);
}

/**
 * Assign roles to users
 */
public function assignRole(User $user, string $roleName): void
{
    $role = Role::where('name', $roleName)->first();
    
    if ($role) {
        $user->roles()->attach($role);
        
        // Log role assignment
        Log::info('Role assigned', [
            'user_id' => $user->id,
            'role' => $roleName,
            'assigned_by' => auth()->id()
        ]);
    }
}

/**
 * Check user roles
 */
public function adminDashboard(): Response
{
    // Check single role
    if (!$this->auth->user()->hasRole('admin')) {
        return redirect('/')->with('error', 'Access denied');
    }
    
    // Check multiple roles
    if (!$this->auth->user()->hasAnyRole(['admin', 'super_admin'])) {
        return redirect('/')->with('error', 'Access denied');
    }
    
    // Check all roles
    if (!$this->auth->user()->hasAllRoles(['admin', 'editor'])) {
        return redirect('/')->with('error', 'Insufficient privileges');
    }
    
    return view('admin.dashboard');
}

/**
 * Role-based middleware
 */
class RoleMiddleware
{
    public function handle(Request $request, Closure $next, string $role): mixed
    {
        if (!$request->user() || !$request->user()->hasRole($role)) {
            return response()->json(['error' => 'Insufficient privileges'], 403);
        }
        
        return $next($request);
    }
}

// Usage in routes
Route::middleware(['auth', 'role:admin'])->group(function () {
    Route::get('/admin', 'AdminController@index');
    Route::get('/admin/users', 'AdminController@users');
});

Route::middleware(['auth', 'role:editor'])->group(function () {
    Route::get('/content', 'ContentController@index');
    Route::post('/content', 'ContentController@store');
});

/**
 * Dynamic role permissions
 */
class DynamicRoleManager
{
    private array $rolePermissions = [
        'super_admin' => [
            'users' => ['create', 'read', 'update', 'delete'],
            'posts' => ['create', 'read', 'update', 'delete', 'publish'],
            'settings' => ['read', 'update'],
            'reports' => ['read']
        ],
        'admin' => [
            'users' => ['create', 'read', 'update'],
            'posts' => ['create', 'read', 'update', 'publish'],
            'reports' => ['read']
        ],
        'editor' => [
            'posts' => ['create', 'read', 'update', 'publish'],
            'users' => ['read']
        ],
        'author' => [
            'posts' => ['create', 'read', 'update.own']
        ]
    ];
    
    public function can(User $user, string $resource, string $action, ?Model $resourceInstance = null): bool
    {
        foreach ($user->roles as $role) {
            if ($this->roleCan($role, $resource, $action, $resourceInstance)) {
                return true;
            }
        }
        
        return false;
    }
    
    private function roleCan(Role $role, string $resource, string $action, ?Model $resourceInstance = null): bool
    {
        $permissions = $this->rolePermissions[$role->name] ?? [];
        
        if (!isset($permissions[$resource])) {
            return false;
        }
        
        $resourcePermissions = $permissions[$resource];
        
        // Check for specific action
        if (in_array($action, $resourcePermissions)) {
            return true;
        }
        
        // Check for own resource permission
        if (in_array("$action.own", $resourcePermissions)) {
            return $resourceInstance && $resourceInstance->user_id === $role->id;
        }
        
        return false;
     }
 }

                    
                    

Security Headers

Security Headers Manager

Comprehensive security headers for HTTP responses.

use Forge\Security\Headers\SecurityHeaders;
use Forge\Security\Headers\HeaderManager;

$headerManager = new HeaderManager();
$securityHeaders = new SecurityHeaders();

/**
 * Set security headers
 */
public function setSecurityHeaders(Response $response): Response
{
    // X-Content-Type-Options
    $response->header('X-Content-Type-Options', 'nosniff');
    
    // X-Frame-Options
    $response->header('X-Frame-Options', 'DENY');
    
    // X-XSS-Protection
    $response->header('X-XSS-Protection', '1; mode=block');
    
    // Strict-Transport-Security
    $response->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
    
    // Referrer-Policy
    $response->header('Referrer-Policy', 'strict-origin-when-cross-origin');
    
    // Permissions-Policy
    $response->header('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
    
    return $response;
}

/**
 * Content Security Policy headers
 */
public function setCspHeaders(Response $response): Response
{
    $csp = "default-src 'self'; " .
           "script-src 'self' 'unsafe-inline' https://cdn.example.com; " .
           "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " .
           "img-src 'self' data: https:; " .
           "font-src 'self' https://fonts.gstatic.com; " .
           "connect-src 'self' https://api.example.com; " .
           "frame-src 'none'; " .
           "object-src 'none'; " .
           "base-uri 'self'; " .
           "form-action 'self';";
    
    $response->header('Content-Security-Policy', $csp);
    
    return $response;
}

/**
 * Security headers middleware
 */
class SecurityHeadersMiddleware
{
    private array $headers = [
        'X-Content-Type-Options' => 'nosniff',
        'X-Frame-Options' => 'DENY',
        'X-XSS-Protection' => '1; mode=block',
        'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains',
        'Referrer-Policy' => 'strict-origin-when-cross-origin',
        'Permissions-Policy' => 'geolocation=(), microphone=(), camera=()'
    ];
    
    public function handle(Request $request, Closure $next): mixed
    {
        $response = $next($request);
        
        foreach ($this->headers as $header => $value) {
            $response->header($header, $value);
        }
        
        return $response;
    }
    
    public function setHeader(string $header, string $value): void
    {
        $this->headers[$header] = $value;
    }
    
    public function removeHeader(string $header): void
    {
        unset($this->headers[$header]);
    }
}

Security Best Practices

Do's

  • ✅ Use HTTPS everywhere
  • ✅ Implement proper authentication
  • ✅ Use parameterized queries
  • ✅ Validate and sanitize all input
  • ✅ Use strong encryption for sensitive data
  • ✅ Implement rate limiting
  • ✅ Keep dependencies updated
  • ✅ Use security headers
  • ✅ Implement proper logging
  • ✅ Regular security audits

Don'ts

  • ❌ Don't store passwords in plain text
  • ❌ Don't trust user input
  • ❌ Don't expose sensitive data in URLs
  • ❌ Don't use weak encryption
  • ❌ Don't disable CSRF protection
  • ❌ Don't store secrets in code
  • ❌ Don't ignore security warnings
  • ❌ Don't use outdated libraries
  • ❌ Don't expose error details
  • ❌ Don't skip input validation

Security Checklist

Authentication

  • • Use strong password requirements
  • • Implement account lockout
  • • Use secure session management
  • • Implement 2FA when possible

Data Protection

  • • Encrypt sensitive data at rest
  • • Use TLS for data in transit
  • • Implement proper key management
  • • Regular data backups

Input Validation

  • • Validate all user input
  • • Use parameterized queries
  • • Implement proper error handling
  • • Sanitize output data

Monitoring

  • • Log security events
  • • Monitor for suspicious activity
  • • Set up alerts
  • • Regular security reviews