199 lines
8.1 KiB
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()
|
|
];
|
|
}
|
|
}
|