Webauthn抽象

This commit is contained in:
Jerry Yan 2022-08-08 10:50:49 +08:00
parent f32c2e6344
commit 4c967584ad
2 changed files with 106 additions and 64 deletions

View File

@ -32,8 +32,6 @@ use Webauthn\TokenBinding\IgnoreTokenBindingHandler;
class UserWebAuthnController extends BaseController
{
private $attestationStatementSupportManager = null;
public function webauthn_login(Request $request)
{
return view("user.webauthn.login");
@ -68,12 +66,21 @@ class UserWebAuthnController extends BaseController
public function register_validate(Request $request)
{
if (!$request->session()->has("webauthn_register_challenge")) {
///
return new Response([
"success" => false,
"code" => 403,
"message" => "重放攻击可能?无对应验证请求"
], 403);
}
$publicKeyCredential = $this->get_public_key_credential_loader()->loadArray($request->json()->all());
$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,
@ -82,31 +89,49 @@ class UserWebAuthnController extends BaseController
);
$challenge = $request->session()->remove("webauthn_register_challenge");
$publicKeyCredentialCreationOptions = WebAuthnService::createRequestOptions($userEntity, $challenge);
$publicKeyCredentialSource = $this->get_authn_attestation_response_validator()->check(
$authenticatorAttestationResponse,
$publicKeyCredentialCreationOptions,
ServerRequest::fromGlobals(),
["localhost"]
);
$this->get_pub_key_cred_source_repository()->saveCredentialSource($publicKeyCredentialSource);
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 = $this->get_public_key_credential_loader()->loadArray($request->json()->all());
$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 = $this->get_authn_assertion_response_validator()->check(
$publicKeyCredentialSource = WebAuthnService::getAuthenticatorAssertionResponseValidator()->check(
$publicKeyCredential->getRawId(),
$authenticatorAssertionResponse,
$publicKeyCredentialRequestOptions,
@ -115,7 +140,12 @@ class UserWebAuthnController extends BaseController
["localhost"]
);
} catch (\Throwable $e) {
return new Response($e->getMessage(), 500);
return new Response([
"success" => false,
"code" => 500,
"message" => "服务器异常",
"exception" => $e->getMessage()
], 500);
}
Auth::loginUsingId($publicKeyCredentialSource->getUserHandle());
return [
@ -125,52 +155,4 @@ class UserWebAuthnController extends BaseController
"userId" => $publicKeyCredentialSource->getUserHandle()
];
}
private function get_authn_attestation_response_validator(): AuthenticatorAttestationResponseValidator
{
$authenticatorAttestationResponseValidator = new AuthenticatorAttestationResponseValidator(
$this->get_attestation_stmt_sup_mgr(),
WebAuthnService::getPublicKeyCredentialSourceRepository(),
new IgnoreTokenBindingHandler(),
new ExtensionOutputCheckerHandler()
);
return $authenticatorAttestationResponseValidator;
}
private function get_authn_assertion_response_validator(): AuthenticatorAssertionResponseValidator
{
$algorithmManager = new Manager();
$algorithmManager->add(new ES256());
$algorithmManager->add(new RS256());
$authenticatorAssertionResponseValidator = new AuthenticatorAssertionResponseValidator(
WebAuthnService::getPublicKeyCredentialSourceRepository(),
new IgnoreTokenBindingHandler(),
new ExtensionOutputCheckerHandler(),
$algorithmManager
);
return $authenticatorAssertionResponseValidator;
}
private function get_public_key_credential_loader(): PublicKeyCredentialLoader
{
$publicKeyCredentialLoader = new PublicKeyCredentialLoader(
$this->get_attestation_loader()
);
return $publicKeyCredentialLoader;
}
private function get_attestation_loader(): AttestationObjectLoader
{
$attestationObjectLoader = new AttestationObjectLoader($this->get_attestation_stmt_sup_mgr());
return $attestationObjectLoader;
}
private function get_attestation_stmt_sup_mgr()
{
if ($this->attestationStatementSupportManager === null) {
$this->attestationStatementSupportManager = new AttestationStatementSupportManager();
$this->attestationStatementSupportManager->add(new NoneAttestationStatementSupport());
}
return $this->attestationStatementSupportManager;
}
}

View File

@ -3,20 +3,33 @@
namespace App\WebAuthn;
use App\WebAuthn\Repository\PublicKeyCredentialSourceRepositoryImpl;
use Cose\Algorithm\Manager;
use Cose\Algorithm\Signature\ECDSA\ES256;
use Cose\Algorithm\Signature\RSA\RS256;
use Cose\Algorithms;
use Webauthn\AttestationStatement\AttestationObjectLoader;
use Webauthn\AttestationStatement\AttestationStatementSupportManager;
use Webauthn\AttestationStatement\NoneAttestationStatementSupport;
use Webauthn\AuthenticationExtensions\ExtensionOutputCheckerHandler;
use Webauthn\AuthenticatorAssertionResponseValidator;
use Webauthn\AuthenticatorAttestationResponseValidator;
use Webauthn\AuthenticatorSelectionCriteria;
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialLoader;
use Webauthn\PublicKeyCredentialParameters;
use Webauthn\PublicKeyCredentialRpEntity;
use Webauthn\PublicKeyCredentialSourceRepository;
use Webauthn\PublicKeyCredentialUserEntity;
use Webauthn\TokenBinding\IgnoreTokenBindingHandler;
class WebAuthnService
{
private static $rpName = "开心鄢的录播查询小站";
private static $rpId = "localhost";
private static $rpId = "comment.sc.jerryyan.top";
private static $timeout = 45000;
private static $publicKeyCredentialSourceRepositoryInstance = null;
private static $authenticatorAssertionResponseValidator = null;
private static $attestationStatementSupportManager = null;
public static function createRequestOptions(PublicKeyCredentialUserEntity $userEntity, string $challenge): PublicKeyCredentialCreationOptions
{
$publicKeyCredentialParametersList = [
@ -40,6 +53,53 @@ class WebAuthnService
return static::$publicKeyCredentialSourceRepositoryInstance;
}
public static function getAuthenticatorAssertionResponseValidator(): AuthenticatorAssertionResponseValidator
{
if (static::$authenticatorAssertionResponseValidator === null) {
$algorithmManager = new Manager();
$algorithmManager->add(new ES256());
$algorithmManager->add(new RS256());
static::$authenticatorAssertionResponseValidator = new AuthenticatorAssertionResponseValidator(
static::getPublicKeyCredentialSourceRepository(),
new IgnoreTokenBindingHandler(),
new ExtensionOutputCheckerHandler(),
$algorithmManager
);
}
return static::$authenticatorAssertionResponseValidator;
}
public static function getAuthenticatorAttestationResponseValidator(): AuthenticatorAttestationResponseValidator
{
return new AuthenticatorAttestationResponseValidator(
static::getAttestationStatementSupportManager(),
static::getPublicKeyCredentialSourceRepository(),
new IgnoreTokenBindingHandler(),
new ExtensionOutputCheckerHandler()
);
}
public static function getPublicKeyCredentialLoader(): PublicKeyCredentialLoader
{
return new PublicKeyCredentialLoader(
static::getAttestationObjectLoader()
);
}
public static function getAttestationObjectLoader(): AttestationObjectLoader
{
return new AttestationObjectLoader(static::getAttestationStatementSupportManager());
}
private static function getAttestationStatementSupportManager(): AttestationStatementSupportManager
{
if (static::$attestationStatementSupportManager === null) {
static::$attestationStatementSupportManager = new AttestationStatementSupportManager();
static::$attestationStatementSupportManager->add(new NoneAttestationStatementSupport());
}
return static::$attestationStatementSupportManager;
}
private static function getRpEntity(): PublicKeyCredentialRpEntity
{
return new PublicKeyCredentialRpEntity(