Auth API — Register, Login, JWT Protection

লেসন ৩৮ / ৩৯

Database

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(150) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    role ENUM('admin','user') DEFAULT 'user',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Simple JWT (dependency ছাড়া)

<?php
define("JWT_SECRET", "your-secret-key-change-this");

function b64url(string $data): string {
    return rtrim(strtr(base64_encode($data), "+/", "-_"), "=");
}
function b64urlDecode(string $data): string {
    return base64_decode(strtr($data, "-_", "+/"));
}

function generateJWT(int $uid, string $email, string $role): string {
    $h = b64url(json_encode(["alg"=>"HS256","typ"=>"JWT"]));
    $p = b64url(json_encode([
        "user_id" => $uid,
        "email" => $email,
        "role" => $role,
        "exp" => time() + 86400
    ]));
    $sig = b64url(hash_hmac("sha256", "$h.$p", JWT_SECRET, true));
    return "$h.$p.$sig";
}

function verifyJWT(string $token): ?array {
    $parts = explode(".", $token);
    if (count($parts) !== 3) return null;
    [$h, $p, $sig] = $parts;
    $expected = b64url(hash_hmac("sha256", "$h.$p", JWT_SECRET, true));
    if (!hash_equals($expected, $sig)) return null;
    $payload = json_decode(b64urlDecode($p), true);
    if (($payload["exp"] ?? 0) < time()) return null;
    return $payload;
}
?>

Register

<?php
function authRegister(mysqli $conn): void {
    $data = getInput();
    $name = trim($data["name"] ?? "");
    $email = trim($data["email"] ?? "");
    $password = $data["password"] ?? "";

    $errors = [];
    if (strlen($name) < 2) $errors["name"] = "নাম কমপক্ষে ২ অক্ষর";
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errors["email"] = "সঠিক ইমেইল";
    if (strlen($password) < 6) $errors["password"] = "কমপক্ষে ৬ অক্ষর";
    if ($errors) respond(422, ["success"=>false, "errors"=>$errors]);

    $stmt = $conn->prepare("SELECT id FROM users WHERE email=?");
    $stmt->bind_param("s", $email);
    $stmt->execute();
    if ($stmt->get_result()->fetch_assoc()) {
        respond(409, ["error"=>"ইমেইল ইতিমধ্যে আছে"]);
    }

    $hash = password_hash($password, PASSWORD_BCRYPT);
    $stmt = $conn->prepare("INSERT INTO users (name, email, password) VALUES (?,?,?)");
    $stmt->bind_param("sss", $name, $email, $hash);
    $stmt->execute();

    $uid = $conn->insert_id;
    $token = generateJWT($uid, $email, "user");

    respond(201, [
        "success" => true,
        "data" => [
            "user" => ["id"=>$uid, "name"=>$name, "email"=>$email],
            "token" => $token
        ]
    ]);
}
?>

Login

function authLogin(mysqli $conn): void {
    $data = getInput();
    $email = trim($data["email"] ?? "");
    $password = $data["password"] ?? "";

    if (!$email || !$password) respond(400, ["error"=>"Email ও password দিন"]);

    $stmt = $conn->prepare("SELECT id, name, email, password, role FROM users WHERE email=?");
    $stmt->bind_param("s", $email);
    $stmt->execute();
    $user = $stmt->get_result()->fetch_assoc();

    if (!$user || !password_verify($password, $user["password"])) {
        respond(401, ["error"=>"ভুল credentials"]);
    }

    unset($user["password"]);
    $token = generateJWT($user["id"], $user["email"], $user["role"]);

    respond(200, [
        "success" => true,
        "data" => ["user" => $user, "token" => $token]
    ]);
}

Protection Middleware

function requireAuth(): array {
    $headers = getallheaders();
    $auth = $headers["Authorization"] ?? $headers["authorization"] ?? "";

    if (!preg_match("/Bearer\s+(.+)/", $auth, $m)) {
        respond(401, ["error" => "Token দরকার"]);
    }

    $payload = verifyJWT($m[1]);
    if (!$payload) respond(401, ["error" => "Invalid/Expired token"]);

    return $payload;
}

function requireAdmin(): array {
    $user = requireAuth();
    if (($user["role"] ?? "") !== "admin") {
        respond(403, ["error" => "Admin অনুমতি দরকার"]);
    }
    return $user;
}

Protected Endpoint

// GET /api/profile
function getProfile(mysqli $conn): void {
    $user = requireAuth();    // ← Protection

    $stmt = $conn->prepare("SELECT id,name,email,role FROM users WHERE id=?");
    $stmt->bind_param("i", $user["user_id"]);
    $stmt->execute();
    $profile = $stmt->get_result()->fetch_assoc();

    respond(200, ["success" => true, "data" => $profile]);
}

// DELETE /api/products/5 (admin only)
function productDelete(mysqli $conn, int $id): void {
    requireAdmin();
    // ... delete logic
}

Test

# Register
curl -X POST http://localhost/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name":"করিম","email":"k@mail.com","password":"secret123"}'

# Login
curl -X POST http://localhost/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"k@mail.com","password":"secret123"}'

# Protected
curl http://localhost/api/profile \
  -H "Authorization: Bearer eyJ0eXAi..."