Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af04066dd0 | ||
|
|
93d12d12c9 | ||
|
|
73e0e6c8ee | ||
|
|
ed68058bd8 | ||
|
|
f784f601d4 | ||
|
|
5ac8f14103 | ||
|
|
7f4465a4ef | ||
|
|
0b39038873 | ||
|
|
07283858a9 | ||
|
|
b98d07c277 | ||
|
|
7f628f9a79 |
7
.github/FUNDING.yml
vendored
7
.github/FUNDING.yml
vendored
@@ -1,6 +1,3 @@
|
|||||||
# Help me support this package
|
# Let's keep it free and up to date
|
||||||
|
|
||||||
github: DarkGhostHunter
|
github: DarkGhostHunter
|
||||||
patreon: PackagesForLaravel
|
custom: "https://paypal.me/darkghosthunter"
|
||||||
ko_fi: DarkGhostHunter
|
|
||||||
custom: ['https://www.buymeacoffee.com/darkghosthunter', 'https://paypal.me/darkghosthunter']
|
|
||||||
|
|||||||
59
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
59
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,5 +1,8 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: File a bug report
|
description: |
|
||||||
|
File a bug report to be fixed.
|
||||||
|
|
||||||
|
Sponsors get priority issues, PRs, fixes and requests. Not a sponsor? [You're a just click away!](https://github.com/sponsors/DarkGhostHunter).
|
||||||
title: "[X.x] What does happen that is considered an error or bug?"
|
title: "[X.x] What does happen that is considered an error or bug?"
|
||||||
labels: ["bug"]
|
labels: ["bug"]
|
||||||
assignees:
|
assignees:
|
||||||
@@ -9,6 +12,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
Thanks for taking the time to fill out this bug report!
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
|
||||||
The more detailed this bug report is, the faster it can be reviewed and fixed.
|
The more detailed this bug report is, the faster it can be reviewed and fixed.
|
||||||
- type: input
|
- type: input
|
||||||
id: version-php-os
|
id: version-php-os
|
||||||
@@ -18,6 +22,14 @@ body:
|
|||||||
placeholder: 8.1.2 - Ubuntu 22.04 x64
|
placeholder: 8.1.2 - Ubuntu 22.04 x64
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: version-db
|
||||||
|
attributes:
|
||||||
|
label: Database
|
||||||
|
description: Exact DB version using this package, if applicable.
|
||||||
|
placeholder: MySQL 8.0.28
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
- type: input
|
- type: input
|
||||||
id: version-laravel
|
id: version-laravel
|
||||||
attributes:
|
attributes:
|
||||||
@@ -26,28 +38,12 @@ body:
|
|||||||
placeholder: 9.2.3
|
placeholder: 9.2.3
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
|
||||||
id: version-authenticator
|
|
||||||
attributes:
|
|
||||||
label: Authenticator type
|
|
||||||
description: If applicable, exact authenticator you're using.
|
|
||||||
placeholder: YubiKey 5, iPhone 7s, Samsung Galaxy S11+...
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: input
|
|
||||||
id: version-os-browser
|
|
||||||
attributes:
|
|
||||||
label: OS and Browser versions
|
|
||||||
description: If applicable, exact OS and Browser versions
|
|
||||||
placeholder: Android 12.0 - Chrome 102.0.5005.99
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: requirements
|
id: requirements
|
||||||
attributes:
|
attributes:
|
||||||
label: Have you done this?
|
label: Have you done this?
|
||||||
options:
|
options:
|
||||||
- label: I am willing to share my stack trace and logs
|
- label: I have checked my logs and I'm sure is a bug in this package.
|
||||||
required: true
|
required: true
|
||||||
- label: I can reproduce this bug in isolation (vanilla Laravel install)
|
- label: I can reproduce this bug in isolation (vanilla Laravel install)
|
||||||
required: true
|
required: true
|
||||||
@@ -58,7 +54,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Expectation
|
label: Expectation
|
||||||
description: Write what you expect to (correctly) happen.
|
description: Write what you expect to (correctly) happen.
|
||||||
placeholder: When I do this, I expect to this to happen.
|
placeholder: When I do this, I expect to happen that.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@@ -73,7 +69,7 @@ body:
|
|||||||
id: reproduction
|
id: reproduction
|
||||||
attributes:
|
attributes:
|
||||||
label: Reproduction
|
label: Reproduction
|
||||||
description: Paste the code to assert in a test, or just comment with the repository with the bug.
|
description: Paste the code to assert in a test, or just comment with the repository with the bug to download.
|
||||||
render: php
|
render: php
|
||||||
placeholder: |
|
placeholder: |
|
||||||
$test = Laragear::make()->break();
|
$test = Laragear::make()->break();
|
||||||
@@ -87,27 +83,8 @@ body:
|
|||||||
id: logs
|
id: logs
|
||||||
attributes:
|
attributes:
|
||||||
label: Stack trace & logs
|
label: Stack trace & logs
|
||||||
description: If you have a stack trace, you can copy it here. You may hide sensible information.
|
description: If you have a **full** stack trace, you can copy it here. You may hide sensible information.
|
||||||
placeholder: This is automatically formatted into code, no need for backticks.
|
placeholder: This is automatically formatted into code, no need for ``` backticks.
|
||||||
render: shell
|
render: shell
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
|
||||||
id: attestation-assertion
|
|
||||||
attributes:
|
|
||||||
label: Attestation / Assertion objects
|
|
||||||
description: If applicable, add the Attestation and Assertion objects you have debugged.
|
|
||||||
placeholder: This is automatically formatted into Javascript, no need for backticks.
|
|
||||||
render: javascript
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: dropdown
|
|
||||||
id: supporter
|
|
||||||
attributes:
|
|
||||||
label: Are you a Patreon supporter?
|
|
||||||
description: Patreon supporters get priority review, fixing and responses. Are you not? [Become one!](https://patreon.com/packagesforlaravel)
|
|
||||||
options:
|
|
||||||
- Yes, with my username
|
|
||||||
- No, don't give priority to this
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|||||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
22
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
22
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,5 +1,8 @@
|
|||||||
name: Feature request
|
name: Feature request
|
||||||
description: Suggest a feature for this package
|
description: |
|
||||||
|
Suggest a feature for this package.
|
||||||
|
|
||||||
|
Sponsors get priority issues, PRs, fixes and requests. Not a sponsor? [You're a just click away!](https://github.com/sponsors/DarkGhostHunter).
|
||||||
title: "[X.x] Add this cool feature for this package"
|
title: "[X.x] Add this cool feature for this package"
|
||||||
labels: ["enhancement"]
|
labels: ["enhancement"]
|
||||||
assignees:
|
assignees:
|
||||||
@@ -28,7 +31,12 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description
|
||||||
description: Describe how the feature works
|
description: Describe how the feature works
|
||||||
placeholder: This new feature would accomplish this, and would be cool to integrate it to the package because...
|
placeholder: |
|
||||||
|
This new feature would accomplish...
|
||||||
|
|
||||||
|
It could be implemented by doing...
|
||||||
|
|
||||||
|
And it would be cool because...
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@@ -41,13 +49,3 @@ body:
|
|||||||
render: php
|
render: php
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
|
||||||
id: supporter
|
|
||||||
attributes:
|
|
||||||
label: Are you a Patreon supporter?
|
|
||||||
description: Patreon supporters get priority review, fixing and responses. Are you not? [Become one!](https://patreon.com/packagesforlaravel)
|
|
||||||
options:
|
|
||||||
- Yes, with my username
|
|
||||||
- No, don't give priority to this
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|||||||
11
.github/PULL_REQUEST_TEMPLATE.md
vendored
11
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,23 +1,22 @@
|
|||||||
<!--
|
<!--
|
||||||
|
|
||||||
Thanks for contributing to this package! We only accept PR to the latest stable version.
|
Thanks for contributing to this package! We only accept PR to the latest stable version.
|
||||||
|
|
||||||
If you're pushing a Feature:
|
If you're pushing a Feature:
|
||||||
- Title it: "[X.x] This new feature"
|
- Title it: "[X.x] This new feature"
|
||||||
- Describe what the new feature enables
|
- Describe what the new feature enables
|
||||||
- Show a small code snippet of the new feature
|
- Show a small code snippet of the new feature
|
||||||
- Ensure it doesn't break any feature.
|
- Ensure it doesn't break backward compatibility.
|
||||||
|
|
||||||
If you're pushing a Fix:
|
If you're pushing a Fix:
|
||||||
- Title it: "[X.x] FIX: The bug name"
|
- Title it: "[X.x] FIX: The bug name"
|
||||||
- Describe how it fixes in a few words.
|
- Describe how it fixes in a few words.
|
||||||
- Ensure it doesn't break any feature.
|
- Ensure it doesn't break backward compatibility.
|
||||||
|
|
||||||
All Pull Requests run with extensive tests for stable and latest versions of PHP and Laravel.
|
All Pull Requests run with extensive tests for stable and latest versions of PHP and Laravel.
|
||||||
Ensure your tests pass or your PR may be taken down.
|
Ensure your tests pass or your PR may be taken down.
|
||||||
|
|
||||||
If you're a Patreon supporter, this PR will have priority.
|
If you're a Sponsor, this PR will have priority review.
|
||||||
Not a Patreon supporter? Become one at https://patreon.com/packagesforlaravel
|
Not a Sponsor? Become one at https://github.com/sponsors/DarkGhostHunter
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
@@ -29,5 +28,3 @@ This feature/fix allows to...
|
|||||||
```php
|
```php
|
||||||
Laragear::sample();
|
Laragear::sample();
|
||||||
```
|
```
|
||||||
|
|
||||||
<!-- You may delete this section if it's a FIX -->
|
|
||||||
|
|||||||
BIN
.github/assets/buymeacoffee.png
vendored
BIN
.github/assets/buymeacoffee.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
BIN
.github/assets/ko-fi.png
vendored
BIN
.github/assets/ko-fi.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 8.2 KiB |
BIN
.github/assets/patreon.png
vendored
BIN
.github/assets/patreon.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 5.7 KiB |
BIN
.github/assets/paypal.png
vendored
BIN
.github/assets/paypal.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 7.0 KiB |
BIN
.github/assets/support.png
vendored
Normal file
BIN
.github/assets/support.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
19
.github/workflows/php.yml
vendored
19
.github/workflows/php.yml
vendored
@@ -1,6 +1,6 @@
|
|||||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow
|
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow
|
||||||
|
|
||||||
name: "Tests"
|
name: Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -21,6 +21,7 @@ jobs:
|
|||||||
- name: "Find non-printable ASCII characters"
|
- name: "Find non-printable ASCII characters"
|
||||||
run: |
|
run: |
|
||||||
! LC_ALL=C.UTF-8 find ./src -type f -name "*.php" -print0 | xargs -0 -- grep -PHn "[^ -~]"
|
! LC_ALL=C.UTF-8 find ./src -type f -name "*.php" -print0 | xargs -0 -- grep -PHn "[^ -~]"
|
||||||
|
|
||||||
syntax_errors:
|
syntax_errors:
|
||||||
name: "1️⃣ Syntax errors"
|
name: "1️⃣ Syntax errors"
|
||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
@@ -34,10 +35,8 @@ jobs:
|
|||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: "actions/checkout@v3"
|
uses: "actions/checkout@v3"
|
||||||
|
|
||||||
- name: "Install dependencies"
|
- name: "Validate Composer configuration"
|
||||||
uses: "ramsey/composer-install@v2"
|
run: "composer validate --strict"
|
||||||
with:
|
|
||||||
dependency-versions: "highest"
|
|
||||||
|
|
||||||
- name: "Check source code for syntax errors"
|
- name: "Check source code for syntax errors"
|
||||||
run: "composer exec -- parallel-lint src/"
|
run: "composer exec -- parallel-lint src/"
|
||||||
@@ -51,6 +50,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php-version:
|
php-version:
|
||||||
|
- "8.0"
|
||||||
- "8.1"
|
- "8.1"
|
||||||
laravel-constrain:
|
laravel-constrain:
|
||||||
- "9.*"
|
- "9.*"
|
||||||
@@ -89,19 +89,14 @@ jobs:
|
|||||||
- name: "Set up PHP"
|
- name: "Set up PHP"
|
||||||
uses: "shivammathur/setup-php@v2"
|
uses: "shivammathur/setup-php@v2"
|
||||||
with:
|
with:
|
||||||
php-version: "8.1"
|
|
||||||
tools: "phpstan"
|
tools: "phpstan"
|
||||||
|
php-version: "latest"
|
||||||
|
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: "actions/checkout@v3"
|
uses: "actions/checkout@v3"
|
||||||
|
|
||||||
- name: "Validate Composer configuration"
|
|
||||||
run: "composer validate --strict"
|
|
||||||
|
|
||||||
- name: "Install dependencies"
|
- name: "Install dependencies"
|
||||||
uses: "ramsey/composer-install@v2"
|
uses: "ramsey/composer-install@v2"
|
||||||
with:
|
|
||||||
dependency-versions: "highest"
|
|
||||||
|
|
||||||
- name: "Execute static analysis"
|
- name: "Execute static analysis"
|
||||||
run: "composer exec -- phpstan analyze -l 5 src/"
|
run: "composer exec -- phpstan analyze -l 5 src/"
|
||||||
@@ -119,7 +114,7 @@ jobs:
|
|||||||
- name: "Check exported files"
|
- name: "Check exported files"
|
||||||
run: |
|
run: |
|
||||||
EXPECTED="LICENSE.md,README.md,composer.json"
|
EXPECTED="LICENSE.md,README.md,composer.json"
|
||||||
CURRENT="$(git archive HEAD | tar --list --exclude="src" --exclude="src/*" --exclude=".stubs" --exclude=".stubs/*" --exclude="stubs" --exclude="stubs/*" --exclude="lang" --exclude="lang/*" --exclude="config" --exclude="config/*" --exclude="database" --exclude="database/*" --exclude="resources" --exclude="resources/*" --exclude="routes" --exclude="routes/*" | paste -s -d ",")"
|
CURRENT="$(git archive HEAD | tar --list --exclude="src" --exclude="src/*" --exclude=".stubs" --exclude=".stubs/*" --exclude="routes" --exclude="routes/*" --exclude="stubs" --exclude="stubs/*" --exclude="lang" --exclude="lang/*" --exclude="config" --exclude="config/*" --exclude="database" --exclude="database/*" --exclude="resources" --exclude="resources/*" | paste -s -d ",")"
|
||||||
echo "CURRENT =${CURRENT}"
|
echo "CURRENT =${CURRENT}"
|
||||||
echo "EXPECTED=${EXPECTED}"
|
echo "EXPECTED=${EXPECTED}"
|
||||||
test "${CURRENT}" == "${EXPECTED}"
|
test "${CURRENT}" == "${EXPECTED}"
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -23,9 +23,9 @@ public function login(AssertedRequest $request)
|
|||||||
|
|
||||||
> You want to add two-factor authentication to your app? Check out [Laragear TwoFactor](https://github.com/Laragear/TwoFactor).
|
> You want to add two-factor authentication to your app? Check out [Laragear TwoFactor](https://github.com/Laragear/TwoFactor).
|
||||||
|
|
||||||
## Keep this package free
|
## Become a sponsor
|
||||||
|
|
||||||
[](https://patreon.com/packagesforlaravel)[](https://ko-fi.com/DarkGhostHunter)[](https://www.buymeacoffee.com/darkghosthunter)[](https://www.paypal.com/paypalme/darkghosthunter)
|
[](https://github.com/sponsors/DarkGhostHunter)
|
||||||
|
|
||||||
Your support allows me to keep this package free, up-to-date and maintainable. Alternatively, you can **[spread the word!](http://twitter.com/share?text=I%20am%20using%20this%20cool%20PHP%20package&url=https://github.com%2FLaragear%2FWebAuthn&hashtags=PHP,Laravel)**
|
Your support allows me to keep this package free, up-to-date and maintainable. Alternatively, you can **[spread the word!](http://twitter.com/share?text=I%20am%20using%20this%20cool%20PHP%20package&url=https://github.com%2FLaragear%2FWebAuthn&hashtags=PHP,Laravel)**
|
||||||
|
|
||||||
@@ -547,7 +547,7 @@ After that, you will receive the `config/webauthn.php` config file with an array
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'relaying_party' => [
|
'relying_party' => [
|
||||||
'name' => env('WEBAUTHN_NAME', env('APP_NAME')),
|
'name' => env('WEBAUTHN_NAME', env('APP_NAME')),
|
||||||
'id' => env('WEBAUTHN_ID'),
|
'id' => env('WEBAUTHN_ID'),
|
||||||
],
|
],
|
||||||
@@ -559,18 +559,18 @@ return [
|
|||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
### Relaying Party Information
|
### Relying Party Information
|
||||||
|
|
||||||
```php
|
```php
|
||||||
return [
|
return [
|
||||||
'relaying_party' => [
|
'relying_party' => [
|
||||||
'name' => env('WEBAUTHN_NAME', env('APP_NAME')),
|
'name' => env('WEBAUTHN_NAME', env('APP_NAME')),
|
||||||
'id' => env('WEBAUTHN_ID'),
|
'id' => env('WEBAUTHN_ID'),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
The _Relaying Party_ is just a way to uniquely identify your application in the user device:
|
The _Relying Party_ is just a way to uniquely identify your application in the user device:
|
||||||
|
|
||||||
* `name`: The name of the application. Defaults to the application name.
|
* `name`: The name of the application. Defaults to the application name.
|
||||||
* `id`: An unique ID the application, like the site domain. If `null`, the device may fill it internally, usually as the full domain.
|
* `id`: An unique ID the application, like the site domain. If `null`, the device may fill it internally, usually as the full domain.
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ return [
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Relaying Party
|
| Relying Party
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
|
||||||
| We will use your application information to inform the device who is the
|
| We will use your application information to inform the device who is the
|
||||||
| relaying party. While only the name is enough, you can further set the
|
| relying party. While only the name is enough, you can further set the
|
||||||
| a custom domain as ID and even an icon image data encoded as BASE64.
|
| a custom domain as ID and even an icon image data encoded as BASE64.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,14 +4,17 @@ use App\Http\Controllers\WebAuthn\WebAuthnLoginController;
|
|||||||
use App\Http\Controllers\WebAuthn\WebAuthnRegisterController;
|
use App\Http\Controllers\WebAuthn\WebAuthnRegisterController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::middleware('web')->group(static function (): void {
|
Route::middleware('web')
|
||||||
Route::post('webauthn/register/options', [WebAuthnRegisterController::class, 'options'])
|
->group(static function (): void {
|
||||||
->name('webauthn.register.options');
|
Route::controller(WebAuthnRegisterController::class)
|
||||||
Route::post('webauthn/register', [WebAuthnRegisterController::class, 'register'])
|
->group(static function (): void {
|
||||||
->name('webauthn.register');
|
Route::post('webauthn/register/options', 'options')->name('webauthn.register.options');
|
||||||
|
Route::post('webauthn/register', 'register')->name('webauthn.register');
|
||||||
Route::post('webauthn/login/options', [WebAuthnLoginController::class, 'options'])
|
});
|
||||||
->name('webauthn.login.options');
|
|
||||||
Route::post('webauthn/login', [WebAuthnLoginController::class, 'login'])
|
Route::controller(WebAuthnLoginController::class)
|
||||||
->name('webauthn.login');
|
->group(static function (): void {
|
||||||
|
Route::post('webauthn/login/options', 'options')->name('webauthn.login.options');
|
||||||
|
Route::post('webauthn/login', 'login')->name('webauthn.login');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,6 +35,6 @@ class CheckRelyingPartyHashSame extends BaseCheckRelyingPartyHashSame
|
|||||||
*/
|
*/
|
||||||
protected function relyingPartyId(AssertionValidation|AttestationValidation $validation): string
|
protected function relyingPartyId(AssertionValidation|AttestationValidation $validation): string
|
||||||
{
|
{
|
||||||
return $this->config->get('webauthn.relaying_party.id') ?? $this->config->get('app.url');
|
return $this->config->get('webauthn.relying_party.id') ?? $this->config->get('app.url');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class MakeWebAuthnCredential
|
|||||||
'alias' => $validation->request->json('response.alias'),
|
'alias' => $validation->request->json('response.alias'),
|
||||||
|
|
||||||
'counter' => $validation->attestationObject->authenticatorData->counter,
|
'counter' => $validation->attestationObject->authenticatorData->counter,
|
||||||
'rp_id' => $this->config->get('webauthn.relaying_party.id') ?? $this->config->get('app.url'),
|
'rp_id' => $this->config->get('webauthn.relying_party.id') ?? $this->config->get('app.url'),
|
||||||
'origin' => $validation->clientDataJson->origin,
|
'origin' => $validation->clientDataJson->origin,
|
||||||
'transports' => $validation->request->json('response.transports'),
|
'transports' => $validation->request->json('response.transports'),
|
||||||
'aaguid' => Uuid::fromBytes($validation->attestationObject->authenticatorData->attestedCredentialData->aaguid),
|
'aaguid' => Uuid::fromBytes($validation->attestationObject->authenticatorData->attestedCredentialData->aaguid),
|
||||||
|
|||||||
@@ -39,11 +39,11 @@ abstract class CheckRelyingPartyHashSame
|
|||||||
public function handle(AttestationValidation|AssertionValidation $validation, Closure $next): mixed
|
public function handle(AttestationValidation|AssertionValidation $validation, Closure $next): mixed
|
||||||
{
|
{
|
||||||
// This way we can get the app RP ID on attestation, and the Credential RP ID
|
// This way we can get the app RP ID on attestation, and the Credential RP ID
|
||||||
// on assertion. The credential will have the same Relaying Party ID on both
|
// on assertion. The credential will have the same Relying Party ID on both
|
||||||
// the authenticator and the application so on assertion both should match.
|
// the authenticator and the application so on assertion both should match.
|
||||||
$relayingParty = parse_url($this->relyingPartyId($validation), PHP_URL_HOST);
|
$relyingParty = parse_url($this->relyingPartyId($validation), PHP_URL_HOST);
|
||||||
|
|
||||||
if ($this->authenticatorData($validation)->hasNotSameRPIdHash($relayingParty)) {
|
if ($this->authenticatorData($validation)->hasNotSameRPIdHash($relyingParty)) {
|
||||||
static::throw($validation, 'Response has different Relying Party ID hash.');
|
static::throw($validation, 'Response has different Relying Party ID hash.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ abstract class CheckRelyingPartyIdContained
|
|||||||
public function handle(AttestationValidation|AssertionValidation $validation, Closure $next): mixed
|
public function handle(AttestationValidation|AssertionValidation $validation, Closure $next): mixed
|
||||||
{
|
{
|
||||||
if (!$host = parse_url($validation->clientDataJson->origin, PHP_URL_HOST)) {
|
if (!$host = parse_url($validation->clientDataJson->origin, PHP_URL_HOST)) {
|
||||||
static::throw($validation, 'Relaying Party ID is invalid.');
|
static::throw($validation, 'Relying Party ID is invalid.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$current = parse_url(
|
$current = parse_url(
|
||||||
$this->config->get('webauthn.relaying_party.id') ?? $this->config->get('app.url'), PHP_URL_HOST
|
$this->config->get('webauthn.relying_party.id') ?? $this->config->get('app.url'), PHP_URL_HOST
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check the host is the same or is a subdomain of the current config domain.
|
// Check the host is the same or is a subdomain of the current config domain.
|
||||||
@@ -52,6 +52,6 @@ abstract class CheckRelyingPartyIdContained
|
|||||||
return $next($validation);
|
return $next($validation);
|
||||||
}
|
}
|
||||||
|
|
||||||
static::throw($validation, 'Relaying Party ID not scoped to current.');
|
static::throw($validation, 'Relying Party ID not scoped to current.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,20 +31,19 @@ class WebAuthn
|
|||||||
*/
|
*/
|
||||||
public static function routes(): void
|
public static function routes(): void
|
||||||
{
|
{
|
||||||
Route::middleware('web')->group(static function (): void {
|
Route::middleware('web')
|
||||||
Route::post('webauthn/register/options')
|
->group(static function (): void {
|
||||||
->uses([\App\Http\Controllers\WebAuthn\WebAuthnRegisterController::class, 'options'])
|
Route::controller(\App\Http\Controllers\WebAuthn\WebAuthnRegisterController::class)
|
||||||
->name('webauthn.register.options');
|
->group(static function (): void {
|
||||||
Route::post('webauthn/register')
|
Route::post('webauthn/register/options', 'options')->name('webauthn.register.options');
|
||||||
->uses([\App\Http\Controllers\WebAuthn\WebAuthnRegisterController::class, 'register'])
|
Route::post('webauthn/register', 'register')->name('webauthn.register');
|
||||||
->name('webauthn.register');
|
});
|
||||||
|
|
||||||
Route::post('webauthn/login/options')
|
Route::controller(\App\Http\Controllers\WebAuthn\WebAuthnLoginController::class)
|
||||||
->uses([\App\Http\Controllers\WebAuthn\WebAuthnLoginController::class, 'options'])
|
->group(static function (): void {
|
||||||
->name('webauthn.login.options');
|
Route::post('webauthn/login/options', 'options')->name('webauthn.login.options');
|
||||||
Route::post('webauthn/login')
|
Route::post('webauthn/login', 'login')->name('webauthn.login');
|
||||||
->uses([\App\Http\Controllers\WebAuthn\WebAuthnLoginController::class, 'login'])
|
});
|
||||||
->name('webauthn.login');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class WebAuthnServiceProvider extends ServiceProvider
|
|||||||
if ($this->app->runningInConsole()) {
|
if ($this->app->runningInConsole()) {
|
||||||
$this->publishesMigrations(static::MIGRATIONS);
|
$this->publishesMigrations(static::MIGRATIONS);
|
||||||
$this->publishes([static::ROUTES => $this->app->basePath('routes/webauthn.php')], 'routes');
|
$this->publishes([static::ROUTES => $this->app->basePath('routes/webauthn.php')], 'routes');
|
||||||
|
$this->publishes([static::CONFIG => $this->app->configPath('webauthn.php')], 'config');
|
||||||
// @phpstan-ignore-next-line
|
// @phpstan-ignore-next-line
|
||||||
$this->publishes([static::CONTROLLERS => $this->app->path('Http/Controllers/WebAuthn')], 'controllers');
|
$this->publishes([static::CONTROLLERS => $this->app->path('Http/Controllers/WebAuthn')], 'controllers');
|
||||||
$this->publishes([static::JS => $this->app->resourcePath('js/vendor/webauthn')], 'js');
|
$this->publishes([static::JS => $this->app->resourcePath('js/vendor/webauthn')], 'js');
|
||||||
|
|||||||
@@ -457,7 +457,7 @@ class ValidationTest extends TestCase
|
|||||||
$this->request->setJson(new ParameterBag($invalid));
|
$this->request->setJson(new ParameterBag($invalid));
|
||||||
|
|
||||||
$this->expectException(AssertionException::class);
|
$this->expectException(AssertionException::class);
|
||||||
$this->expectExceptionMessage('Assertion Error: Relaying Party ID not scoped to current.');
|
$this->expectExceptionMessage('Assertion Error: Relying Party ID not scoped to current.');
|
||||||
|
|
||||||
$this->validate();
|
$this->validate();
|
||||||
}
|
}
|
||||||
@@ -477,7 +477,7 @@ class ValidationTest extends TestCase
|
|||||||
$this->request->setJson(new ParameterBag($invalid));
|
$this->request->setJson(new ParameterBag($invalid));
|
||||||
|
|
||||||
$this->expectException(AssertionException::class);
|
$this->expectException(AssertionException::class);
|
||||||
$this->expectExceptionMessage('Assertion Error: Relaying Party ID not scoped to current.');
|
$this->expectExceptionMessage('Assertion Error: Relying Party ID not scoped to current.');
|
||||||
|
|
||||||
$this->validate();
|
$this->validate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class CreatorTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_uses_relaying_party_config(): void
|
public function test_uses_relying_party_config(): void
|
||||||
{
|
{
|
||||||
config(['webauthn.relying_party' => [
|
config(['webauthn.relying_party' => [
|
||||||
'id' => 'https://foo.bar',
|
'id' => 'https://foo.bar',
|
||||||
|
|||||||
@@ -504,7 +504,7 @@ class ValidationTest extends TestCase
|
|||||||
public function test_rp_id_fails_if_not_equal(): void
|
public function test_rp_id_fails_if_not_equal(): void
|
||||||
{
|
{
|
||||||
$this->expectException(AttestationException::class);
|
$this->expectException(AttestationException::class);
|
||||||
$this->expectExceptionMessage('Attestation Error: Relaying Party ID not scoped to current.');
|
$this->expectExceptionMessage('Attestation Error: Relying Party ID not scoped to current.');
|
||||||
|
|
||||||
$invalid = FakeAuthenticator::attestationResponse();
|
$invalid = FakeAuthenticator::attestationResponse();
|
||||||
|
|
||||||
@@ -524,7 +524,7 @@ class ValidationTest extends TestCase
|
|||||||
public function test_rp_id_fails_if_not_contained(): void
|
public function test_rp_id_fails_if_not_contained(): void
|
||||||
{
|
{
|
||||||
$this->expectException(AttestationException::class);
|
$this->expectException(AttestationException::class);
|
||||||
$this->expectExceptionMessage('Attestation Error: Relaying Party ID not scoped to current.');
|
$this->expectExceptionMessage('Attestation Error: Relying Party ID not scoped to current.');
|
||||||
|
|
||||||
$invalid = FakeAuthenticator::attestationResponse();
|
$invalid = FakeAuthenticator::attestationResponse();
|
||||||
|
|
||||||
@@ -546,7 +546,7 @@ class ValidationTest extends TestCase
|
|||||||
$this->app->when(CheckRelyingPartyHashSame::class)
|
$this->app->when(CheckRelyingPartyHashSame::class)
|
||||||
->needs(ConfigContract::class)
|
->needs(ConfigContract::class)
|
||||||
->give(static function (): Repository {
|
->give(static function (): Repository {
|
||||||
return tap(new Repository())->set('webauthn.relaying_party.id', 'https://otherhost.com');
|
return tap(new Repository())->set('webauthn.relying_party.id', 'https://otherhost.com');
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->expectException(AttestationException::class);
|
$this->expectException(AttestationException::class);
|
||||||
|
|||||||
64
tests/ServiceProviderTest.php
Normal file
64
tests/ServiceProviderTest.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Auth\Authenticatable;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Fluent;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable;
|
||||||
|
use Laragear\WebAuthn\WebAuthnAuthentication;
|
||||||
|
use Laragear\WebAuthn\WebAuthnServiceProvider;
|
||||||
|
|
||||||
|
class ServiceProviderTest extends TestCase
|
||||||
|
{
|
||||||
|
public function test_merges_config(): void
|
||||||
|
{
|
||||||
|
static::assertSame(
|
||||||
|
File::getRequire(WebAuthnServiceProvider::CONFIG),
|
||||||
|
$this->app->make('config')->get('webauthn')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_publishes_config(): void
|
||||||
|
{
|
||||||
|
static::assertSame(
|
||||||
|
[WebAuthnServiceProvider::CONFIG => $this->app->configPath('webauthn.php')],
|
||||||
|
ServiceProvider::$publishGroups['config']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_publishes_migrations(): void
|
||||||
|
{
|
||||||
|
$format = now()->format('Y_m_d_His');
|
||||||
|
|
||||||
|
static::assertSame(
|
||||||
|
[
|
||||||
|
realpath(WebAuthnServiceProvider::MIGRATIONS . '/2022_07_01_000000_create_webauthn_credentials.php') =>
|
||||||
|
$this->app->databasePath("migrations/{$format}_create_webauthn_credentials.php"),
|
||||||
|
],
|
||||||
|
ServiceProvider::pathsToPublish(WebAuthnServiceProvider::class, 'migrations')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_bounds_user(): void
|
||||||
|
{
|
||||||
|
static::assertNull($this->app->make(WebAuthnAuthenticatable::class));
|
||||||
|
|
||||||
|
$user = new class extends Fluent implements WebAuthnAuthenticatable {
|
||||||
|
use WebAuthnAuthentication;
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->app->instance(Authenticatable::class, $user);
|
||||||
|
|
||||||
|
static::assertSame($user, $this->app->make(WebAuthnAuthenticatable::class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_publishes_routes_file(): void
|
||||||
|
{
|
||||||
|
static::assertSame(
|
||||||
|
[WebAuthnServiceProvider::ROUTES => $this->app->basePath('routes/webauthn.php')],
|
||||||
|
ServiceProvider::$publishGroups['routes']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user