<?php namespace App\Http\Controllers; 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 webauthn_login(Request $request) { return view("user.webauthn.login"); } 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): PublicKeyCredentialRequestOptions { $challenge = random_bytes(32); $request->session()->put("webauthn_login_challenge", $challenge); $publicKeyCredentialRequestOptions = new PublicKeyCredentialRequestOptions( $challenge ); $publicKeyCredentialRequestOptions->setUserVerification( PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_REQUIRED ); $publicKeyCredentialRequestOptions->allowCredentials([]); 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") ); $publicKeyCredential = WebAuthnService::getPublicKeyCredentialLoader()->loadArray($request->json()->all()); $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(), $authenticatorAssertionResponse->getUserHandle(), ["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() ]; } }