PHP API authentication security provides comprehensive protection for web APIs using modern authentication methods and security best practices.

PHP API Authentication Security: Complete Guide to Secure API Development with Modern PHP

PHP API Authentication Security: Complete Guide to Secure API Development with Modern PHP

PHP API authentication security provides comprehensive protection for web APIs using modern authentication methods and security best practices.

API Security

PHP authentication, API security, and modern protection create secure applications.

This guide covers essential security patterns for PHP API development.

Understanding API Authentication

API authentication verifies client identity and authorizes access to resources.

Authentication Basics

API authentication, identity verification, and access control create security.

Proper authentication prevents unauthorized access while enabling legitimate use.

"When APIs authenticate properly, security transforms from vulnerability to strength."

JWT Authentication

Implementing JSON Web Token authentication in PHP APIs.

Token Security

JWT authentication, token security, and web tokens create stateless authentication.

JWT provides secure, scalable authentication for distributed systems.

<?php
// JWT Authentication implementation
class JwtAuth {
    private $secretKey;
    private $algorithm = 'HS256';
    private $expiresIn = 3600; // 1 hour
    
    public function __construct(string $secretKey) {
        $this->secretKey = $secretKey;
    }
    
    public function generateToken(array $payload): string {
        $header = [
            'typ' => 'JWT',
            'alg' => $this->algorithm
        ];
        
        $payload['iat'] = time();
        $payload['exp'] = time() + $this->expiresIn;
        
        $headerEncoded = $this->base64UrlEncode(json_encode($header));
        $payloadEncoded = $this->base64UrlEncode(json_encode($payload));
        
        $signature = hash_hmac(
            'sha256',
            "$headerEncoded.$payloadEncoded",
            $this->secretKey,
            true
        );
        
        $signatureEncoded = $this->base64UrlEncode($signature);
        
        return "$headerEncoded.$payloadEncoded.$signatureEncoded";
    }
    
    public function verifyToken(string $token): ?array {
        $parts = explode('.', $token);
        
        if (count($parts) !== 3) {
            return null;
        }
        
        [$headerEncoded, $payloadEncoded, $signatureEncoded] = $parts;
        
        $signature = hash_hmac(
            'sha256',
            "$headerEncoded.$payloadEncoded",
            $this->secretKey,
            true
        );
        
        if ($this->base64UrlEncode($signature) !== $signatureEncoded) {
            return null;
        }
        
        $payload = json_decode($this->base64UrlDecode($payloadEncoded), true);
        
        if ($payload['exp'] < time()) {
            return null;
        }
        
        return $payload;
    }
    
    private function base64UrlEncode(string $data): string {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }
    
    private function base64UrlDecode(string $data): string {
        return base64_decode(strtr($data, '-_', '+/'));
    }
}

OAuth2 Implementation

Building OAuth2 authorization server and client applications.

OAuth2 Security

OAuth2 implementation, authorization server, and client applications create standards.

OAuth2 provides secure delegated authorization for third-party access.

<?php
// OAuth2 Authorization Server
class OAuth2Server {
    private $clientRepository;
    private $accessTokenRepository;
    private $userRepository;
    
    public function __construct(
        ClientRepository $clientRepository,
        AccessTokenRepository $accessTokenRepository,
        UserRepository $userRepository
    ) {
        $this->clientRepository = $clientRepository;
        $this->accessTokenRepository = $accessTokenRepository;
        $this->userRepository = $userRepository;
    }
    
    public function authorize(array $request): array {
        $clientId = $request['client_id'] ?? null;
        $redirectUri = $request['redirect_uri'] ?? null;
        $scopes = $request['scope'] ?? 'read';
        $state = $request['state'] ?? null;
        
        // Validate client
        $client = $this->clientRepository->findById($clientId);
        if (!$client || $client->redirect_uri !== $redirectUri) {
            throw new OAuth2Exception('Invalid client');
        }
        
        // Generate authorization code
        $authCode = $this->generateAuthorizationCode();
        
        // Store authorization code
        $this->storeAuthorizationCode($authCode, $clientId, $scopes, $state);
        
        return [
            'authorization_code' => $authCode,
            'redirect_uri' => $redirectUri . '?code=' . $authCode . '&state=' . $state
        ];
    }
    
    public function token(array $request): array {
        $grantType = $request['grant_type'] ?? null;
        
        switch ($grantType) {
            case 'authorization_code':
                return $this->handleAuthorizationCodeGrant($request);
            case 'client_credentials':
                return $this->handleClientCredentialsGrant($request);
            case 'refresh_token':
                return $this->handleRefreshTokenGrant($request);
            default:
                throw new OAuth2Exception('Unsupported grant type');
        }
    }
    
    private function handleAuthorizationCodeGrant(array $request): array {
        $code = $request['code'] ?? null;
        $clientId = $request['client_id'] ?? null;
        $clientSecret = $request['client_secret'] ?? null;
        
        // Verify client credentials
        $client = $this->clientRepository->findById($clientId);
        if (!$client || !$this->verifyClientSecret($client, $clientSecret)) {
            throw new OAuth2Exception('Invalid client credentials');
        }
        
        // Verify authorization code
        $authData = $this->getAuthorizationCodeData($code);
        if (!$authData || $authData['client_id'] !== $clientId) {
            throw new OAuth2Exception('Invalid authorization code');
        }
        
        // Generate access token
        $accessToken = $this->generateAccessToken($authData['user_id'], $authData['scopes']);
        $refreshToken = $this->generateRefreshToken($authData['user_id']);
        
        // Store tokens
        $this->accessTokenRepository->store($accessToken, $authData['user_id'], $authData['scopes']);
        
        // Remove authorization code
        $this->removeAuthorizationCode($code);
        
        return [
            'access_token' => $accessToken,
            'token_type' => 'Bearer',
            'expires_in' => 3600,
            'refresh_token' => $refreshToken,
            'scope' => $authData['scopes']
        ];
    }
}

API Key Authentication

Implementing API key-based authentication for simple scenarios.

Key Security

API keys, key authentication, and simple security create basic protection.

API keys provide straightforward authentication for machine-to-machine communication.

<?php
// API Key Authentication
class ApiKeyAuth {
    private $apiKeyRepository;
    
    public function __construct(ApiKeyRepository $apiKeyRepository) {
        $this->apiKeyRepository = $apiKeyRepository;
    }
    
    public function authenticate(Request $request): ?User {
        $apiKey = $this->extractApiKey($request);
        
        if (!$apiKey) {
            return null;
        }
        
        $keyData = $this->apiKeyRepository->findByKey($apiKey);
        
        if (!$keyData || !$keyData->isActive()) {
            return null;
        }
        
        // Update last used timestamp
        $this->apiKeyRepository->updateLastUsed($apiKey);
        
        return $keyData->getUser();
    }
    
    private function extractApiKey(Request $request): ?string {
        // Check header
        $headerKey = $request->getHeader('X-API-Key');
        if ($headerKey) {
            return $headerKey;
        }
        
        // Check query parameter
        $queryKey = $request->getQuery('api_key');
        if ($queryKey) {
            return $queryKey;
        }
        
        return null;
    }
    
    public function generateApiKey(User $user, array $scopes = []): string {
        $apiKey = $this->generateRandomKey();
        
        $keyData = new ApiKey([
            'key' => $apiKey,
            'user_id' => $user->getId(),
            'scopes' => $scopes,
            'created_at' => new DateTime(),
            'last_used_at' => null,
            'is_active' => true
        ]);
        
        $this->apiKeyRepository->store($keyData);
        
        return $apiKey;
    }
    
    private function generateRandomKey(): string {
        return bin2hex(random_bytes(32));
    }
}

Rate Limiting

Implementing rate limiting to prevent API abuse.

Rate Protection

Rate limiting, API abuse prevention, and usage control create stability.

Rate limiting protects APIs from abuse while ensuring fair usage.

<?php
// Rate Limiting Implementation
class RateLimiter {
    private $redis;
    private $config;
    
    public function __construct(Redis $redis, array $config) {
        $this->redis = $redis;
        $this->config = $config;
    }
    
    public function checkLimit(string $identifier, string $route = 'default'): bool {
        $key = "rate_limit:{$route}:{$identifier}";
        $limit = $this->config[$route]['requests'] ?? 100;
        $window = $this->config[$route]['window'] ?? 3600;
        
        $current = $this->redis->incr($key);
        
        if ($current === 1) {
            $this->redis->expire($key, $window);
        }
        
        if ($current > $limit) {
            return false;
        }
        
        return true;
    }
    
    public function getRemainingRequests(string $identifier, string $route = 'default'): int {
        $key = "rate_limit:{$route}:{$identifier}";
        $limit = $this->config[$route]['requests'] ?? 100;
        $current = $this->redis->get($key) ?? 0;
        
        return max(0, $limit - (int)$current);
    }
    
    public function getResetTime(string $identifier, string $route = 'default'): int {
        $key = "rate_limit:{$route}:{$identifier}";
        return $this->redis->ttl($key);
    }
}

// Rate Limiting Middleware
class RateLimitMiddleware {
    private $rateLimiter;
    
    public function __construct(RateLimiter $rateLimiter) {
        $this->rateLimiter = $rateLimiter;
    }
    
    public function handle(Request $request, callable $next): Response {
        $identifier = $this->getIdentifier($request);
        $route = $this->getRoute($request);
        
        if (!$this->rateLimiter->checkLimit($identifier, $route)) {
            return new Response('Rate limit exceeded', 429, [
                'X-RateLimit-Limit' => $this->rateLimiter->getLimit($route),
                'X-RateLimit-Remaining' => 0,
                'X-RateLimit-Reset' => $this->rateLimiter->getResetTime($identifier, $route)
            ]);
        }
        
        $response = $next($request);
        
        $response->withHeaders([
            'X-RateLimit-Limit' => $this->rateLimiter->getLimit($route),
            'X-RateLimit-Remaining' => $this->rateLimiter->getRemainingRequests($identifier, $route),
            'X-RateLimit-Reset' => $this->rateLimiter->getResetTime($identifier, $route)
        ]);
        
        return $response;
    }
    
    private function getIdentifier(Request $request): string {
        // Use IP address for unauthenticated requests
        // Use user ID for authenticated requests
        $user = $request->getAttribute('user');
        
        return $user ? "user:{$user->getId()}" : "ip:{$request->getClientIp()}";
    }
    
    private function getRoute(Request $request): string {
        return $request->getRoute() ?? 'default';
    }
}

Input Validation

Validating and sanitizing API input data.

Data Validation

Input validation, data sanitization, and security filtering create protection.

Proper validation prevents injection attacks and data corruption.

<?php
// Input Validation
class ApiValidator {
    private $rules;
    
    public function __construct(array $rules) {
        $this->rules = $rules;
    }
    
    public function validate(array $data, string $ruleSet): array {
        $rules = $this->rules[$ruleSet] ?? [];
        $errors = [];
        $sanitized = [];
        
        foreach ($rules as $field => $fieldRules) {
            $value = $data[$field] ?? null;
            
            foreach ($fieldRules as $rule) {
                $result = $this->applyRule($value, $rule);
                
                if (!$result['valid']) {
                    $errors[$field][] = $result['message'];
                } else {
                    $sanitized[$field] = $result['value'];
                }
            }
        }
        
        return [
            'valid' => empty($errors),
            'errors' => $errors,
            'data' => $sanitized
        ];
    }
    
    private function applyRule($value, string $rule): array {
        $parts = explode(':', $rule);
        $ruleName = $parts[0];
        $parameters = array_slice($parts, 1);
        
        switch ($ruleName) {
            case 'required':
                return $this->validateRequired($value);
            case 'email':
                return $this->validateEmail($value);
            case 'min':
                return $this->validateMin($value, $parameters[0] ?? 0);
            case 'max':
                return $this->validateMax($value, $parameters[0] ?? 255);
            case 'sanitize':
                return $this->sanitize($value, $parameters[0] ?? 'string');
            default:
                return ['valid' => true, 'value' => $value];
        }
    }
    
    private function validateRequired($value): array {
        if ($value === null || $value === '') {
            return [
                'valid' => false,
                'message' => 'This field is required',
                'value' => $value
            ];
        }
        
        return ['valid' => true, 'value' => $value];
    }
    
    private function validateEmail($value): array {
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            return [
                'valid' => false,
                'message' => 'Invalid email format',
                'value' => $value
            ];
        }
        
        return ['valid' => true, 'value' => strtolower($value)];
    }
    
    private function sanitize($value, string $type): array {
        switch ($type) {
            case 'string':
                $sanitized = filter_var($value, FILTER_SANITIZE_STRING);
                break;
            case 'int':
                $sanitized = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
                break;
            case 'email':
                $sanitized = filter_var($value, FILTER_SANITIZE_EMAIL);
                break;
            default:
                $sanitized = $value;
        }
        
        return ['valid' => true, 'value' => $sanitized];
    }
}

CORS Security

Implementing Cross-Origin Resource Sharing security.

CORS Protection

CORS security, cross-origin protection, and browser security create safety.

Proper CORS configuration prevents unauthorized cross-origin requests.

<?php
// CORS Security Middleware
class CorsMiddleware {
    private $config;
    
    public function __construct(array $config) {
        $this->config = array_merge([
            'allowed_origins' => ['*'],
            'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
            'allowed_headers' => ['Content-Type', 'Authorization', 'X-Requested-With'],
            'max_age' => 86400,
            'allow_credentials' => false
        ], $config);
    }
    
    public function handle(Request $request, callable $next): Response {
        $origin = $request->getHeader('Origin');
        $method = $request->getMethod();
        
        // Handle preflight requests
        if ($method === 'OPTIONS') {
            return $this->handlePreflight($origin);
        }
        
        $response = $next($request);
        
        // Add CORS headers to actual requests
        if ($this->isOriginAllowed($origin)) {
            $response->withHeaders([
                'Access-Control-Allow-Origin' => $this->getAllowedOrigin($origin),
                'Access-Control-Allow-Methods' => implode(', ', $this->config['allowed_methods']),
                'Access-Control-Allow-Headers' => implode(', ', $this->config['allowed_headers']),
                'Access-Control-Max-Age' => $this->config['max_age']
            ]);
            
            if ($this->config['allow_credentials']) {
                $response->withHeader('Access-Control-Allow-Credentials', 'true');
            }
        }
        
        return $response;
    }
    
    private function handlePreflight(string $origin): Response {
        if (!$this->isOriginAllowed($origin)) {
            return new Response('CORS policy violation', 403);
        }
        
        return new Response('', 200, [
            'Access-Control-Allow-Origin' => $this->getAllowedOrigin($origin),
            'Access-Control-Allow-Methods' => implode(', ', $this->config['allowed_methods']),
            'Access-Control-Allow-Headers' => implode(', ', $this->config['allowed_headers']),
            'Access-Control-Max-Age' => $this->config['max_age'],
            'Access-Control-Allow-Credentials' => $this->config['allow_credentials'] ? 'true' : 'false'
        ]);
    }
    
    private function isOriginAllowed(string $origin): bool {
        if (in_array('*', $this->config['allowed_origins'])) {
            return true;
        }
        
        return in_array($origin, $this->config['allowed_origins']);
    }
    
    private function getAllowedOrigin(string $origin): string {
        if (in_array('*', $this->config['allowed_origins'])) {
            return '*';
        }
        
        return $origin;
    }
}

Security Headers

Implementing security headers for API protection.

Header Security

Security headers, HTTP protection, and browser security create defense.

Security headers protect against various web vulnerabilities.

<?php
// Security Headers Middleware
class SecurityHeadersMiddleware {
    private $config;
    
    public function __construct(array $config = []) {
        $this->config = array_merge([
            'X-Content-Type-Options' => 'nosniff',
            'X-Frame-Options' => 'DENY',
            'X-XSS-Protection' => '1; mode=block',
            'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains',
            'Content-Security-Policy' => "default-src 'self'",
            'Referrer-Policy' => 'strict-origin-when-cross-origin'
        ], $config);
    }
    
    public function handle(Request $request, callable $next): Response {
        $response = $next($request);
        
        foreach ($this->config as $header => $value) {
            $response->withHeader($header, $value);
        }
        
        return $response;
    }
}

// Security Audit Logger
class SecurityAuditLogger {
    private $logger;
    
    public function __construct(Logger $logger) {
        $this->logger = $logger;
    }
    
    public function logAuthenticationAttempt(string $identifier, bool $success, string $method): void {
        $this->logger->info('Authentication attempt', [
            'identifier' => $identifier,
            'success' => $success,
            'method' => $method,
            'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
            'timestamp' => date('Y-m-d H:i:s')
        ]);
    }
    
    public function logSecurityEvent(string $event, array $context = []): void {
        $this->logger->warning('Security event', array_merge([
            'event' => $event,
            'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
            'timestamp' => date('Y-m-d H:i:s')
        ], $context));
    }
    
    public function logRateLimitExceeded(string $identifier, string $route): void {
        $this->logger->warning('Rate limit exceeded', [
            'identifier' => $identifier,
            'route' => $route,
            'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'timestamp' => date('Y-m-d H:i:s')
        ]);
    }
}

Testing Security

Testing API security implementations.

Security Testing

Security testing, penetration testing, and vulnerability assessment create assurance.

Comprehensive testing ensures security measures work effectively.

<?php
// Security Testing
class ApiSecurityTest extends TestCase {
    private $httpClient;
    private $baseUrl;
    
    protected function setUp(): void {
        $this->httpClient = new HttpClient();
        $this->baseUrl = getenv('API_BASE_URL');
    }
    
    public function testJwtAuthentication(): void {
        // Test valid authentication
        $loginResponse = $this->httpClient->post("{$this->baseUrl}/auth/login", [
            'email' => 'test@example.com',
            'password' => 'password123'
        ]);
        
        $this->assertEquals(200, $loginResponse->getStatusCode());
        
        $loginData = json_decode($loginResponse->getBody(), true);
        $this->assertArrayHasKey('token', $loginData);
        
        // Test authenticated request
        $protectedResponse = $this->httpClient->get("{$this->baseUrl}/api/protected", [
            'Authorization' => "Bearer {$loginData['token']}"
        ]);
        
        $this->assertEquals(200, $protectedResponse->getStatusCode());
        
        // Test invalid token
        $invalidResponse = $this->httpClient->get("{$this->baseUrl}/api/protected", [
            'Authorization' => 'Bearer invalid_token'
        ]);
        
        $this->assertEquals(401, $invalidResponse->getStatusCode());
    }
    
    public function testRateLimiting(): void {
        // Make multiple requests to trigger rate limit
        for ($i = 0; $i < 10; $i++) {
            $response = $this->httpClient->get("{$this->baseUrl}/api/test");
            
            if ($i < 5) {
                $this->assertEquals(200, $response->getStatusCode());
            } else {
                $this->assertEquals(429, $response->getStatusCode());
                break;
            }
        }
    }
    
    public function testInputValidation(): void {
        // Test invalid input
        $invalidResponse = $this->httpClient->post("{$this->baseUrl}/api/users", [
            'email' => 'invalid-email',
            'name' => ''
        ]);
        
        $this->assertEquals(400, $invalidResponse->getStatusCode());
        
        $errors = json_decode($invalidResponse->getBody(), true);
        $this->assertArrayHasKey('errors', $errors);
    }
    
    public function testCorsHeaders(): void {
        $response = $this->httpClient->options("{$this->baseUrl}/api/test", [
            'Origin' => 'https://example.com'
        ]);
        
        $this->assertEquals(200, $response->getStatusCode());
        $this->assertNotEmpty($response->getHeader('Access-Control-Allow-Origin'));
    }
}

Conclusion: API Security

Comprehensive API security protects applications while enabling legitimate access.

Security Excellence

As security evolves, PHP APIs create opportunities for secure application development.

The transformation of API security through modern PHP represents hope for protected, scalable systems.