Files
a.montask/app/Http/Controllers/FingerprintController.php
T
2026-04-25 11:46:02 +00:00

180 lines
6.5 KiB
PHP
Executable File

<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use App\Models\Webauth;
use App\Models\User;
use Carbon\Carbon;
class FingerprintController extends Controller
{
protected string $rpName = 'a.montask.ir';
protected string $rpId = 'a.montask.ir';
/** برای ثبت اثر انگشت */
public function generateRegisterOptions(Request $request)
{
$user_identity = $_COOKIE['user_identity'] ?? null;
if (!$user_identity) {
return response()->json(['error' => 'کاربر لاگین نیست'], 403);
}
$customer = User::where('user_name', $user_identity)->first();
if (!$customer) {
return response()->json(['error' => 'کاربر یافت نشد'], 404);
}
$challenge = random_bytes(32);
$challengeBase64 = $this->b64e($challenge);
Cache::put("register_challenge_{$customer->id}", $challengeBase64, 120);
return response()->json([
"publicKey" => [
"rp" => [
"name" => $this->rpName,
"id" => $this->rpId,
],
"user" => [
"id" => $this->b64e($customer->id),
"name" => $customer->user_name,
"displayName" => $customer->name,
"number" => $customer->number,
],
"challenge" => $challengeBase64,
"pubKeyCredParams" => [
["type" => "public-key", "alg" => -7]
],
"authenticatorSelection" => [
"userVerification" => "required"
]
]
]);
}
/** تایید ثبت اثر انگشت */
public function verifyRegister(Request $request)
{
$user_identity = $_COOKIE['user_identity'] ?? null;
if (!$user_identity) {
return response()->json(['error' => 'کاربر لاگین نیست'], 403);
}
$customer = User::where('user_name', $user_identity)->first();
if (!$customer) {
return response()->json(['error' => 'کاربر یافت نشد'], 404);
}
$data = $request->all();
$storedChallenge = Cache::pull("register_challenge_{$customer->id}");
if (!$storedChallenge) return response()->json(["error" => "challenge not found"], 400);
if (!isset($data['response']['clientDataJSON'])) return response()->json(["error" => "clientDataJSON missing"], 400);
$clientDataJSON = base64_decode(strtr($data['response']['clientDataJSON'], '-_', '+/'));
$clientData = json_decode($clientDataJSON, true);
if (!$clientData) return response()->json(["error" => "Invalid clientData"], 400);
if (!hash_equals($clientData['challenge'], $storedChallenge)) return response()->json(["error" => "Invalid challenge"], 403);
if ($clientData['origin'] !== 'https://' . $this->rpId) return response()->json(["error" => "Invalid origin"], 403);
if ($clientData['type'] !== 'webauthn.create') return response()->json(["error" => "Invalid type"], 403);
Webauth::create([
"name" => $request->input("name", "دستگاه " . $customer->name),
"customer_id" => $customer->id,
"credential_id" => $data["id"],
"public_key" => $data["response"]["publicKey"],
"attestation" => json_encode($data["response"]["attestationObject"]),
"transports" => null,
"sign_count" => 0,
"last_used_at" => now(),
]);
return response()->json(["message" => "اثر انگشت با موفقیت ثبت شد"]);
}
/** تولید challenge برای ورود */
public function generateLoginOptions()
{
$challenge = $this->b64e(random_bytes(32));
Cache::put("login_challenge_global", $challenge, 120);
return response()->json([
"publicKey" => [
"challenge" => $challenge,
"rpId" => $this->rpId,
"userVerification" => "required",
"timeout" => 60000,
]
]);
}
/** تایید ورود */
public function verifyLogin(Request $request)
{
$data = $request->all();
$credentialId = $data['id'] ?? null;
if (!$credentialId) {
return response()->json(["message" => "credential_id missing"], 400);
}
$credential = Webauth::where("credential_id", $credentialId)->first();
if (!$credential) {
return response()->json(["message" => "اثر انگشت شناخته نشد"], 404);
}
$customer = User::find($credential->customer_id);
if (!$customer) {
return response()->json(["message" => "کاربر حذف شده"], 404);
}
$storedChallenge = Cache::pull("login_challenge_global");
if (!$storedChallenge) {
return response()->json(["message" => "Challenge منقضی شد"], 403);
}
// clientData
$clientDataJSON = base64_decode(strtr($data['response']['clientDataJSON'], '-_', '+/'));
$clientData = json_decode($clientDataJSON, true);
if ($clientData['challenge'] !== $storedChallenge) {
return response()->json(["message" => "Invalid challenge"], 403);
}
if ($clientData['origin'] !== 'https://' . $this->rpId) {
return response()->json(["message" => "Invalid origin"], 403);
}
// authenticatorData
$authData = base64_decode(strtr($data['response']['authenticatorData'], '-_', '+/'));
$signature = base64_decode(strtr($data['response']['signature'], '-_', '+/'));
$clientDataHash = hash('sha256', $clientDataJSON, true);
$signedData = $authData . $clientDataHash;
$ok = openssl_verify($signedData, $signature, $credential->public_key, OPENSSL_ALGO_SHA256);
if ($ok !== 1) {
return response()->json(["message" => "Invalid signature"], 403);
}
// لاگین
$token = auth('customer')->login($customer);
auth('customer')->setUser($customer);
return response()->json([
"message" => "ورود موفق",
"token" => $token,
"user_id" => $customer->id
]);
}
private function b64e($v)
{
return rtrim(strtr(base64_encode($v),'+/','-_'),'=');
}
}