1-4-Evolución en PHP Puro-Credenciales en Body/Query String:

 

Evolución en PHP Puro

Antes (Menos Seguro) - Credenciales en Body/Query String:

php
<?php
// login_old.php - FORMA ANTIGUA (NO RECOMENDADA)

// Credenciales venían en el body (POST) o query string (GET)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $email = $_POST['email'];
    $password = $_POST['password'];
    $api_key = $_POST['api_key'] ?? ''; // ❌ MAL: clave en body
    
    // Verificar credenciales
    if (authenticateUser($email, $password)) {
        session_start();
        $_SESSION['user_id'] = getUserId($email);
        echo json_encode(['status' => 'success']);
    }
}

// O PEOR AÚN - en query string (GET)
// https://api.com/data?api_key=mi_clave_secreta ❌ MUY PELIGROSO

Ahora (Más Seguro) - Tokens en Headers:

php
<?php
// api_auth.php - FORMA ACTUAL (RECOMENDADA)

header('Content-Type: application/json');

function getAuthorizationHeader() {
    $headers = null;
    
    if (isset($_SERVER['Authorization'])) {
        $headers = trim($_SERVER['Authorization']);
    } elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
        $headers = trim($_SERVER['HTTP_AUTHORIZATION']);
    } elseif (function_exists('apache_request_headers')) {
        $requestHeaders = apache_request_headers();
        $requestHeaders = array_combine(
            array_map('ucwords', array_keys($requestHeaders)),
            array_values($requestHeaders)
        );
        if (isset($requestHeaders['Authorization'])) {
            $headers = trim($requestHeaders['Authorization']);
        }
    }
    
    return $headers;
}

function getBearerToken() {
    $headers = getAuthorizationHeader();
    
    if (!empty($headers)) {
        if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
            return $matches[1];
        }
    }
    return null;
}

// Uso del token desde el header
$token = getBearerToken();

if (!$token) {
    http_response_code(401);
    echo json_encode(['error' => 'Token no proporcionado']);
    exit;
}

// Verificar token
if (verifyToken($token)) {
    $userData = decodeToken($token);
    echo json_encode(['user' => $userData]);
} else {
    http_response_code(401);
    echo json_encode(['error' => 'Token inválido']);
}

Implementación Completa en PHP Puro

1. Estructura de Archivos:

text
/api/
  ├── auth/
  │   ├── login.php          # Login inicial (body)
  │   └── logout.php         # Logout (header)
  ├── users/
  │   └── profile.php        # Datos usuario (header)
  └── config/
      ├── database.php
      └── auth.php

2. Configuración de Base de Datos:

php
<?php
// config/database.php

class Database {
    private $host = 'localhost';
    private $db_name = 'mi_api';
    private $username = 'root';
    private $password = '';
    public $conn;
    
    public function getConnection() {
        $this->conn = null;
        try {
            $this->conn = new PDO(
                "mysql:host=" . $this->host . ";dbname=" . $this->db_name,
                $this->username, 
                $this->password
            );
            $this->conn->exec("set names utf8");
        } catch(PDOException $exception) {
            echo "Error de conexión: " . $exception->getMessage();
        }
        return $this->conn;
    }
}

3. Sistema de Autenticación:

php
<?php
// config/auth.php

class Auth {
    private $conn;
    private $table_name = "users";
    
    public function __construct($db) {
        $this->conn = $db;
    }
    
    // Login inicial (credenciales en BODY)
    public function login($email, $password) {
        $query = "SELECT id, name, email, password FROM " . $this->table_name . " 
                  WHERE email = :email LIMIT 1";
        
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(':email', $email);
        $stmt->execute();
        
        if ($stmt->rowCount() > 0) {
            $row = $stmt->fetch(PDO::FETCH_ASSOC);
            $id = $row['id'];
            $name = $row['name'];
            $email = $row['email'];
            $hashed_password = $row['password'];
            
            if (password_verify($password, $hashed_password)) {
                // Generar token (simulado - en producción usar JWT)
                $token = $this->generateToken($id);
                
                // Guardar token en base de datos
                $this->saveToken($id, $token);
                
                return [
                    'status' => 'success',
                    'user' => [
                        'id' => $id,
                        'name' => $name,
                        'email' => $email
                    ],
                    'access_token' => $token,
                    'token_type' => 'Bearer'
                ];
            }
        }
        
        return ['status' => 'error', 'message' => 'Credenciales inválidas'];
    }
    
    // Verificar token desde HEADER
    public function verifyToken($token) {
        $query = "SELECT user_id FROM user_tokens WHERE token = :token AND expires_at > NOW()";
        
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(':token', $token);
        $stmt->execute();
        
        if ($stmt->rowCount() > 0) {
            $row = $stmt->fetch(PDO::FETCH_ASSOC);
            return $row['user_id'];
        }
        
        return false;
    }
    
    private function generateToken($userId) {
        return bin2hex(random_bytes(32)) . '_' . $userId;
    }
    
    private function saveToken($userId, $token) {
        $query = "INSERT INTO user_tokens (user_id, token, expires_at) 
                  VALUES (:user_id, :token, DATE_ADD(NOW(), INTERVAL 1 HOUR))
                  ON DUPLICATE KEY UPDATE token = :token, expires_at = DATE_ADD(NOW(), INTERVAL 1 HOUR)";
        
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(':user_id', $userId);
        $stmt->bindParam(':token', $token);
        return $stmt->execute();
    }
}

4. Login (Credenciales en BODY):

php
<?php
// auth/login.php

header('Content-Type: application/json');
require_once '../config/database.php';
require_once '../config/auth.php';

// Permitir CORS si es necesario
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-Type');

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    
    // Obtener datos del BODY (JSON)
    $input = json_decode(file_get_contents('php://input'), true);
    
    $email = $input['email'] ?? '';
    $password = $input['password'] ?? '';
    
    if (!empty($email) && !empty($password)) {
        $database = new Database();
        $db = $database->getConnection();
        $auth = new Auth($db);
        
        $result = $auth->login($email, $password);
        
        if ($result['status'] === 'success') {
            // También enviar token en header (buena práctica)
            header('Authorization: Bearer ' . $result['access_token']);
            http_response_code(200);
            echo json_encode($result);
        } else {
            http_response_code(401);
            echo json_encode($result);
        }
    } else {
        http_response_code(400);
        echo json_encode(['status' => 'error', 'message' => 'Email y password requeridos']);
    }
} else {
    http_response_code(405);
    echo json_encode(['status' => 'error', 'message' => 'Método no permitido']);
}

5. Endpoint Protegido (Token en HEADER):

php
<?php
// users/profile.php

header('Content-Type: application/json');
require_once '../config/database.php';
require_once '../config/auth.php';

function getBearerToken() {
    $headers = null;
    
    if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
        $headers = trim($_SERVER['HTTP_AUTHORIZATION']);
    } elseif (function_exists('apache_request_headers')) {
        $requestHeaders = apache_request_headers();
        if (isset($requestHeaders['Authorization'])) {
            $headers = trim($requestHeaders['Authorization']);
        }
    }
    
    if (!empty($headers) && preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
        return $matches[1];
    }
    return null;
}

// Verificar autenticación
$token = getBearerToken();

if (!$token) {
    http_response_code(401);
    echo json_encode(['error' => 'Token de acceso requerido']);
    exit;
}

$database = new Database();
$db = $database->getConnection();
$auth = new Auth($db);

$userId = $auth->verifyToken($token);

if (!$userId) {
    http_response_code(401);
    echo json_encode(['error' => 'Token inválido o expirado']);
    exit;
}

// Token válido - obtener datos del usuario
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    $query = "SELECT id, name, email FROM users WHERE id = :id";
    $stmt = $db->prepare($query);
    $stmt->bindParam(':id', $userId);
    $stmt->execute();
    
    $user = $stmt->fetch(PDO::FETCH_ASSOC);
    
    http_response_code(200);
    echo json_encode([
        'status' => 'success',
        'user' => $user
    ]);
} else {
    http_response_code(405);
    echo json_encode(['error' => 'Método no permitido']);
}

6. Cliente PHP Consumiendo la API:

php
<?php
// client_example.php

class ApiClient {
    private $base_url = 'http://localhost/api';
    private $token = '';
    
    public function login($email, $password) {
        $url = $this->base_url . '/auth/login.php';
        
        $data = json_encode([
            'email' => $email,
            'password' => $password
        ]);
        
        $response = $this->makeRequest('POST', $url, $data);
        
        if (isset($response['access_token'])) {
            $this->token = $response['access_token'];
        }
        
        return $response;
    }
    
    public function getProfile() {
        $url = $this->base_url . '/users/profile.php';
        return $this->makeRequest('GET', $url);
    }
    
    private function makeRequest($method, $url, $data = null) {
        $ch = curl_init();
        
        $headers = [
            'Content-Type: application/json',
        ];
        
        // Agregar token al header si existe
        if ($this->token) {
            $headers[] = 'Authorization: Bearer ' . $this->token;
        }
        
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        
        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        }
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        return json_decode($response, true);
    }
}

// Uso del cliente
$client = new ApiClient();

// Login (credenciales en BODY)
$loginResult = $client->login('usuario@ejemplo.com', 'password123');

if (isset($loginResult['access_token'])) {
    // Solicitudes siguientes (token en HEADER)
    $profile = $client->getProfile();
    print_r($profile);
}

Resumen de Flujo en PHP Puro:

  1. Login: Credenciales en php://input (body) → Genera token

  2. Solicitudes siguientes: Token en $_SERVER['HTTP_AUTHORIZATION'] (header)

  3. Seguridad: Nunca credenciales en URLs o logs

  4. Headers personalizados: Acceder via $_SERVER o apache_request_headers()

¿Quieres que profundice en algún aspecto específico como JWT en PHP puro o seguridad avanzada?

Comentarios

Entradas más populares de este blog

8-Creación de una API RESTful con Laravel

02 -Rutas en Laravel

3-Rutas