From cf978cd749b2ccc35d7bdcc1d12f99cb0fc40e12 Mon Sep 17 00:00:00 2001 From: Jonny Barnes Date: Mon, 7 Apr 2025 19:44:13 +0100 Subject: [PATCH] Fix PasskeysController with new webauthn library version --- .env.example | 2 - .../Controllers/Admin/PasskeysController.php | 106 +++++++++++------- 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/.env.example b/.env.example index 43a5f376..4eb61db5 100644 --- a/.env.example +++ b/.env.example @@ -4,8 +4,6 @@ APP_KEY= APP_DEBUG=true APP_TIMEZONE=UTC APP_URL=https://example.com -APP_LONGURL=example.com -APP_SHORTURL=examp.le APP_LOCALE=en APP_FALLBACK_LOCALE=en diff --git a/app/Http/Controllers/Admin/PasskeysController.php b/app/Http/Controllers/Admin/PasskeysController.php index b9a67f64..47ea5f0c 100644 --- a/app/Http/Controllers/Admin/PasskeysController.php +++ b/app/Http/Controllers/Admin/PasskeysController.php @@ -18,6 +18,7 @@ use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Auth; use Illuminate\View\View; use ParagonIE\ConstantTime\Base64UrlSafe; +use Random\RandomException; use Throwable; use Webauthn\AttestationStatement\AttestationStatementSupportManager; use Webauthn\AttestationStatement\NoneAttestationStatementSupport; @@ -52,22 +53,26 @@ class PasskeysController extends Controller return view('admin.passkeys.index', compact('passkeys')); } - public function getCreateOptions(): JsonResponse + /** + * @throws RandomException + * @throws \JsonException + */ + public function getCreateOptions(Request $request): JsonResponse { /** @var User $user */ $user = auth()->user(); // RP Entity i.e. the application $rpEntity = PublicKeyCredentialRpEntity::create( - config('app.name'), - config('app.url'), + name: config('app.name'), + id: config('app.url'), ); // User Entity $userEntity = PublicKeyCredentialUserEntity::create( - $user->name, - (string) $user->id, - $user->name, + name: $user->name, + id: (string) $user->id, + displayName: $user->name, ); // Challenge @@ -85,25 +90,38 @@ class PasskeysController extends Controller $authenticatorSelectionCriteria = AuthenticatorSelectionCriteria::create( userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED, residentKey: AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_REQUIRED, - requireResidentKey: true, ); - $options = PublicKeyCredentialCreationOptions::create( - $rpEntity, - $userEntity, - $challenge, - $pubKeyCredParams, + $publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create( + rp: $rpEntity, + user: $userEntity, + challenge: $challenge, + pubKeyCredParams: $pubKeyCredParams, authenticatorSelection: $authenticatorSelectionCriteria, attestation: PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE ); - $options = json_encode($options, JSON_THROW_ON_ERROR); + $attestationStatementSupportManager = new AttestationStatementSupportManager; + $attestationStatementSupportManager->add(new NoneAttestationStatementSupport); + $webauthnSerializerFactory = new WebauthnSerializerFactory( + attestationStatementSupportManager: $attestationStatementSupportManager + ); + $webauthnSerializer = $webauthnSerializerFactory->create(); + $publicKeyCredentialCreationOptions = $webauthnSerializer->serialize( + data: $publicKeyCredentialCreationOptions, + format: 'json' + ); - session(['create_options' => $options]); + $request->session()->put('create_options', $publicKeyCredentialCreationOptions); - return JsonResponse::fromJsonString($options); + return JsonResponse::fromJsonString($publicKeyCredentialCreationOptions); } + /** + * @throws Throwable + * @throws WebauthnException + * @throws \JsonException + */ public function create(Request $request): JsonResponse { /** @var User $user */ @@ -111,17 +129,17 @@ class PasskeysController extends Controller $publicKeyCredentialCreationOptionsData = session('create_options'); // Unset session data to mitigate replay attacks - session()->forget('create_options'); + $request->session()->forget('create_options'); if (empty($publicKeyCredentialCreationOptionsData)) { throw new WebAuthnException('No public key credential request options found'); } $attestationStatementSupportManager = new AttestationStatementSupportManager; $attestationStatementSupportManager->add(new NoneAttestationStatementSupport); - - $webauthnSerializer = (new WebauthnSerializerFactory( - $attestationStatementSupportManager - ))->create(); + $webauthnSerializerFactory = new WebauthnSerializerFactory( + attestationStatementSupportManager: $attestationStatementSupportManager + ); + $webauthnSerializer = $webauthnSerializerFactory->create(); $publicKeyCredential = $webauthnSerializer->deserialize( json_encode($request->all(), JSON_THROW_ON_ERROR), @@ -146,11 +164,11 @@ class PasskeysController extends Controller $ceremonyStepManagerFactory->setExtensionOutputCheckerHandler( ExtensionOutputCheckerHandler::create() ); - $securedRelyingPartyId = []; + $allowedOrigins = []; if (App::environment('local', 'development')) { - $securedRelyingPartyId = [config('app.url')]; + $allowedOrigins = [config('app.url')]; } - $ceremonyStepManagerFactory->setSecuredRelyingPartyId($securedRelyingPartyId); + $ceremonyStepManagerFactory->setAllowedOrigins($allowedOrigins); $authenticatorAttestationResponseValidator = AuthenticatorAttestationResponseValidator::create( ceremonyStepManager: $ceremonyStepManagerFactory->creationCeremony() @@ -165,8 +183,7 @@ class PasskeysController extends Controller $publicKeyCredentialSource = $authenticatorAttestationResponseValidator->check( authenticatorAttestationResponse: $publicKeyCredential->response, publicKeyCredentialCreationOptions: $publicKeyCredentialCreationOptions, - request: config('app.url'), - securedRelyingPartyId: $securedRelyingPartyId, + host: config('app.url') ); $user->passkey()->create([ @@ -180,24 +197,37 @@ class PasskeysController extends Controller ]); } - public function getRequestOptions(): JsonResponse + /** + * @throws RandomException + * @throws \JsonException + */ + public function getRequestOptions(Request $request): JsonResponse { $publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create( challenge: random_bytes(16), userVerification: PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_REQUIRED ); - $publicKeyCredentialRequestOptions = json_encode($publicKeyCredentialRequestOptions, JSON_THROW_ON_ERROR); + $attestationStatementSupportManager = AttestationStatementSupportManager::create(); + $attestationStatementSupportManager->add(NoneAttestationStatementSupport::create()); + $factory = new WebauthnSerializerFactory( + attestationStatementSupportManager: $attestationStatementSupportManager + ); + $serializer = $factory->create(); + $publicKeyCredentialRequestOptions = $serializer->serialize(data: $publicKeyCredentialRequestOptions, format: 'json'); - session(['request_options' => $publicKeyCredentialRequestOptions]); + $request->session()->put('request_options', $publicKeyCredentialRequestOptions); return JsonResponse::fromJsonString($publicKeyCredentialRequestOptions); } + /** + * @throws \JsonException + */ public function login(Request $request): JsonResponse { $requestOptions = session('request_options'); - session()->forget('request_options'); + $request->session()->forget('request_options'); if (empty($requestOptions)) { return response()->json([ @@ -209,9 +239,10 @@ class PasskeysController extends Controller $attestationStatementSupportManager = new AttestationStatementSupportManager; $attestationStatementSupportManager->add(new NoneAttestationStatementSupport); - $webauthnSerializer = (new WebauthnSerializerFactory( - $attestationStatementSupportManager - ))->create(); + $webauthnSerializerFactory = new WebauthnSerializerFactory( + attestationStatementSupportManager: $attestationStatementSupportManager + ); + $webauthnSerializer = $webauthnSerializerFactory->create(); $publicKeyCredential = $webauthnSerializer->deserialize( json_encode($request->all(), JSON_THROW_ON_ERROR), @@ -256,11 +287,11 @@ class PasskeysController extends Controller $ceremonyStepManagerFactory->setExtensionOutputCheckerHandler( ExtensionOutputCheckerHandler::create() ); - $securedRelyingPartyId = []; + $allowedOrigins = []; if (App::environment('local', 'development')) { - $securedRelyingPartyId = [config('app.url')]; + $allowedOrigins = [config('app.url')]; } - $ceremonyStepManagerFactory->setSecuredRelyingPartyId($securedRelyingPartyId); + $ceremonyStepManagerFactory->setAllowedOrigins($allowedOrigins); $authenticatorAssertionResponseValidator = AuthenticatorAssertionResponseValidator::create( ceremonyStepManager: $ceremonyStepManagerFactory->requestCeremony() @@ -274,12 +305,11 @@ class PasskeysController extends Controller try { $authenticatorAssertionResponseValidator->check( - credentialId: $publicKeyCredentialSource, + publicKeyCredentialSource: $publicKeyCredentialSource, authenticatorAssertionResponse: $publicKeyCredential->response, publicKeyCredentialRequestOptions: $publicKeyCredentialRequestOptions, - request: config('app.url'), + host: config('app.url'), userHandle: null, - securedRelyingPartyId: $securedRelyingPartyId, ); } catch (Throwable) { return response()->json([