lubo_comment_query/app/Http/Controllers/UserWebAuthnController.php

199 lines
8.1 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\WebAuthn\Repository\PublicKeyCredentialSourceRepositoryImpl;
use App\WebAuthn\WebAuthnService;
use Cose\Algorithm\Manager;
use Cose\Algorithm\Signature\ECDSA\ES256;
use Cose\Algorithm\Signature\RSA\RS256;
use GuzzleHttp\Psr7\ServerRequest;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Auth;
use Webauthn\AttestationStatement\AttestationObjectLoader;
use Webauthn\AttestationStatement\AttestationStatementSupportManager;
use Webauthn\AttestationStatement\NoneAttestationStatementSupport;
use Webauthn\AuthenticationExtensions\ExtensionOutputCheckerHandler;
use Webauthn\AuthenticatorAssertionResponse;
use Webauthn\AuthenticatorAssertionResponseValidator;
use Webauthn\AuthenticatorAttestationResponse;
use Webauthn\AuthenticatorAttestationResponseValidator;
use Webauthn\AuthenticatorSelectionCriteria;
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialLoader;
use Webauthn\PublicKeyCredentialParameters;
use Webauthn\PublicKeyCredentialRequestOptions;
use Webauthn\PublicKeyCredentialRpEntity;
use Webauthn\PublicKeyCredentialSourceRepository;
use Webauthn\PublicKeyCredentialUserEntity;
use Webauthn\TokenBinding\IgnoreTokenBindingHandler;
class UserWebAuthnController extends BaseController
{
public function register_options(Request $request): PublicKeyCredentialCreationOptions
{
$userEntity = new PublicKeyCredentialUserEntity(
$request->user("web")->name,
$request->user("web")->id,
$request->user("web")->name,
);
$challenge = random_bytes(16);
$request->session()->put("webauthn_register_challenge", $challenge);
return WebAuthnService::createRequestOptions($userEntity, $challenge);
}
public function login_options(Request $request)
{
$challenge = random_bytes(32);
$request->session()->put("webauthn_login_challenge", $challenge);
$username = $request->post("username", "");
if ($username) {
$query = User::query();
if (str_contains($username, "@")) {
$query->where("email", "=", $username);
} else {
$query->where("name", "=", $username);
}
$user = $query->first();
if ($user) {
$userHandle = (string) $user->id;
} else {
return new Response([
"success" => false,
"code" => 401,
"message" => "无此用户"
], 401);
}
} else {
$userHandle = "0";
}
$request->session()->put("webauthn_login_user", $userHandle);
$publicKeyCredentialRequestOptions = new PublicKeyCredentialRequestOptions(
$challenge
);
$publicKeyCredentialRequestOptions->setUserVerification(
PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_REQUIRED
);
$publicKeyCredentialSources = WebAuthnService::getPublicKeyCredentialSourceRepository()->findAllForUserEntity(
new PublicKeyCredentialUserEntity("", $userHandle, "")
);
array_map(function ($source) use ($publicKeyCredentialRequestOptions) {
$publicKeyCredentialRequestOptions->allowCredential($source->getPublicKeyCredentialDescriptor());
} ,$publicKeyCredentialSources);
return $publicKeyCredentialRequestOptions;
}
public function register_validate(Request $request)
{
if (!$request->session()->has("webauthn_register_challenge")) {
return new Response([
"success" => false,
"code" => 403,
"message" => "重放攻击可能?无对应验证请求"
], 403);
}
$publicKeyCredential = WebAuthnService::getPublicKeyCredentialLoader()->loadArray($request->json()->all());
$authenticatorAttestationResponse = $publicKeyCredential->getResponse();
if (!$authenticatorAttestationResponse instanceof AuthenticatorAttestationResponse) {
//e.g. process here with a redirection to the public key creation page.
return new Response([
"success" => false,
"code" => 400,
"message" => "接口调用错误?无法验证请求"
], 400);
}
$userEntity = new PublicKeyCredentialUserEntity(
$request->user("web")->name,
$request->user("web")->id,
$request->user("web")->name,
);
$challenge = $request->session()->remove("webauthn_register_challenge");
$publicKeyCredentialCreationOptions = WebAuthnService::createRequestOptions($userEntity, $challenge);
try {
$publicKeyCredentialSource = WebAuthnService::getAuthenticatorAttestationResponseValidator()->check(
$authenticatorAttestationResponse,
$publicKeyCredentialCreationOptions,
ServerRequest::fromGlobals(),
["localhost"]
);
} catch (\Throwable $e) {
return new Response([
"success" => false,
"code" => 500,
"message" => "服务器异常",
"exception" => $e->getMessage()
], 500);
}
WebAuthnService::getPublicKeyCredentialSourceRepository()->saveCredentialSource($publicKeyCredentialSource);
return $publicKeyCredentialSource;
}
public function login_validate(Request $request)
{
if (!$request->session()->has("webauthn_login_challenge")) {
return new Response([
"success" => false,
"code" => 403,
"message" => "重放攻击可能?无对应验证请求"
], 403);
}
$publicKeyCredentialRequestOptions = new PublicKeyCredentialRequestOptions(
$request->session()->remove("webauthn_login_challenge")
);
$publicKeyCredentialSources = WebAuthnService::getPublicKeyCredentialSourceRepository()->findAllForUserEntity(
new PublicKeyCredentialUserEntity("", "0", "")
);
$publicKeyCredential = WebAuthnService::getPublicKeyCredentialLoader()->loadArray($request->json()->all());
$userHandle = null;
foreach ($publicKeyCredentialSources as $source) {
if ($source->getPublicKeyCredentialId() === $publicKeyCredential->getRawId()) {
$userHandle = $source->getUserHandle();
break;
}
}
if ($userHandle === null) {
return new Response([
"success" => false,
"code" => 401,
"message" => "无此密钥"
], 401);
}
$authenticatorAssertionResponse = $publicKeyCredential->getResponse();
if (!$authenticatorAssertionResponse instanceof AuthenticatorAssertionResponse) {
//e.g. process here with a redirection to the public key login/MFA page.
return new Response([
"success" => false,
"code" => 400,
"message" => "接口调用错误?无法验证请求"
], 400);
}
try {
$publicKeyCredentialSource = WebAuthnService::getAuthenticatorAssertionResponseValidator()->check(
$publicKeyCredential->getRawId(),
$authenticatorAssertionResponse,
$publicKeyCredentialRequestOptions,
ServerRequest::fromGlobals(),
$userHandle,
["localhost"]
);
} catch (\Throwable $e) {
return new Response([
"success" => false,
"code" => 500,
"message" => "服务器异常",
"exception" => $e->getMessage()
], 500);
}
Auth::loginUsingId($publicKeyCredentialSource->getUserHandle());
return [
"success" => true,
"code" => 0,
"message" => "登录成功",
"userId" => $publicKeyCredentialSource->getUserHandle()
];
}
}