webauthn登录逻辑调整
This commit is contained in:
parent
cc57e4bb18
commit
6632d8cea1
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use App\WebAuthn\Repository\PublicKeyCredentialSourceRepositoryImpl;
|
use App\WebAuthn\Repository\PublicKeyCredentialSourceRepositoryImpl;
|
||||||
use App\WebAuthn\WebAuthnService;
|
use App\WebAuthn\WebAuthnService;
|
||||||
use Cose\Algorithm\Manager;
|
use Cose\Algorithm\Manager;
|
||||||
@ -32,11 +33,6 @@ use Webauthn\TokenBinding\IgnoreTokenBindingHandler;
|
|||||||
|
|
||||||
class UserWebAuthnController extends BaseController
|
class UserWebAuthnController extends BaseController
|
||||||
{
|
{
|
||||||
public function webauthn_login(Request $request)
|
|
||||||
{
|
|
||||||
return view("user.webauthn.login");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function register_options(Request $request): PublicKeyCredentialCreationOptions
|
public function register_options(Request $request): PublicKeyCredentialCreationOptions
|
||||||
{
|
{
|
||||||
$userEntity = new PublicKeyCredentialUserEntity(
|
$userEntity = new PublicKeyCredentialUserEntity(
|
||||||
@ -49,17 +45,44 @@ class UserWebAuthnController extends BaseController
|
|||||||
return WebAuthnService::createRequestOptions($userEntity, $challenge);
|
return WebAuthnService::createRequestOptions($userEntity, $challenge);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login_options(Request $request): PublicKeyCredentialRequestOptions
|
public function login_options(Request $request)
|
||||||
{
|
{
|
||||||
$challenge = random_bytes(32);
|
$challenge = random_bytes(32);
|
||||||
$request->session()->put("webauthn_login_challenge", $challenge);
|
$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(
|
$publicKeyCredentialRequestOptions = new PublicKeyCredentialRequestOptions(
|
||||||
$challenge
|
$challenge
|
||||||
);
|
);
|
||||||
$publicKeyCredentialRequestOptions->setUserVerification(
|
$publicKeyCredentialRequestOptions->setUserVerification(
|
||||||
PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_REQUIRED
|
PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_REQUIRED
|
||||||
);
|
);
|
||||||
$publicKeyCredentialRequestOptions->allowCredentials([]);
|
$publicKeyCredentialSources = WebAuthnService::getPublicKeyCredentialSourceRepository()->findAllForUserEntity(
|
||||||
|
new PublicKeyCredentialUserEntity("", $userHandle, "")
|
||||||
|
);
|
||||||
|
array_map(function ($source) use ($publicKeyCredentialRequestOptions) {
|
||||||
|
$publicKeyCredentialRequestOptions->allowCredential($source->getPublicKeyCredentialDescriptor());
|
||||||
|
} ,$publicKeyCredentialSources);
|
||||||
return $publicKeyCredentialRequestOptions;
|
return $publicKeyCredentialRequestOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +143,24 @@ class UserWebAuthnController extends BaseController
|
|||||||
$publicKeyCredentialRequestOptions = new PublicKeyCredentialRequestOptions(
|
$publicKeyCredentialRequestOptions = new PublicKeyCredentialRequestOptions(
|
||||||
$request->session()->remove("webauthn_login_challenge")
|
$request->session()->remove("webauthn_login_challenge")
|
||||||
);
|
);
|
||||||
|
$publicKeyCredentialSources = WebAuthnService::getPublicKeyCredentialSourceRepository()->findAllForUserEntity(
|
||||||
|
new PublicKeyCredentialUserEntity("", "0", "")
|
||||||
|
);
|
||||||
$publicKeyCredential = WebAuthnService::getPublicKeyCredentialLoader()->loadArray($request->json()->all());
|
$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();
|
$authenticatorAssertionResponse = $publicKeyCredential->getResponse();
|
||||||
if (!$authenticatorAssertionResponse instanceof AuthenticatorAssertionResponse) {
|
if (!$authenticatorAssertionResponse instanceof AuthenticatorAssertionResponse) {
|
||||||
//e.g. process here with a redirection to the public key login/MFA page.
|
//e.g. process here with a redirection to the public key login/MFA page.
|
||||||
@ -136,7 +176,7 @@ class UserWebAuthnController extends BaseController
|
|||||||
$authenticatorAssertionResponse,
|
$authenticatorAssertionResponse,
|
||||||
$publicKeyCredentialRequestOptions,
|
$publicKeyCredentialRequestOptions,
|
||||||
ServerRequest::fromGlobals(),
|
ServerRequest::fromGlobals(),
|
||||||
$authenticatorAssertionResponse->getUserHandle(),
|
$userHandle,
|
||||||
["localhost"]
|
["localhost"]
|
||||||
);
|
);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\WebAuthn\Repository;
|
namespace App\WebAuthn\Repository;
|
||||||
|
|
||||||
use App\Models\WebauthnCredential;
|
use App\Models\WebauthnCredential;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Webauthn\PublicKeyCredentialSource;
|
use Webauthn\PublicKeyCredentialSource;
|
||||||
use Webauthn\PublicKeyCredentialSourceRepository;
|
use Webauthn\PublicKeyCredentialSourceRepository;
|
||||||
use Webauthn\PublicKeyCredentialUserEntity;
|
use Webauthn\PublicKeyCredentialUserEntity;
|
||||||
@ -52,18 +53,18 @@ class PublicKeyCredentialSourceRepositoryImpl implements PublicKeyCredentialSour
|
|||||||
/**
|
/**
|
||||||
* @var WebauthnCredential
|
* @var WebauthnCredential
|
||||||
*/
|
*/
|
||||||
return WebauthnCredential::query()->where(function ($query) use ($publicKeyCredentialId) {
|
return WebauthnCredential::query()->where(function (Builder $query) use ($publicKeyCredentialId) {
|
||||||
$query->where("credential_id", "=", base64_encode($publicKeyCredentialId));
|
$query->where("credential_id", "=", base64_encode($publicKeyCredentialId));
|
||||||
})->first();
|
})->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function findAllModelByTypeFree(): array
|
private function findAllModelByTypeFree(): array
|
||||||
{
|
{
|
||||||
return WebauthnCredential::query()->where("type_free", "=", "1")->get()->toArray();
|
return WebauthnCredential::query()->where("type_free", "=", "1")->get()->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function findAllModelByUserId(string $userId): array
|
private function findAllModelByUserId(string $userId): array
|
||||||
{
|
{
|
||||||
return WebauthnCredential::query()->where("user_id", "=", $userId)->get()->toArray();
|
return WebauthnCredential::query()->where("user_id", "=", $userId)->get()->all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,16 @@ const webauthn_login = useLogin({
|
|||||||
window.webauthn_register = webauthn_register;
|
window.webauthn_register = webauthn_register;
|
||||||
window.webauthn_login = webauthn_login;
|
window.webauthn_login = webauthn_login;
|
||||||
(function() {
|
(function() {
|
||||||
const loginForm = window.document.getElementById("webauthn_login_form");
|
const button = window.document.getElementById("do_webauthn_login");
|
||||||
console.log(loginForm)
|
if (button) {
|
||||||
if (loginForm) {
|
button.addEventListener("click", function (e) {
|
||||||
loginForm.addEventListener("submit", function (e) {
|
const loginForm = window.document.getElementById("webauthn_login_form");
|
||||||
|
if (!loginForm) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const formData = new FormData(loginForm)
|
const formData = new FormData(loginForm)
|
||||||
webauthn_login(formData)
|
webauthn_login({
|
||||||
|
username: formData.get("username")
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// 成功登录
|
// 成功登录
|
||||||
window.location.href = "/"
|
window.location.href = "/"
|
||||||
|
@ -4,10 +4,11 @@
|
|||||||
<title>登录</title>
|
<title>登录</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link href="{{ mix('/css/app.css') }}" rel="stylesheet"/>
|
<link href="{{ mix('/css/app.css') }}" rel="stylesheet"/>
|
||||||
|
<script src="{{ mix('/js/manifest.js') }}" rel="script"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@include("common.header")
|
@include("common.header")
|
||||||
<form class="w-full lg:w-1/2 border-2 mx-auto my-2" action="" method="post" enctype="multipart/form-data">
|
<form class="w-full lg:w-1/2 border-2 mx-auto my-2" id="webauthn_login_form" action="" method="post" enctype="multipart/form-data">
|
||||||
<div class="block text-lg font-bold ml-4 mt-2">登录</div>
|
<div class="block text-lg font-bold ml-4 mt-2">登录</div>
|
||||||
@csrf
|
@csrf
|
||||||
<label class="block my-2">
|
<label class="block my-2">
|
||||||
@ -25,11 +26,12 @@
|
|||||||
@include("common.form_error")
|
@include("common.form_error")
|
||||||
<div class="block my-2 text-center">
|
<div class="block my-2 text-center">
|
||||||
<input class="px-6 py-2 inline-block rounded-full bg-cyan-600 text-white" type="submit" value="登录">
|
<input class="px-6 py-2 inline-block rounded-full bg-cyan-600 text-white" type="submit" value="登录">
|
||||||
<a class="px-6 py-2 inline-block rounded-full bg-cyan-600 text-white" href="{{ url(route("login.webauthn")) }}">
|
<a class="px-6 py-2 inline-block rounded-full bg-cyan-600 text-white" id="do_webauthn_login" href="javascript: void 0">
|
||||||
免输入登录<sup class="text-xs bg-yellow-600 text-white">Alpha</sup>
|
免输入登录<sup class="text-xs bg-yellow-600 text-white">Alpha</sup>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@include("common.footer")
|
@include("common.footer")
|
||||||
</body>
|
</body>
|
||||||
|
<script src="{{ mix('/js/webauthn.js') }}" rel="script"></script>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
<html lang="zh">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>WebAuthn登录</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<link href="{{ mix('/css/app.css') }}" rel="stylesheet"/>
|
|
||||||
<script src="{{ mix('/js/manifest.js') }}" rel="script"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
@include("common.header")
|
|
||||||
<form class="w-full lg:w-1/2 border-2 mx-auto my-2" id="webauthn_login_form" action="javascript:void 0" method="post" enctype="multipart/form-data">
|
|
||||||
<div class="block text-lg font-bold ml-4 mt-2">登录</div>
|
|
||||||
@csrf
|
|
||||||
<label class="block my-2">
|
|
||||||
用户名
|
|
||||||
<input class="form-input border-0 border-b-2 w-full" type="text" name="username" value="{{ old("username") }}" placeholder="用户名或邮箱">
|
|
||||||
</label>
|
|
||||||
@include("common.form_error")
|
|
||||||
<div class="block my-2 text-center">
|
|
||||||
<input class="px-6 py-2 inline-block rounded-full bg-cyan-600 text-white" type="submit" value="免输入登录">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
@include("common.footer")
|
|
||||||
</body>
|
|
||||||
<script src="{{ mix('/js/webauthn.js') }}" rel="script"></script>
|
|
||||||
</html>
|
|
@ -26,7 +26,6 @@ Route::post('/upload', ["\\App\\Http\\Controllers\\FileController","upload"]);
|
|||||||
// 用户部分
|
// 用户部分
|
||||||
Route::get('/login', ["\\App\\Http\\Controllers\\UserController", "login_page"])->name("login");
|
Route::get('/login', ["\\App\\Http\\Controllers\\UserController", "login_page"])->name("login");
|
||||||
Route::post('/login', ["\\App\\Http\\Controllers\\UserController", "authenticate"])->name("login.submit");
|
Route::post('/login', ["\\App\\Http\\Controllers\\UserController", "authenticate"])->name("login.submit");
|
||||||
Route::get('/login/webauthn/', ["\\App\\Http\\Controllers\\UserWebAuthnController", "webauthn_login"])->name("login.webauthn");
|
|
||||||
Route::post("/login/webauthn/options", ["\\App\\Http\\Controllers\\UserWebAuthnController", "login_options"])->name("login.webauthn.options");
|
Route::post("/login/webauthn/options", ["\\App\\Http\\Controllers\\UserWebAuthnController", "login_options"])->name("login.webauthn.options");
|
||||||
Route::post("/login/webauthn/", ["\\App\\Http\\Controllers\\UserWebAuthnController", "login_validate"])->name("login.webauthn.submit");
|
Route::post("/login/webauthn/", ["\\App\\Http\\Controllers\\UserWebAuthnController", "login_validate"])->name("login.webauthn.submit");
|
||||||
Route::get('/register', ["\\App\\Http\\Controllers\\UserController", "register_page"])->name("register");
|
Route::get('/register', ["\\App\\Http\\Controllers\\UserController", "register_page"])->name("register");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user