Skip to content

Commit 01395e6

Browse files
duncanmccleanclaudejasonvarga
authored
[6.x] Fix creating passkeys with JSON session serialization (#14448)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Jason Varga <jason@pixelfear.com>
1 parent afc6d5b commit 01395e6

File tree

4 files changed

+13
-12
lines changed

4 files changed

+13
-12
lines changed

src/Auth/WebAuthn/WebAuthn.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function prepareAssertion(): PublicKeyCredentialRequestOptions
3131
{
3232
$challenge = random_bytes(32);
3333

34-
session()->put('webauthn.challenge', $challenge);
34+
session()->put('webauthn.challenge', base64_encode($challenge));
3535

3636
return $this->getRequestOptions($challenge);
3737
}
@@ -42,7 +42,7 @@ public function validateAssertion(User $user, array $credentials): bool
4242

4343
$passkey = $this->getPasskey($user, $publicKeyCredential);
4444

45-
$options = $this->getRequestOptions(session()->pull('webauthn.challenge'));
45+
$options = $this->getRequestOptions(base64_decode(session()->pull('webauthn.challenge')));
4646

4747
$publicKeyCredentialSource = $this->assertionResponseValidator->check(
4848
$passkey->credential(),
@@ -79,7 +79,7 @@ public function prepareAttestation(User $user): PublicKeyCredentialCreationOptio
7979
{
8080
$challenge = random_bytes(16);
8181

82-
session()->put('webauthn.challenge', $challenge);
82+
session()->put('webauthn.challenge', base64_encode($challenge));
8383

8484
return $this->getCreationOptions($user, $challenge);
8585
}
@@ -92,7 +92,7 @@ public function validateAttestation(User $user, array $credentials, string $name
9292
throw new Exception(__('Invalid credentials.'));
9393
}
9494

95-
$options = $this->getCreationOptions($user, session()->pull('webauthn.challenge'));
95+
$options = $this->getCreationOptions($user, base64_decode(session()->pull('webauthn.challenge')));
9696

9797
$publicKeyCredentialSource = $this->attestationResponseValidator->check(
9898
$publicKeyCredential->response,

tests/Auth/WebAuthn/WebAuthnTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public function it_prepares_assertion_with_challenge()
5656

5757
$this->assertInstanceOf(PublicKeyCredentialRequestOptions::class, $options);
5858
$this->assertNotNull(session('webauthn.challenge'));
59-
$this->assertEquals(32, strlen(session('webauthn.challenge')));
59+
$this->assertEquals(32, strlen(base64_decode(session('webauthn.challenge'))));
6060
$this->assertEquals(PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_REQUIRED, $options->userVerification);
6161
}
6262

@@ -84,11 +84,11 @@ public function it_stores_challenge_in_session()
8484
// Challenge should be stored in session for later verification
8585
$storedChallenge = session('webauthn.challenge');
8686
$this->assertNotNull($storedChallenge);
87-
$this->assertEquals(32, strlen($storedChallenge));
87+
$this->assertEquals(32, strlen(base64_decode($storedChallenge)));
8888

8989
// The challenge in the options should be the same binary value
9090
// (base64url encoding happens during serialization, not in the object)
91-
$this->assertEquals($storedChallenge, $options->challenge);
91+
$this->assertEquals(base64_decode($storedChallenge), $options->challenge);
9292
}
9393

9494
#[Test]
@@ -180,7 +180,7 @@ public function it_validates_assertion_successfully()
180180

181181
$credentials = ['id' => 'credential-id', 'rawId' => 'raw-id', 'response' => [], 'type' => 'public-key'];
182182
$challenge = random_bytes(32);
183-
session()->put('webauthn.challenge', $challenge);
183+
session()->put('webauthn.challenge', base64_encode($challenge));
184184

185185
// Create real objects
186186
$publicKeyCredential = new PublicKeyCredential(
@@ -219,6 +219,7 @@ public function it_validates_assertion_successfully()
219219
$this->mockAssertionValidator
220220
->shouldReceive('check')
221221
->once()
222+
->withArgs(fn ($credential, $response, $options) => $options->challenge === $challenge)
222223
->andReturn($updatedCredentialSource);
223224

224225
$result = $this->webauthn->validateAssertion($mockUser, $credentials);
@@ -234,7 +235,6 @@ public function it_throws_exception_when_no_matching_passkey()
234235
$user->save();
235236

236237
$credentials = ['id' => 'credential-id', 'rawId' => 'raw-id', 'response' => [], 'type' => 'public-key'];
237-
session()->put('webauthn.challenge', random_bytes(32));
238238

239239
$publicKeyCredential = new PublicKeyCredential(
240240
'public-key',

tests/Feature/Auth/PasskeyLoginTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ public function it_stores_challenge_in_session_for_later_verification()
6565
// Verify the session has been populated
6666
$this->assertNotNull(session('webauthn.challenge'));
6767

68-
// The session challenge is the binary form (32 bytes)
68+
// The session challenge is base64 encoded (32 bytes -> 44 chars)
6969
// The response challenge is the base64url encoded form
70-
$this->assertEquals(32, strlen(session('webauthn.challenge')));
70+
$this->assertEquals(44, strlen(session('webauthn.challenge')));
71+
$this->assertEquals(32, strlen(base64_decode(session('webauthn.challenge'))));
7172
$this->assertIsString($responseChallenge);
7273
}
7374

tests/Feature/Auth/StorePasskeyTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function it_stores_challenge_in_session_during_creation()
6969
$responseChallenge = $response->json('challenge');
7070

7171
$this->assertNotNull(session('webauthn.challenge'));
72-
$this->assertEquals(16, strlen(session('webauthn.challenge')));
72+
$this->assertEquals(16, strlen(base64_decode(session('webauthn.challenge'))));
7373
$this->assertIsString($responseChallenge);
7474
}
7575

0 commit comments

Comments
 (0)