Fix PasskeysController with new webauthn library version
This commit is contained in:
parent
126bb29ae2
commit
cf978cd749
2 changed files with 68 additions and 40 deletions
|
@ -4,8 +4,6 @@ APP_KEY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_TIMEZONE=UTC
|
APP_TIMEZONE=UTC
|
||||||
APP_URL=https://example.com
|
APP_URL=https://example.com
|
||||||
APP_LONGURL=example.com
|
|
||||||
APP_SHORTURL=examp.le
|
|
||||||
|
|
||||||
APP_LOCALE=en
|
APP_LOCALE=en
|
||||||
APP_FALLBACK_LOCALE=en
|
APP_FALLBACK_LOCALE=en
|
||||||
|
|
|
@ -18,6 +18,7 @@ use Illuminate\Support\Facades\App;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use ParagonIE\ConstantTime\Base64UrlSafe;
|
use ParagonIE\ConstantTime\Base64UrlSafe;
|
||||||
|
use Random\RandomException;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Webauthn\AttestationStatement\AttestationStatementSupportManager;
|
use Webauthn\AttestationStatement\AttestationStatementSupportManager;
|
||||||
use Webauthn\AttestationStatement\NoneAttestationStatementSupport;
|
use Webauthn\AttestationStatement\NoneAttestationStatementSupport;
|
||||||
|
@ -52,22 +53,26 @@ class PasskeysController extends Controller
|
||||||
return view('admin.passkeys.index', compact('passkeys'));
|
return view('admin.passkeys.index', compact('passkeys'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCreateOptions(): JsonResponse
|
/**
|
||||||
|
* @throws RandomException
|
||||||
|
* @throws \JsonException
|
||||||
|
*/
|
||||||
|
public function getCreateOptions(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
// RP Entity i.e. the application
|
// RP Entity i.e. the application
|
||||||
$rpEntity = PublicKeyCredentialRpEntity::create(
|
$rpEntity = PublicKeyCredentialRpEntity::create(
|
||||||
config('app.name'),
|
name: config('app.name'),
|
||||||
config('app.url'),
|
id: config('app.url'),
|
||||||
);
|
);
|
||||||
|
|
||||||
// User Entity
|
// User Entity
|
||||||
$userEntity = PublicKeyCredentialUserEntity::create(
|
$userEntity = PublicKeyCredentialUserEntity::create(
|
||||||
$user->name,
|
name: $user->name,
|
||||||
(string) $user->id,
|
id: (string) $user->id,
|
||||||
$user->name,
|
displayName: $user->name,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Challenge
|
// Challenge
|
||||||
|
@ -85,25 +90,38 @@ class PasskeysController extends Controller
|
||||||
$authenticatorSelectionCriteria = AuthenticatorSelectionCriteria::create(
|
$authenticatorSelectionCriteria = AuthenticatorSelectionCriteria::create(
|
||||||
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED,
|
userVerification: AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED,
|
||||||
residentKey: AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_REQUIRED,
|
residentKey: AuthenticatorSelectionCriteria::RESIDENT_KEY_REQUIREMENT_REQUIRED,
|
||||||
requireResidentKey: true,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$options = PublicKeyCredentialCreationOptions::create(
|
$publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::create(
|
||||||
$rpEntity,
|
rp: $rpEntity,
|
||||||
$userEntity,
|
user: $userEntity,
|
||||||
$challenge,
|
challenge: $challenge,
|
||||||
$pubKeyCredParams,
|
pubKeyCredParams: $pubKeyCredParams,
|
||||||
authenticatorSelection: $authenticatorSelectionCriteria,
|
authenticatorSelection: $authenticatorSelectionCriteria,
|
||||||
attestation: PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE
|
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
|
public function create(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
|
@ -111,17 +129,17 @@ class PasskeysController extends Controller
|
||||||
|
|
||||||
$publicKeyCredentialCreationOptionsData = session('create_options');
|
$publicKeyCredentialCreationOptionsData = session('create_options');
|
||||||
// Unset session data to mitigate replay attacks
|
// Unset session data to mitigate replay attacks
|
||||||
session()->forget('create_options');
|
$request->session()->forget('create_options');
|
||||||
if (empty($publicKeyCredentialCreationOptionsData)) {
|
if (empty($publicKeyCredentialCreationOptionsData)) {
|
||||||
throw new WebAuthnException('No public key credential request options found');
|
throw new WebAuthnException('No public key credential request options found');
|
||||||
}
|
}
|
||||||
|
|
||||||
$attestationStatementSupportManager = new AttestationStatementSupportManager;
|
$attestationStatementSupportManager = new AttestationStatementSupportManager;
|
||||||
$attestationStatementSupportManager->add(new NoneAttestationStatementSupport);
|
$attestationStatementSupportManager->add(new NoneAttestationStatementSupport);
|
||||||
|
$webauthnSerializerFactory = new WebauthnSerializerFactory(
|
||||||
$webauthnSerializer = (new WebauthnSerializerFactory(
|
attestationStatementSupportManager: $attestationStatementSupportManager
|
||||||
$attestationStatementSupportManager
|
);
|
||||||
))->create();
|
$webauthnSerializer = $webauthnSerializerFactory->create();
|
||||||
|
|
||||||
$publicKeyCredential = $webauthnSerializer->deserialize(
|
$publicKeyCredential = $webauthnSerializer->deserialize(
|
||||||
json_encode($request->all(), JSON_THROW_ON_ERROR),
|
json_encode($request->all(), JSON_THROW_ON_ERROR),
|
||||||
|
@ -146,11 +164,11 @@ class PasskeysController extends Controller
|
||||||
$ceremonyStepManagerFactory->setExtensionOutputCheckerHandler(
|
$ceremonyStepManagerFactory->setExtensionOutputCheckerHandler(
|
||||||
ExtensionOutputCheckerHandler::create()
|
ExtensionOutputCheckerHandler::create()
|
||||||
);
|
);
|
||||||
$securedRelyingPartyId = [];
|
$allowedOrigins = [];
|
||||||
if (App::environment('local', 'development')) {
|
if (App::environment('local', 'development')) {
|
||||||
$securedRelyingPartyId = [config('app.url')];
|
$allowedOrigins = [config('app.url')];
|
||||||
}
|
}
|
||||||
$ceremonyStepManagerFactory->setSecuredRelyingPartyId($securedRelyingPartyId);
|
$ceremonyStepManagerFactory->setAllowedOrigins($allowedOrigins);
|
||||||
|
|
||||||
$authenticatorAttestationResponseValidator = AuthenticatorAttestationResponseValidator::create(
|
$authenticatorAttestationResponseValidator = AuthenticatorAttestationResponseValidator::create(
|
||||||
ceremonyStepManager: $ceremonyStepManagerFactory->creationCeremony()
|
ceremonyStepManager: $ceremonyStepManagerFactory->creationCeremony()
|
||||||
|
@ -165,8 +183,7 @@ class PasskeysController extends Controller
|
||||||
$publicKeyCredentialSource = $authenticatorAttestationResponseValidator->check(
|
$publicKeyCredentialSource = $authenticatorAttestationResponseValidator->check(
|
||||||
authenticatorAttestationResponse: $publicKeyCredential->response,
|
authenticatorAttestationResponse: $publicKeyCredential->response,
|
||||||
publicKeyCredentialCreationOptions: $publicKeyCredentialCreationOptions,
|
publicKeyCredentialCreationOptions: $publicKeyCredentialCreationOptions,
|
||||||
request: config('app.url'),
|
host: config('app.url')
|
||||||
securedRelyingPartyId: $securedRelyingPartyId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$user->passkey()->create([
|
$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(
|
$publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create(
|
||||||
challenge: random_bytes(16),
|
challenge: random_bytes(16),
|
||||||
userVerification: PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_REQUIRED
|
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);
|
return JsonResponse::fromJsonString($publicKeyCredentialRequestOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \JsonException
|
||||||
|
*/
|
||||||
public function login(Request $request): JsonResponse
|
public function login(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$requestOptions = session('request_options');
|
$requestOptions = session('request_options');
|
||||||
session()->forget('request_options');
|
$request->session()->forget('request_options');
|
||||||
|
|
||||||
if (empty($requestOptions)) {
|
if (empty($requestOptions)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
|
@ -209,9 +239,10 @@ class PasskeysController extends Controller
|
||||||
$attestationStatementSupportManager = new AttestationStatementSupportManager;
|
$attestationStatementSupportManager = new AttestationStatementSupportManager;
|
||||||
$attestationStatementSupportManager->add(new NoneAttestationStatementSupport);
|
$attestationStatementSupportManager->add(new NoneAttestationStatementSupport);
|
||||||
|
|
||||||
$webauthnSerializer = (new WebauthnSerializerFactory(
|
$webauthnSerializerFactory = new WebauthnSerializerFactory(
|
||||||
$attestationStatementSupportManager
|
attestationStatementSupportManager: $attestationStatementSupportManager
|
||||||
))->create();
|
);
|
||||||
|
$webauthnSerializer = $webauthnSerializerFactory->create();
|
||||||
|
|
||||||
$publicKeyCredential = $webauthnSerializer->deserialize(
|
$publicKeyCredential = $webauthnSerializer->deserialize(
|
||||||
json_encode($request->all(), JSON_THROW_ON_ERROR),
|
json_encode($request->all(), JSON_THROW_ON_ERROR),
|
||||||
|
@ -256,11 +287,11 @@ class PasskeysController extends Controller
|
||||||
$ceremonyStepManagerFactory->setExtensionOutputCheckerHandler(
|
$ceremonyStepManagerFactory->setExtensionOutputCheckerHandler(
|
||||||
ExtensionOutputCheckerHandler::create()
|
ExtensionOutputCheckerHandler::create()
|
||||||
);
|
);
|
||||||
$securedRelyingPartyId = [];
|
$allowedOrigins = [];
|
||||||
if (App::environment('local', 'development')) {
|
if (App::environment('local', 'development')) {
|
||||||
$securedRelyingPartyId = [config('app.url')];
|
$allowedOrigins = [config('app.url')];
|
||||||
}
|
}
|
||||||
$ceremonyStepManagerFactory->setSecuredRelyingPartyId($securedRelyingPartyId);
|
$ceremonyStepManagerFactory->setAllowedOrigins($allowedOrigins);
|
||||||
|
|
||||||
$authenticatorAssertionResponseValidator = AuthenticatorAssertionResponseValidator::create(
|
$authenticatorAssertionResponseValidator = AuthenticatorAssertionResponseValidator::create(
|
||||||
ceremonyStepManager: $ceremonyStepManagerFactory->requestCeremony()
|
ceremonyStepManager: $ceremonyStepManagerFactory->requestCeremony()
|
||||||
|
@ -274,12 +305,11 @@ class PasskeysController extends Controller
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$authenticatorAssertionResponseValidator->check(
|
$authenticatorAssertionResponseValidator->check(
|
||||||
credentialId: $publicKeyCredentialSource,
|
publicKeyCredentialSource: $publicKeyCredentialSource,
|
||||||
authenticatorAssertionResponse: $publicKeyCredential->response,
|
authenticatorAssertionResponse: $publicKeyCredential->response,
|
||||||
publicKeyCredentialRequestOptions: $publicKeyCredentialRequestOptions,
|
publicKeyCredentialRequestOptions: $publicKeyCredentialRequestOptions,
|
||||||
request: config('app.url'),
|
host: config('app.url'),
|
||||||
userHandle: null,
|
userHandle: null,
|
||||||
securedRelyingPartyId: $securedRelyingPartyId,
|
|
||||||
);
|
);
|
||||||
} catch (Throwable) {
|
} catch (Throwable) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
|
|
Loading…
Add table
Reference in a new issue