allow vendord
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s
This commit is contained in:
30
vendor/kreait/firebase-tokens/CHANGELOG.md
vendored
Normal file
30
vendor/kreait/firebase-tokens/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# CHANGELOG
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## 5.3.0 - 2025-09-12
|
||||
|
||||
* Added support for PHP 8.5
|
||||
|
||||
## 5.2.1 - 2024-12-20
|
||||
|
||||
* Fixed deprecated implicit nullable parameter
|
||||
|
||||
## 5.2.0 - 2024-08-17
|
||||
|
||||
* Added support for PHP 8.4
|
||||
([#61](https://github.com/kreait/firebase-tokens-php/pull/61))
|
||||
|
||||
## 5.1.0 - 2024-05-10
|
||||
|
||||
* Restored support for PHP 8.1
|
||||
* Fixed missing signature check when in non-emulated environments
|
||||
([#56](https://github.com/kreait/firebase-tokens-php/pull/56))
|
||||
|
||||
## 5.0.1 - 2023-11-29
|
||||
|
||||
* Fixed ID Token verification when run in emulated environments.
|
||||
|
||||
## 5.0.0 - 2023-11-25
|
||||
|
||||
* Added support for PHP 8.3, removed support for PHP 8.1
|
||||
21
vendor/kreait/firebase-tokens/LICENSE
vendored
Normal file
21
vendor/kreait/firebase-tokens/LICENSE
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Jérôme Gamez, https://github.com/jeromegamez <jerome@gamez.name>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
257
vendor/kreait/firebase-tokens/README.md
vendored
Normal file
257
vendor/kreait/firebase-tokens/README.md
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
# Firebase Tokens
|
||||
|
||||
A library to work with [Google Firebase](https://firebase.google.com) tokens. You can use it to
|
||||
[create custom tokens](https://firebase.google.com/docs/auth/admin/create-custom-tokens) and
|
||||
[verify ID Tokens](https://firebase.google.com/docs/auth/admin/verify-id-tokens).
|
||||
|
||||
Achieve more with the [Firebase Admin SDK](https://github.com/kreait/firebase-php) for PHP (which uses this library).
|
||||
|
||||
[](https://packagist.org/packages/kreait/firebase-tokens)
|
||||
[]()
|
||||
[](https://packagist.org/packages/kreait/firebase-tokens/stats)
|
||||
[](https://packagist.org/packages/kreait/firebase-tokens/stats)
|
||||
[](https://github.com/kreait/firebase-tokens-php/actions)
|
||||
[](https://github.com/sponsors/jeromegamez)
|
||||
|
||||
---
|
||||
|
||||
## The future of the Firebase Admin PHP SDK
|
||||
|
||||
Please read about the future of the Firebase Admin PHP SDK on the
|
||||
[SDK's GitHub Repository](https://github.com/kreait/firebase-php).
|
||||
|
||||
---
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Simple Usage](#simple-usage)
|
||||
- [Create a custom token](#create-a-custom-token)
|
||||
- [Verify an ID token](#verify-an-id-token)
|
||||
- [Verify a Session Cookie](#verify-a-session-cookie)
|
||||
- [Tokens](#tokens)
|
||||
- [Tenant Awareness](#tenant-awareness)
|
||||
- [Advanced Usage](#advanced-usage)
|
||||
- [Cache results from the Google Secure Token Store](#cache-results-from-the-google-secure-token-store)
|
||||
- [Supported Versions](#supported-versions)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
composer require kreait/firebase-tokens
|
||||
```
|
||||
|
||||
## Simple usage
|
||||
|
||||
### Create a custom token
|
||||
|
||||
More information on what a custom token is and how it can be used can be found in
|
||||
[Google's official documentation](https://firebase.google.com/docs/auth/admin/create-custom-tokens).
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Kreait\Firebase\JWT\CustomTokenGenerator;
|
||||
|
||||
$clientEmail = '...';
|
||||
$privateKey = '...';
|
||||
|
||||
$generator = CustomTokenGenerator::withClientEmailAndPrivateKey($clientEmail, $privateKey);
|
||||
$token = $generator->createCustomToken('uid', ['first_claim' => 'first_value' /* ... */]);
|
||||
|
||||
echo $token;
|
||||
// Output: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.e...
|
||||
```
|
||||
|
||||
### Verify an ID token
|
||||
|
||||
The ID token verification methods included in the Firebase Admin SDKs are meant to verify
|
||||
ID tokens that come from the client SDKs, not the custom tokens that you create with the Admin SDKs.
|
||||
See [Auth tokens](https://firebase.google.com/docs/auth/users/#auth_tokens) for more information.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Kreait\Firebase\JWT\Error\IdTokenVerificationFailed;
|
||||
use Kreait\Firebase\JWT\IdTokenVerifier;
|
||||
|
||||
$projectId = '...';
|
||||
$idToken = 'eyJhb...'; // An ID token given to your backend by a Client application
|
||||
|
||||
$verifier = IdTokenVerifier::createWithProjectId($projectId);
|
||||
|
||||
try {
|
||||
$token = $verifier->verifyIdToken($idToken);
|
||||
} catch (IdTokenVerificationFailed $e) {
|
||||
echo $e->getMessage();
|
||||
// Example Output:
|
||||
// The value 'eyJhb...' is not a verified ID token:
|
||||
// - The token is expired.
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$token = $verifier->verifyIdTokenWithLeeway($idToken, $leewayInSeconds = 10000000);
|
||||
} catch (IdTokenVerificationFailed $e) {
|
||||
print $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
```
|
||||
|
||||
### Verify a Session Cookie
|
||||
|
||||
Session cookie verification is similar to ID Token verification.
|
||||
|
||||
See [Manage Session Cookies](https://firebase.google.com/docs/auth/admin/manage-cookies) for more information.
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Kreait\Firebase\JWT\Error\SessionCookieVerificationFailed;
|
||||
use Kreait\Firebase\JWT\SessionCookieVerifier;
|
||||
|
||||
$projectId = '...';
|
||||
$sessionCookie = 'eyJhb...'; // A session cookie given to your backend by a Client application
|
||||
|
||||
$verifier = SessionCookieVerifier::createWithProjectId($projectId);
|
||||
|
||||
try {
|
||||
$token = $verifier->verifySessionCookie($sessionCookie);
|
||||
} catch (SessionCookieVerificationFailed $e) {
|
||||
echo $e->getMessage();
|
||||
// Example Output:
|
||||
// The value 'eyJhb...' is not a verified ID token:
|
||||
// - The token is expired.
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$token = $verifier->verifySessionCookieWithLeeway($sessionCookie, $leewayInSeconds = 10000000);
|
||||
} catch (SessionCookieVerificationFailed $e) {
|
||||
print $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
```
|
||||
|
||||
### Tokens
|
||||
|
||||
Tokens returned from the Generator and Verifier are instances of `\Kreait\Firebase\JWT\Contract\Token` and
|
||||
represent a [JWT](https://jwt.io/). The displayed outputs are examples and vary depending on
|
||||
the information associated with the given user in your project's auth database.
|
||||
|
||||
According to the JWT specification, you can expect the following payload fields to be always
|
||||
available: `iss`, `aud`, `auth_time`, `sub`, `iat`, `exp`. Other fields depend on the
|
||||
authentication method of the given account and the information stored in your project's
|
||||
Auth database.
|
||||
|
||||
```php
|
||||
$token = $verifier->verifyIdToken('eyJhb...'); // An ID token given to your backend by a Client application
|
||||
|
||||
echo json_encode($token->headers(), JSON_PRETTY_PRINT);
|
||||
// {
|
||||
// "alg": "RS256",
|
||||
// "kid": "e5a91d9f39fa4de254a1e89df00f05b7e248b985",
|
||||
// "typ": "JWT"
|
||||
// }
|
||||
|
||||
echo json_encode($token->payload(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
// {
|
||||
// "name": "Jane Doe",
|
||||
// "picture": "https://domain.tld/picture.jpg",
|
||||
// "iss": "https://securetoken.google.com/your-project-id",
|
||||
// "aud": "your-project-id",
|
||||
// "auth_time": 1580063945,
|
||||
// "user_id": "W0IturDwy4TYTmX6ilkd2ZbAXRp2",
|
||||
// "sub": "W0IturDwy4TYTmX6ilkd2ZbAXRp2",
|
||||
// "iat": 1580063945,
|
||||
// "exp": 1580067545,
|
||||
// "email": "jane@doe.tld",
|
||||
// "email_verified": true,
|
||||
// "phone_number": "+1234567890",
|
||||
// "firebase": {
|
||||
// "identities": {
|
||||
// "phone": [
|
||||
// "+1234567890"
|
||||
// ],
|
||||
// "email": [
|
||||
// "jane@doe.tld"
|
||||
// ]
|
||||
// },
|
||||
// "sign_in_provider": "custom"
|
||||
// }
|
||||
// }
|
||||
|
||||
echo $token->toString();
|
||||
// eyJhb...
|
||||
|
||||
$tokenString = (string) $token; // string
|
||||
// eyJhb...
|
||||
```
|
||||
|
||||
### Tenant Awareness
|
||||
|
||||
You can create custom tokens that are scoped to a given tenant:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Kreait\Firebase\JWT\CustomTokenGenerator;
|
||||
|
||||
$generator = CustomTokenGenerator::withClientEmailAndPrivateKey('...', '...');
|
||||
|
||||
$tenantAwareGenerator = $generator->withTenantId('my-tenant-id');
|
||||
```
|
||||
|
||||
Similarly, you can verify that ID tokens were issued in the scope of a given tenant:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use Kreait\Firebase\JWT\IdTokenVerifier;
|
||||
|
||||
$verifier = IdTokenVerifier::createWithProjectId('my-project-id');
|
||||
|
||||
$tenantAwareVerifier = $verifier->withExpectedTenantId('my-tenant-id');
|
||||
```
|
||||
|
||||
Session cookies currently don't support tenants.
|
||||
|
||||
## Advanced usage
|
||||
|
||||
### Cache results from the Google Secure Token Store
|
||||
|
||||
In order to verify ID tokens, the verifier makes a call to fetch Firebase's currently available public
|
||||
keys. The keys are cached in memory by default.
|
||||
|
||||
If you want to cache the public keys more effectively, you can initialize the verifier with an
|
||||
implementation of [psr/simple-cache](https://packagist.org/providers/psr/simple-cache-implementation)
|
||||
or [psr/cache](https://packagist.org/providers/psr/cache-implementation) to reduce the amount
|
||||
of HTTP requests to Google's servers.
|
||||
|
||||
Here's an example using the [Symfony Cache Component](https://symfony.com/doc/current/components/cache.html):
|
||||
|
||||
```php
|
||||
use Kreait\Firebase\JWT\IdTokenVerifier;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
|
||||
$cache = new FilesystemAdapter();
|
||||
|
||||
$verifier = IdTokenVerifier::createWithProjectIdAndCache($projectId, $cache);
|
||||
```
|
||||
|
||||
## Supported Versions
|
||||
|
||||
**Only the latest version is actively supported.**
|
||||
|
||||
Earlier versions will receive security fixes as long as their **lowest** PHP requirement receives security fixes. For
|
||||
example, when a version supports PHP 7.4 and PHP 8.0, security support will end when security support for PHP 7.4 ends.
|
||||
|
||||
| Version | Initial Release | Supported PHP Versions | Status |
|
||||
|---------|-----------------|------------------------------------------|-------------|
|
||||
| `5.x` | 25 Nov 2023 | `~8.1.0, ~8.2.0, ~8.3.0, ~8.4.0, ~8.5.0` | Active |
|
||||
| `4.x` | 26 Nov 2022 | `~8.1.0, ~8.2.0, ~8.3.0` | End of life |
|
||||
| `3.x` | 25 Apr 2022 | `^7.4, ^8.0` | End of life |
|
||||
| `2.x` | 03 Jan 2022 | `^7.4, ^8.0` | End of life |
|
||||
| `1.x` | 06 Feb 2017 | `>=5.5` | End of life |
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](LICENSE) for more information.
|
||||
5
vendor/kreait/firebase-tokens/SECURITY.md
vendored
Normal file
5
vendor/kreait/firebase-tokens/SECURITY.md
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
## Security contact information
|
||||
|
||||
To report a security vulnerability, please use the
|
||||
[Tidelift security contact](https://tidelift.com/security).
|
||||
Tidelift will coordinate the fix and disclosure.
|
||||
87
vendor/kreait/firebase-tokens/composer.json
vendored
Normal file
87
vendor/kreait/firebase-tokens/composer.json
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"name": "kreait/firebase-tokens",
|
||||
"description": "A library to work with Firebase tokens",
|
||||
"type": "library",
|
||||
"keywords": ["firebase", "google", "token", "authentication", "auth"],
|
||||
"homepage": "https://github.com/kreait/firebase-token-php",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jérôme Gamez",
|
||||
"homepage": "https://github.com/jeromegamez"
|
||||
}
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jeromegamez"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
|
||||
"ext-json": "*",
|
||||
"ext-openssl": "*",
|
||||
"beste/clock": "^3.0",
|
||||
"fig/http-message-util": "^1.1.5",
|
||||
"guzzlehttp/guzzle": "^7.8",
|
||||
"lcobucci/jwt": "^5.2",
|
||||
"psr/cache": "^1.0|^2.0|^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/cache-implementation": "to cache fetched remote public keys"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.62.0",
|
||||
"phpstan/extension-installer": "^1.4.1",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpunit/phpunit": "^10.5.30",
|
||||
"rector/rector": "^2.1",
|
||||
"symfony/cache": "^6.4.3 || ^7.1.3",
|
||||
"symfony/var-dumper": "^6.4.3 || ^7.1.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Kreait\\Firebase\\JWT\\":"src/JWT"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Kreait\\Firebase\\JWT\\Tests\\": "tests/JWT"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"phpstan/extension-installer": true
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"analyse": [
|
||||
"XDEBUG_MODE=off vendor/bin/phpstan"
|
||||
],
|
||||
"analyze": "@analyse",
|
||||
"cs": [
|
||||
"PHP_CS_FIXER_IGNORE_ENV=true vendor/bin/php-cs-fixer fix --diff --verbose"
|
||||
],
|
||||
"rector": [
|
||||
"vendor/bin/rector --dry-run"
|
||||
],
|
||||
"rector-fix": [
|
||||
"vendor/bin/rector",
|
||||
"@cs"
|
||||
],
|
||||
"test": [
|
||||
"@analyze",
|
||||
"@test-units",
|
||||
"@test-emulator"
|
||||
],
|
||||
"test-emulator": [
|
||||
"FIREBASE_AUTH_EMULATOR_HOST=localhost:9099",
|
||||
"firebase emulators:exec --only auth --project demo-project 'vendor/bin/phpunit --group=emulator --testdox'"
|
||||
],
|
||||
"test-units": [
|
||||
"vendor/bin/phpunit --exclude-group=emulator --testdox"
|
||||
]
|
||||
}
|
||||
}
|
||||
123
vendor/kreait/firebase-tokens/src/JWT/Action/CreateCustomToken.php
vendored
Normal file
123
vendor/kreait/firebase-tokens/src/JWT/Action/CreateCustomToken.php
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action;
|
||||
|
||||
use DateInterval;
|
||||
use InvalidArgumentException;
|
||||
use Kreait\Firebase\JWT\Value\Duration;
|
||||
|
||||
final class CreateCustomToken
|
||||
{
|
||||
public const MINIMUM_TTL = 'PT1S';
|
||||
|
||||
public const MAXIMUM_TTL = 'PT1H';
|
||||
|
||||
public const DEFAULT_TTL = self::MAXIMUM_TTL;
|
||||
|
||||
private ?string $tenantId = null;
|
||||
|
||||
/** @var array<string, mixed> */
|
||||
private array $customClaims = [];
|
||||
|
||||
private Duration $ttl;
|
||||
|
||||
private function __construct(private string $uid)
|
||||
{
|
||||
$this->ttl = Duration::fromDateIntervalSpec(self::DEFAULT_TTL);
|
||||
}
|
||||
|
||||
public static function forUid(string $uid): self
|
||||
{
|
||||
return new self($uid);
|
||||
}
|
||||
|
||||
public function withTenantId(string $tenantId): self
|
||||
{
|
||||
$action = clone $this;
|
||||
$action->tenantId = $tenantId;
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
public function withChangedUid(string $uid): self
|
||||
{
|
||||
$action = clone $this;
|
||||
$action->uid = $uid;
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
public function withCustomClaim(string $name, mixed $value): self
|
||||
{
|
||||
$action = clone $this;
|
||||
$action->customClaims[$name] = $value;
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $claims
|
||||
*/
|
||||
public function withCustomClaims(array $claims): self
|
||||
{
|
||||
$action = clone $this;
|
||||
$action->customClaims = $claims;
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $claims
|
||||
*/
|
||||
public function withAddedCustomClaims(array $claims): self
|
||||
{
|
||||
$action = clone $this;
|
||||
$action->customClaims = [...$action->customClaims, ...$claims];
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
public function withTimeToLive(Duration|DateInterval|string|int $ttl): self
|
||||
{
|
||||
$ttl = Duration::make($ttl);
|
||||
|
||||
$minTtl = Duration::fromDateIntervalSpec(self::MINIMUM_TTL);
|
||||
$maxTtl = Duration::fromDateIntervalSpec(self::MAXIMUM_TTL);
|
||||
|
||||
if ($ttl->isSmallerThan($minTtl) || $ttl->isLargerThan($maxTtl)) {
|
||||
$message = 'The expiration time of a custom token must be between %s and %s, but got %s';
|
||||
|
||||
throw new InvalidArgumentException(sprintf($message, $minTtl, $maxTtl, $ttl));
|
||||
}
|
||||
|
||||
$action = clone $this;
|
||||
$action->ttl = $ttl;
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
public function uid(): string
|
||||
{
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
public function tenantId(): ?string
|
||||
{
|
||||
return $this->tenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function customClaims(): array
|
||||
{
|
||||
return $this->customClaims;
|
||||
}
|
||||
|
||||
public function timeToLive(): Duration
|
||||
{
|
||||
return $this->ttl;
|
||||
}
|
||||
}
|
||||
17
vendor/kreait/firebase-tokens/src/JWT/Action/CreateCustomToken/Handler.php
vendored
Normal file
17
vendor/kreait/firebase-tokens/src/JWT/Action/CreateCustomToken/Handler.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action\CreateCustomToken;
|
||||
|
||||
use Kreait\Firebase\JWT\Action\CreateCustomToken;
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Kreait\Firebase\JWT\Error\CustomTokenCreationFailed;
|
||||
|
||||
interface Handler
|
||||
{
|
||||
/**
|
||||
* @throws CustomTokenCreationFailed
|
||||
*/
|
||||
public function handle(CreateCustomToken $action): Token;
|
||||
}
|
||||
86
vendor/kreait/firebase-tokens/src/JWT/Action/CreateCustomToken/WithLcobucciJWT.php
vendored
Normal file
86
vendor/kreait/firebase-tokens/src/JWT/Action/CreateCustomToken/WithLcobucciJWT.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action\CreateCustomToken;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Kreait\Firebase\JWT\Action\CreateCustomToken;
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Kreait\Firebase\JWT\Error\CustomTokenCreationFailed;
|
||||
use Kreait\Firebase\JWT\SecureToken;
|
||||
use Lcobucci\JWT\Configuration;
|
||||
use Lcobucci\JWT\Signer\Key\InMemory;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use Psr\Clock\ClockInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class WithLcobucciJWT implements Handler
|
||||
{
|
||||
private readonly Configuration $config;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $clientEmail
|
||||
* @param non-empty-string $privateKey
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $clientEmail,
|
||||
string $privateKey,
|
||||
private readonly ClockInterface $clock,
|
||||
) {
|
||||
$this->config = Configuration::forSymmetricSigner(
|
||||
new Sha256(),
|
||||
InMemory::plainText($privateKey),
|
||||
);
|
||||
}
|
||||
|
||||
public function handle(CreateCustomToken $action): Token
|
||||
{
|
||||
$now = $this->clock->now();
|
||||
|
||||
$builder = $this->config->builder()
|
||||
->issuedAt($now)
|
||||
->issuedBy($this->clientEmail)
|
||||
->expiresAt($now->add($action->timeToLive()->value()))
|
||||
->relatedTo($this->clientEmail)
|
||||
->permittedFor('https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit')
|
||||
->withClaim('uid', $action->uid());
|
||||
|
||||
if ($tenantId = $action->tenantId()) {
|
||||
$builder = $builder->withClaim('tenant_id', $tenantId);
|
||||
}
|
||||
|
||||
if (!empty($customClaims = $action->customClaims())) {
|
||||
$builder = $builder->withClaim('claims', $customClaims);
|
||||
}
|
||||
|
||||
try {
|
||||
$token = $builder->getToken($this->config->signer(), $this->config->signingKey());
|
||||
} catch (Throwable $e) {
|
||||
throw CustomTokenCreationFailed::because($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$claims = $token->claims()->all();
|
||||
|
||||
foreach ($claims as &$claim) {
|
||||
if ($claim instanceof DateTimeInterface) {
|
||||
$claim = $claim->getTimestamp();
|
||||
}
|
||||
}
|
||||
unset($claim);
|
||||
|
||||
$headers = $token->headers()->all();
|
||||
|
||||
foreach ($headers as &$header) {
|
||||
if ($header instanceof DateTimeInterface) {
|
||||
$header = $header->getTimestamp();
|
||||
}
|
||||
}
|
||||
unset($header);
|
||||
|
||||
return SecureToken::withValues($token->toString(), $headers, $claims);
|
||||
}
|
||||
}
|
||||
74
vendor/kreait/firebase-tokens/src/JWT/Action/FetchGooglePublicKeys.php
vendored
Normal file
74
vendor/kreait/firebase-tokens/src/JWT/Action/FetchGooglePublicKeys.php
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action;
|
||||
|
||||
use DateInterval;
|
||||
use Kreait\Firebase\JWT\Value\Duration;
|
||||
|
||||
final class FetchGooglePublicKeys
|
||||
{
|
||||
public const DEFAULT_URLS = [
|
||||
'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com',
|
||||
'https://www.googleapis.com/oauth2/v1/certs',
|
||||
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys',
|
||||
];
|
||||
|
||||
public const DEFAULT_FALLBACK_CACHE_DURATION = 'PT1H';
|
||||
|
||||
/** @var array<int, non-empty-string> */
|
||||
private readonly array $urls;
|
||||
|
||||
/**
|
||||
* @param array<array-key, non-empty-string> $urls
|
||||
*/
|
||||
private function __construct(array $urls, private Duration $fallbackCacheDuration)
|
||||
{
|
||||
$this->urls = array_values($urls);
|
||||
}
|
||||
|
||||
public static function fromGoogle(): self
|
||||
{
|
||||
return new self(self::DEFAULT_URLS, Duration::fromDateIntervalSpec(self::DEFAULT_FALLBACK_CACHE_DURATION));
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method only if Google has changed the default URL and the library hasn't been updated yet.
|
||||
*
|
||||
* @param non-empty-string $url
|
||||
*/
|
||||
public static function fromUrl(string $url): self
|
||||
{
|
||||
return new self([$url], Duration::fromDateIntervalSpec(self::DEFAULT_FALLBACK_CACHE_DURATION));
|
||||
}
|
||||
|
||||
/**
|
||||
* A response from the Google APIs should have a cache control header that determines when the keys expire.
|
||||
* If it doesn't have one, fall back to this value.
|
||||
*
|
||||
* @param Duration|DateInterval|non-empty-string|int $duration
|
||||
*/
|
||||
public function ifKeysDoNotExpireCacheFor(Duration|DateInterval|string|int $duration): self
|
||||
{
|
||||
$duration = Duration::make($duration);
|
||||
|
||||
$action = clone $this;
|
||||
$action->fallbackCacheDuration = $duration;
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function urls(): array
|
||||
{
|
||||
return $this->urls;
|
||||
}
|
||||
|
||||
public function getFallbackCacheDuration(): Duration
|
||||
{
|
||||
return $this->fallbackCacheDuration;
|
||||
}
|
||||
}
|
||||
17
vendor/kreait/firebase-tokens/src/JWT/Action/FetchGooglePublicKeys/Handler.php
vendored
Normal file
17
vendor/kreait/firebase-tokens/src/JWT/Action/FetchGooglePublicKeys/Handler.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action\FetchGooglePublicKeys;
|
||||
|
||||
use Kreait\Firebase\JWT\Action\FetchGooglePublicKeys;
|
||||
use Kreait\Firebase\JWT\Contract\Keys;
|
||||
use Kreait\Firebase\JWT\Error\FetchingGooglePublicKeysFailed;
|
||||
|
||||
interface Handler
|
||||
{
|
||||
/**
|
||||
* @throws FetchingGooglePublicKeysFailed
|
||||
*/
|
||||
public function handle(FetchGooglePublicKeys $action): Keys;
|
||||
}
|
||||
101
vendor/kreait/firebase-tokens/src/JWT/Action/FetchGooglePublicKeys/WithGuzzle.php
vendored
Normal file
101
vendor/kreait/firebase-tokens/src/JWT/Action/FetchGooglePublicKeys/WithGuzzle.php
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action\FetchGooglePublicKeys;
|
||||
|
||||
use Fig\Http\Message\RequestMethodInterface as RequestMethod;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use JsonException;
|
||||
use Kreait\Firebase\JWT\Action\FetchGooglePublicKeys;
|
||||
use Kreait\Firebase\JWT\Contract\Keys;
|
||||
use Kreait\Firebase\JWT\Error\FetchingGooglePublicKeysFailed;
|
||||
use Kreait\Firebase\JWT\Keys\ExpiringKeys;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class WithGuzzle implements Handler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ClientInterface $client,
|
||||
private readonly ClockInterface $clock,
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(FetchGooglePublicKeys $action): Keys
|
||||
{
|
||||
$keys = [];
|
||||
$ttls = [];
|
||||
|
||||
foreach ($action->urls() as $url) {
|
||||
$result = $this->fetchKeysFromUrl($url);
|
||||
|
||||
$keys[] = $result['keys'];
|
||||
$ttls[] = $result['ttl'];
|
||||
}
|
||||
|
||||
$keys = array_merge(...$keys);
|
||||
$ttl = $ttls !== [] ? min($ttls) : 0;
|
||||
$now = $this->clock->now();
|
||||
|
||||
$expiresAt = $ttl > 0
|
||||
? $now->setTimestamp($now->getTimestamp() + $ttl)
|
||||
: $now->add($action->getFallbackCacheDuration()->value());
|
||||
|
||||
return ExpiringKeys::withValuesAndExpirationTime($keys, $expiresAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* keys: array<non-empty-string, non-empty-string>,
|
||||
* ttl: int
|
||||
* }
|
||||
*/
|
||||
private function fetchKeysFromUrl(string $url): array
|
||||
{
|
||||
try {
|
||||
$response = $this->client->request(RequestMethod::METHOD_GET, $url, [
|
||||
'http_errors' => false,
|
||||
'headers' => [
|
||||
'Content-Type' => 'Content-Type: application/json; charset=UTF-8',
|
||||
],
|
||||
]);
|
||||
} catch (GuzzleException $e) {
|
||||
throw FetchingGooglePublicKeysFailed::because("The connection to {$url} failed: ".$e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
if (($statusCode = $response->getStatusCode()) !== 200) {
|
||||
throw FetchingGooglePublicKeysFailed::because(
|
||||
"Unexpected status code {$statusCode} when trying to fetch public keys from {$url}",
|
||||
);
|
||||
}
|
||||
|
||||
$ttl = preg_match('/max-age=(\d+)/i', $response->getHeaderLine('Cache-Control'), $matches)
|
||||
? (int) $matches[1]
|
||||
: 0;
|
||||
|
||||
try {
|
||||
$keys = json_decode((string) $response->getBody(), true, 512, JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException $e) {
|
||||
throw FetchingGooglePublicKeysFailed::because('Unexpected response: '.$e->getMessage());
|
||||
}
|
||||
|
||||
if (!is_array($keys)) {
|
||||
$keys = [];
|
||||
}
|
||||
|
||||
$keys = array_filter($keys, fn(mixed $key): bool => is_string($key));
|
||||
$keys = array_map(fn(string $key): string => trim($key), $keys);
|
||||
$keys = array_filter($keys, fn(string $key): bool => $key !== '');
|
||||
|
||||
return [
|
||||
'keys' => $keys,
|
||||
'ttl' => $ttl,
|
||||
];
|
||||
}
|
||||
}
|
||||
77
vendor/kreait/firebase-tokens/src/JWT/Action/FetchGooglePublicKeys/WithPsr6Cache.php
vendored
Normal file
77
vendor/kreait/firebase-tokens/src/JWT/Action/FetchGooglePublicKeys/WithPsr6Cache.php
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action\FetchGooglePublicKeys;
|
||||
|
||||
use Kreait\Firebase\JWT\Action\FetchGooglePublicKeys;
|
||||
use Kreait\Firebase\JWT\Contract\Expirable;
|
||||
use Kreait\Firebase\JWT\Contract\Keys;
|
||||
use Kreait\Firebase\JWT\Error\FetchingGooglePublicKeysFailed;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class WithPsr6Cache implements Handler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Handler $handler,
|
||||
private readonly CacheItemPoolInterface $cache,
|
||||
private readonly ClockInterface $clock,
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(FetchGooglePublicKeys $action): Keys
|
||||
{
|
||||
$now = $this->clock->now();
|
||||
$cacheKey = md5($action::class);
|
||||
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$cacheItem = $this->cache->getItem($cacheKey);
|
||||
|
||||
/** @var Keys|Expirable|null $keys */
|
||||
$keys = $cacheItem->get();
|
||||
|
||||
// We deliberately don't care if the cache item is expired here, as long as the keys
|
||||
// themselves are not expired
|
||||
if ($keys instanceof Keys && $keys instanceof Expirable && !$keys->isExpiredAt($now)) {
|
||||
return $keys;
|
||||
}
|
||||
|
||||
// Non-expiring keys coming from a cache hit can be returned as well
|
||||
if ($keys instanceof Keys && !($keys instanceof Expirable) && $cacheItem->isHit()) {
|
||||
return $keys;
|
||||
}
|
||||
|
||||
// At this point, we have to re-fetch the keys, because either the cache item is a miss
|
||||
// or the value in the cache item is not a Keys object
|
||||
|
||||
// We need fresh keys
|
||||
try {
|
||||
$keys = $this->handler->handle($action);
|
||||
} catch (FetchingGooglePublicKeysFailed $e) {
|
||||
$reason = sprintf(
|
||||
'The inner handler of %s (%s) failed in fetching keys: %s',
|
||||
self::class,
|
||||
$this->handler::class,
|
||||
$e->getMessage(),
|
||||
);
|
||||
|
||||
throw FetchingGooglePublicKeysFailed::because($reason, $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$cacheItem->set($keys);
|
||||
|
||||
if ($keys instanceof Expirable) {
|
||||
$cacheItem->expiresAt($keys->expiresAt());
|
||||
} else {
|
||||
$cacheItem->expiresAfter($action->getFallbackCacheDuration()->value());
|
||||
}
|
||||
|
||||
$this->cache->save($cacheItem);
|
||||
|
||||
return $keys;
|
||||
}
|
||||
}
|
||||
71
vendor/kreait/firebase-tokens/src/JWT/Action/VerifyIdToken.php
vendored
Normal file
71
vendor/kreait/firebase-tokens/src/JWT/Action/VerifyIdToken.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class VerifyIdToken
|
||||
{
|
||||
/**
|
||||
* @param non-empty-string $token
|
||||
* @param int<0, max> $leewayInSeconds
|
||||
* @param non-empty-string|null $expectedTenantId
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly string $token,
|
||||
private readonly int $leewayInSeconds,
|
||||
private readonly ?string $expectedTenantId,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $token
|
||||
*/
|
||||
public static function withToken(string $token): self
|
||||
{
|
||||
return new self($token, 0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $tenantId
|
||||
*/
|
||||
public function withExpectedTenantId(string $tenantId): self
|
||||
{
|
||||
return new self($this->token, $this->leewayInSeconds, $tenantId);
|
||||
}
|
||||
|
||||
public function withLeewayInSeconds(int $seconds): self
|
||||
{
|
||||
if ($seconds < 0) {
|
||||
throw new InvalidArgumentException('Leeway must not be negative');
|
||||
}
|
||||
|
||||
return new self($this->token, $seconds, $this->expectedTenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function token(): string
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string|null
|
||||
*/
|
||||
public function expectedTenantId(): ?string
|
||||
{
|
||||
return $this->expectedTenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function leewayInSeconds(): int
|
||||
{
|
||||
return $this->leewayInSeconds;
|
||||
}
|
||||
}
|
||||
20
vendor/kreait/firebase-tokens/src/JWT/Action/VerifyIdToken/Handler.php
vendored
Normal file
20
vendor/kreait/firebase-tokens/src/JWT/Action/VerifyIdToken/Handler.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action\VerifyIdToken;
|
||||
|
||||
use Kreait\Firebase\JWT\Action\VerifyIdToken;
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Kreait\Firebase\JWT\Error\IdTokenVerificationFailed;
|
||||
|
||||
/**
|
||||
* @see https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_a_third-party_jwt_library
|
||||
*/
|
||||
interface Handler
|
||||
{
|
||||
/**
|
||||
* @throws IdTokenVerificationFailed
|
||||
*/
|
||||
public function handle(VerifyIdToken $action): Token;
|
||||
}
|
||||
203
vendor/kreait/firebase-tokens/src/JWT/Action/VerifyIdToken/WithLcobucciJWT.php
vendored
Normal file
203
vendor/kreait/firebase-tokens/src/JWT/Action/VerifyIdToken/WithLcobucciJWT.php
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action\VerifyIdToken;
|
||||
|
||||
use Beste\Clock\FrozenClock;
|
||||
use DateInterval;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Kreait\Firebase\JWT\Action\VerifyIdToken;
|
||||
use Kreait\Firebase\JWT\Contract\Keys;
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Kreait\Firebase\JWT\Error\IdTokenVerificationFailed;
|
||||
use Kreait\Firebase\JWT\Error\SessionCookieVerificationFailed;
|
||||
use Kreait\Firebase\JWT\InsecureToken;
|
||||
use Kreait\Firebase\JWT\SecureToken;
|
||||
use Kreait\Firebase\JWT\Signer\None;
|
||||
use Kreait\Firebase\JWT\Token\Parser;
|
||||
use Kreait\Firebase\JWT\Util;
|
||||
use Lcobucci\JWT\Encoding\JoseEncoder;
|
||||
use Lcobucci\JWT\Signer;
|
||||
use Lcobucci\JWT\Signer\Key\InMemory;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\Constraint\IssuedBy;
|
||||
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
|
||||
use Lcobucci\JWT\Validation\Constraint\PermittedFor;
|
||||
use Lcobucci\JWT\Validation\Constraint\SignedWith;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
|
||||
use Lcobucci\JWT\Validation\Validator;
|
||||
use Psr\Clock\ClockInterface;
|
||||
use Throwable;
|
||||
|
||||
use function assert;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class WithLcobucciJWT implements Handler
|
||||
{
|
||||
private readonly Parser $parser;
|
||||
|
||||
private readonly Signer $signer;
|
||||
|
||||
private readonly Validator $validator;
|
||||
|
||||
private readonly bool $isRunOnEmulator;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $projectId
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $projectId,
|
||||
private readonly Keys $keys,
|
||||
private readonly ClockInterface $clock,
|
||||
) {
|
||||
$this->parser = new Parser(new JoseEncoder());
|
||||
|
||||
$this->isRunOnEmulator = Util::authEmulatorHost() !== '';
|
||||
|
||||
$this->signer = $this->isRunOnEmulator ? new None() : new Sha256();
|
||||
$this->validator = new Validator();
|
||||
}
|
||||
|
||||
public function handle(VerifyIdToken $action): Token
|
||||
{
|
||||
$tokenString = $action->token();
|
||||
|
||||
try {
|
||||
$token = $this->parser->parse($tokenString);
|
||||
assert($token instanceof UnencryptedToken);
|
||||
} catch (Throwable $e) {
|
||||
throw IdTokenVerificationFailed::withTokenAndReasons($tokenString, ['The token is invalid'.$e->getMessage()]);
|
||||
}
|
||||
|
||||
$key = $this->getKey($token);
|
||||
$clock = FrozenClock::at($this->clock->now());
|
||||
$leeway = new DateInterval('PT'.$action->leewayInSeconds().'S');
|
||||
$errors = [];
|
||||
|
||||
$constraints = [
|
||||
new LooseValidAt($clock, $leeway),
|
||||
new IssuedBy(...["https://securetoken.google.com/{$this->projectId}"]),
|
||||
new PermittedFor($this->projectId),
|
||||
];
|
||||
|
||||
if ($key !== '' && !$this->isRunOnEmulator) {
|
||||
$constraints[] = new SignedWith($this->signer, InMemory::plainText($key));
|
||||
}
|
||||
|
||||
try {
|
||||
$this->validator->assert($token, ...$constraints);
|
||||
|
||||
$this->assertUserAuthedAt($token, $clock->now()->add($leeway));
|
||||
|
||||
if ($tenantId = $action->expectedTenantId()) {
|
||||
$this->assertTenantId($token, $tenantId);
|
||||
}
|
||||
} catch (RequiredConstraintsViolated $e) {
|
||||
$errors = array_filter(array_map(
|
||||
static fn(ConstraintViolation $violation): string => $violation->getMessage(),
|
||||
$e->violations(),
|
||||
));
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
throw IdTokenVerificationFailed::withTokenAndReasons($tokenString, $errors);
|
||||
}
|
||||
|
||||
$claims = $token->claims()->all();
|
||||
|
||||
foreach ($claims as &$claim) {
|
||||
if ($claim instanceof DateTimeInterface) {
|
||||
$claim = $claim->getTimestamp();
|
||||
}
|
||||
}
|
||||
unset($claim);
|
||||
|
||||
$headers = $token->headers()->all();
|
||||
|
||||
foreach ($headers as &$header) {
|
||||
if ($header instanceof DateTimeInterface) {
|
||||
$header = $header->getTimestamp();
|
||||
}
|
||||
}
|
||||
unset($header);
|
||||
|
||||
if ($this->isRunOnEmulator) {
|
||||
return InsecureToken::withValues($tokenString, $headers, $claims);
|
||||
}
|
||||
|
||||
return SecureToken::withValues($tokenString, $headers, $claims);
|
||||
}
|
||||
|
||||
private function getKey(UnencryptedToken $token): string
|
||||
{
|
||||
$keys = $this->keys->all();
|
||||
if ($keys === []) {
|
||||
throw IdTokenVerificationFailed::withTokenAndReasons($token->toString(), ['No keys are available to verify the tokens signature.']);
|
||||
}
|
||||
|
||||
if ($this->isRunOnEmulator && ($this->signer instanceof None)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$keyId = $token->headers()->get('kid');
|
||||
if (!is_string($keyId) || $keyId === '') {
|
||||
throw IdTokenVerificationFailed::withTokenAndReasons($token->toString(), ['No key ID was found to verify the signature of this token.']);
|
||||
}
|
||||
|
||||
$key = $keys[$keyId] ?? null;
|
||||
|
||||
if ($key === null) {
|
||||
throw IdTokenVerificationFailed::withTokenAndReasons($token->toString(), ["No public key matching the key ID '{$keyId}' was found to verify the signature of this token."]);
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
private function assertUserAuthedAt(UnencryptedToken $token, DateTimeInterface $now): void
|
||||
{
|
||||
/** @var int|DateTimeImmutable $authTime */
|
||||
$authTime = $token->claims()->get('auth_time');
|
||||
|
||||
if (!$authTime) {
|
||||
throw RequiredConstraintsViolated::fromViolations(
|
||||
new ConstraintViolation('The token is missing the "auth_time" claim.'),
|
||||
);
|
||||
}
|
||||
|
||||
if (is_numeric($authTime)) {
|
||||
$authTime = new DateTimeImmutable('@'.((int) $authTime));
|
||||
}
|
||||
|
||||
if ($now < $authTime) {
|
||||
throw RequiredConstraintsViolated::fromViolations(
|
||||
new ConstraintViolation("The token's user must have authenticated in the past"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function assertTenantId(UnencryptedToken $token, string $tenantId): void
|
||||
{
|
||||
$claim = (array) $token->claims()->get('firebase', []);
|
||||
|
||||
$tenant = $claim['tenant'] ?? null;
|
||||
|
||||
if (!is_string($tenant)) {
|
||||
throw RequiredConstraintsViolated::fromViolations(
|
||||
new ConstraintViolation('The ID token does not contain a tenant identifier'),
|
||||
);
|
||||
}
|
||||
|
||||
if ($tenant !== $tenantId) {
|
||||
throw RequiredConstraintsViolated::fromViolations(
|
||||
new ConstraintViolation("The token's tenant ID did not match with the expected tenant ID"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
71
vendor/kreait/firebase-tokens/src/JWT/Action/VerifySessionCookie.php
vendored
Normal file
71
vendor/kreait/firebase-tokens/src/JWT/Action/VerifySessionCookie.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class VerifySessionCookie
|
||||
{
|
||||
/**
|
||||
* @param non-empty-string $sessionCookie
|
||||
* @param int<0, max> $leewayInSeconds
|
||||
* @param non-empty-string|null $expectedTenantId
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly string $sessionCookie,
|
||||
private readonly int $leewayInSeconds,
|
||||
private readonly ?string $expectedTenantId,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $sessionCookie
|
||||
*/
|
||||
public static function withSessionCookie(string $sessionCookie): self
|
||||
{
|
||||
return new self($sessionCookie, 0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $tenantId
|
||||
*/
|
||||
public function withExpectedTenantId(string $tenantId): self
|
||||
{
|
||||
return new self($this->sessionCookie, $this->leewayInSeconds, $tenantId);
|
||||
}
|
||||
|
||||
public function withLeewayInSeconds(int $seconds): self
|
||||
{
|
||||
if ($seconds < 0) {
|
||||
throw new InvalidArgumentException('Leeway must not be negative');
|
||||
}
|
||||
|
||||
return new self($this->sessionCookie, $seconds, $this->expectedTenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function sessionCookie(): string
|
||||
{
|
||||
return $this->sessionCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string|null
|
||||
*/
|
||||
public function expectedTenantId(): ?string
|
||||
{
|
||||
return $this->expectedTenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function leewayInSeconds(): int
|
||||
{
|
||||
return $this->leewayInSeconds;
|
||||
}
|
||||
}
|
||||
20
vendor/kreait/firebase-tokens/src/JWT/Action/VerifySessionCookie/Handler.php
vendored
Normal file
20
vendor/kreait/firebase-tokens/src/JWT/Action/VerifySessionCookie/Handler.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action\VerifySessionCookie;
|
||||
|
||||
use Kreait\Firebase\JWT\Action\VerifySessionCookie;
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Kreait\Firebase\JWT\Error\SessionCookieVerificationFailed;
|
||||
|
||||
/**
|
||||
* @see https://firebase.google.com/docs/auth/admin/manage-cookies#verify_session_cookies_using_a_third-party_jwt_library
|
||||
*/
|
||||
interface Handler
|
||||
{
|
||||
/**
|
||||
* @throws SessionCookieVerificationFailed
|
||||
*/
|
||||
public function handle(VerifySessionCookie $action): Token;
|
||||
}
|
||||
201
vendor/kreait/firebase-tokens/src/JWT/Action/VerifySessionCookie/WithLcobucciJWT.php
vendored
Normal file
201
vendor/kreait/firebase-tokens/src/JWT/Action/VerifySessionCookie/WithLcobucciJWT.php
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Action\VerifySessionCookie;
|
||||
|
||||
use Beste\Clock\FrozenClock;
|
||||
use DateInterval;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
use Kreait\Firebase\JWT\Action\VerifySessionCookie;
|
||||
use Kreait\Firebase\JWT\Contract\Keys;
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Kreait\Firebase\JWT\Error\SessionCookieVerificationFailed;
|
||||
use Kreait\Firebase\JWT\InsecureToken;
|
||||
use Kreait\Firebase\JWT\SecureToken;
|
||||
use Kreait\Firebase\JWT\Signer\None;
|
||||
use Kreait\Firebase\JWT\Token\Parser;
|
||||
use Kreait\Firebase\JWT\Util;
|
||||
use Lcobucci\JWT\Encoding\JoseEncoder;
|
||||
use Lcobucci\JWT\Signer;
|
||||
use Lcobucci\JWT\Signer\Key\InMemory;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\Constraint\IssuedBy;
|
||||
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
|
||||
use Lcobucci\JWT\Validation\Constraint\PermittedFor;
|
||||
use Lcobucci\JWT\Validation\Constraint\SignedWith;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Lcobucci\JWT\Validation\RequiredConstraintsViolated;
|
||||
use Lcobucci\JWT\Validation\Validator;
|
||||
use Psr\Clock\ClockInterface;
|
||||
use Throwable;
|
||||
|
||||
use function assert;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class WithLcobucciJWT implements Handler
|
||||
{
|
||||
private readonly Parser $parser;
|
||||
|
||||
private Signer $signer;
|
||||
|
||||
private readonly Validator $validator;
|
||||
|
||||
private readonly bool $isRunOnEmulator;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $projectId
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $projectId,
|
||||
private readonly Keys $keys,
|
||||
private readonly ClockInterface $clock,
|
||||
) {
|
||||
$this->parser = new Parser(new JoseEncoder());
|
||||
|
||||
$this->isRunOnEmulator = Util::authEmulatorHost() !== '';
|
||||
|
||||
$this->signer = $this->isRunOnEmulator ? new None() : new Sha256();
|
||||
$this->validator = new Validator();
|
||||
}
|
||||
|
||||
public function handle(VerifySessionCookie $action): Token
|
||||
{
|
||||
$cookieString = $action->sessionCookie();
|
||||
|
||||
try {
|
||||
$token = $this->parser->parse($cookieString);
|
||||
assert($token instanceof UnencryptedToken);
|
||||
} catch (Throwable $e) {
|
||||
throw SessionCookieVerificationFailed::withSessionCookieAndReasons($cookieString, ['The token is invalid', $e->getMessage()]);
|
||||
}
|
||||
|
||||
$key = $this->getKey($token);
|
||||
$clock = FrozenClock::at($this->clock->now());
|
||||
$leeway = new DateInterval('PT'.$action->leewayInSeconds().'S');
|
||||
$errors = [];
|
||||
$constraints = [
|
||||
new LooseValidAt($clock, $leeway),
|
||||
new IssuedBy(...["https://session.firebase.google.com/{$this->projectId}"]),
|
||||
new PermittedFor($this->projectId),
|
||||
];
|
||||
|
||||
if ($key !== '' && Util::authEmulatorHost() === '') {
|
||||
$constraints[] = new SignedWith($this->signer, InMemory::plainText($key));
|
||||
}
|
||||
|
||||
try {
|
||||
$this->validator->assert($token, ...$constraints);
|
||||
|
||||
$this->assertUserAuthedAt($token, $clock->now()->add($leeway));
|
||||
|
||||
if ($tenantId = $action->expectedTenantId()) {
|
||||
$this->assertTenantId($token, $tenantId);
|
||||
}
|
||||
} catch (RequiredConstraintsViolated $e) {
|
||||
$errors = array_map(
|
||||
static fn(ConstraintViolation $violation): string => '- '.$violation->getMessage(),
|
||||
$e->violations(),
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
throw SessionCookieVerificationFailed::withSessionCookieAndReasons($cookieString, $errors);
|
||||
}
|
||||
|
||||
$claims = $token->claims()->all();
|
||||
|
||||
foreach ($claims as &$claim) {
|
||||
if ($claim instanceof DateTimeInterface) {
|
||||
$claim = $claim->getTimestamp();
|
||||
}
|
||||
}
|
||||
unset($claim);
|
||||
|
||||
$headers = $token->headers()->all();
|
||||
|
||||
foreach ($headers as &$header) {
|
||||
if ($header instanceof DateTimeInterface) {
|
||||
$header = $header->getTimestamp();
|
||||
}
|
||||
}
|
||||
unset($header);
|
||||
|
||||
if (Util::authEmulatorHost() !== '') {
|
||||
return InsecureToken::withValues($cookieString, $headers, $claims);
|
||||
}
|
||||
|
||||
return SecureToken::withValues($cookieString, $headers, $claims);
|
||||
}
|
||||
|
||||
private function getKey(UnencryptedToken $token): string
|
||||
{
|
||||
$keys = $this->keys->all();
|
||||
if ($keys === []) {
|
||||
throw SessionCookieVerificationFailed::withSessionCookieAndReasons($token->toString(), ["No keys are available to verify the token's signature."]);
|
||||
}
|
||||
|
||||
if ($this->isRunOnEmulator && ($this->signer instanceof None)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$keyId = $token->headers()->get('kid');
|
||||
if (!is_string($keyId) || $keyId === '') {
|
||||
throw SessionCookieVerificationFailed::withSessionCookieAndReasons($token->toString(), ["The session cookie doesn't include a `kid` header."]);
|
||||
}
|
||||
|
||||
$key = $keys[$keyId] ?? null;
|
||||
|
||||
if ($key === null) {
|
||||
throw SessionCookieVerificationFailed::withSessionCookieAndReasons($token->toString(), ["The `kid` header of the given token is missing or empty.No public key matching the key ID `{$keyId}` was found to verify the signature of this session cookie."]);
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
private function assertUserAuthedAt(UnencryptedToken $token, DateTimeInterface $now): void
|
||||
{
|
||||
/** @var int|DateTimeImmutable $authTime */
|
||||
$authTime = $token->claims()->get('auth_time');
|
||||
|
||||
if (!$authTime) {
|
||||
throw RequiredConstraintsViolated::fromViolations(
|
||||
new ConstraintViolation('The token is missing the "auth_time" claim.'),
|
||||
);
|
||||
}
|
||||
|
||||
if (is_numeric($authTime)) {
|
||||
$authTime = new DateTimeImmutable('@'.((int) $authTime));
|
||||
}
|
||||
|
||||
if ($now < $authTime) {
|
||||
throw RequiredConstraintsViolated::fromViolations(
|
||||
new ConstraintViolation("The token's user must have authenticated in the past"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function assertTenantId(UnencryptedToken $token, string $tenantId): void
|
||||
{
|
||||
$claim = (array) $token->claims()->get('firebase', []);
|
||||
|
||||
$tenant = $claim['tenant'] ?? null;
|
||||
|
||||
if (!is_string($tenant)) {
|
||||
throw RequiredConstraintsViolated::fromViolations(
|
||||
new ConstraintViolation('The ID token does not contain a tenant identifier'),
|
||||
);
|
||||
}
|
||||
|
||||
if ($tenant !== $tenantId) {
|
||||
throw RequiredConstraintsViolated::fromViolations(
|
||||
new ConstraintViolation("The token's tenant ID did not match with the expected tenant ID"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
vendor/kreait/firebase-tokens/src/JWT/Contract/Expirable.php
vendored
Normal file
17
vendor/kreait/firebase-tokens/src/JWT/Contract/Expirable.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Contract;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
|
||||
interface Expirable
|
||||
{
|
||||
public function withExpirationTime(DateTimeImmutable $time): self;
|
||||
|
||||
public function isExpiredAt(DateTimeInterface $now): bool;
|
||||
|
||||
public function expiresAt(): DateTimeImmutable;
|
||||
}
|
||||
31
vendor/kreait/firebase-tokens/src/JWT/Contract/ExpirableTrait.php
vendored
Normal file
31
vendor/kreait/firebase-tokens/src/JWT/Contract/ExpirableTrait.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Contract;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
|
||||
trait ExpirableTrait
|
||||
{
|
||||
private DateTimeImmutable $expirationTime;
|
||||
|
||||
public function withExpirationTime(DateTimeImmutable $expirationTime): self
|
||||
{
|
||||
$expirable = clone $this;
|
||||
$expirable->expirationTime = $expirationTime;
|
||||
|
||||
return $expirable;
|
||||
}
|
||||
|
||||
public function isExpiredAt(DateTimeInterface $now): bool
|
||||
{
|
||||
return $this->expirationTime < $now;
|
||||
}
|
||||
|
||||
public function expiresAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->expirationTime;
|
||||
}
|
||||
}
|
||||
13
vendor/kreait/firebase-tokens/src/JWT/Contract/Keys.php
vendored
Normal file
13
vendor/kreait/firebase-tokens/src/JWT/Contract/Keys.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Contract;
|
||||
|
||||
interface Keys
|
||||
{
|
||||
/**
|
||||
* @return array<non-empty-string, non-empty-string>
|
||||
*/
|
||||
public function all(): array;
|
||||
}
|
||||
19
vendor/kreait/firebase-tokens/src/JWT/Contract/KeysTrait.php
vendored
Normal file
19
vendor/kreait/firebase-tokens/src/JWT/Contract/KeysTrait.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Contract;
|
||||
|
||||
trait KeysTrait
|
||||
{
|
||||
/** @var array<non-empty-string, non-empty-string> */
|
||||
private array $values = [];
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, non-empty-string>
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
}
|
||||
22
vendor/kreait/firebase-tokens/src/JWT/Contract/Token.php
vendored
Normal file
22
vendor/kreait/firebase-tokens/src/JWT/Contract/Token.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Contract;
|
||||
|
||||
interface Token
|
||||
{
|
||||
public function __toString(): string;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function headers(): array;
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function payload(): array;
|
||||
|
||||
public function toString(): string;
|
||||
}
|
||||
75
vendor/kreait/firebase-tokens/src/JWT/CustomTokenGenerator.php
vendored
Normal file
75
vendor/kreait/firebase-tokens/src/JWT/CustomTokenGenerator.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT;
|
||||
|
||||
use Beste\Clock\SystemClock;
|
||||
use DateInterval;
|
||||
use Kreait\Firebase\JWT\Action\CreateCustomToken;
|
||||
use Kreait\Firebase\JWT\Action\CreateCustomToken\Handler;
|
||||
use Kreait\Firebase\JWT\Action\CreateCustomToken\WithLcobucciJWT;
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Kreait\Firebase\JWT\Error\CustomTokenCreationFailed;
|
||||
use Kreait\Firebase\JWT\Value\Duration;
|
||||
|
||||
final class CustomTokenGenerator
|
||||
{
|
||||
private ?string $tenantId = null;
|
||||
|
||||
public function __construct(private readonly Handler $handler)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $clientEmail
|
||||
* @param non-empty-string $privateKey
|
||||
*/
|
||||
public static function withClientEmailAndPrivateKey(string $clientEmail, string $privateKey): self
|
||||
{
|
||||
$handler = new WithLcobucciJWT($clientEmail, $privateKey, SystemClock::create());
|
||||
|
||||
return new self($handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $tenantId
|
||||
*/
|
||||
public function withTenantId(string $tenantId): self
|
||||
{
|
||||
$generator = clone $this;
|
||||
$generator->tenantId = $tenantId;
|
||||
|
||||
return $generator;
|
||||
}
|
||||
|
||||
public function execute(CreateCustomToken $action): Token
|
||||
{
|
||||
if ($this->tenantId) {
|
||||
$action = $action->withTenantId($this->tenantId);
|
||||
}
|
||||
|
||||
return $this->handler->handle($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $uid
|
||||
* @param array<non-empty-string, mixed>|null $claims
|
||||
*
|
||||
* @throws CustomTokenCreationFailed
|
||||
*/
|
||||
public function createCustomToken(string $uid, array|null $claims = null, Duration|DateInterval|string|int|null $timeToLive = null): Token
|
||||
{
|
||||
$action = CreateCustomToken::forUid($uid);
|
||||
|
||||
if ($claims !== null) {
|
||||
$action = $action->withCustomClaims($claims);
|
||||
}
|
||||
|
||||
if ($timeToLive !== null) {
|
||||
$action = $action->withTimeToLive($timeToLive);
|
||||
}
|
||||
|
||||
return $this->execute($action);
|
||||
}
|
||||
}
|
||||
18
vendor/kreait/firebase-tokens/src/JWT/Error/CustomTokenCreationFailed.php
vendored
Normal file
18
vendor/kreait/firebase-tokens/src/JWT/Error/CustomTokenCreationFailed.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Error;
|
||||
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
final class CustomTokenCreationFailed extends RuntimeException
|
||||
{
|
||||
public static function because(string $reason, ?int $code = null, ?Throwable $previous = null): self
|
||||
{
|
||||
$code = $code ?: 0;
|
||||
|
||||
return new self($reason, $code, $previous);
|
||||
}
|
||||
}
|
||||
18
vendor/kreait/firebase-tokens/src/JWT/Error/FetchingGooglePublicKeysFailed.php
vendored
Normal file
18
vendor/kreait/firebase-tokens/src/JWT/Error/FetchingGooglePublicKeysFailed.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Error;
|
||||
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
final class FetchingGooglePublicKeysFailed extends RuntimeException
|
||||
{
|
||||
public static function because(string $reason, ?int $code = null, ?Throwable $previous = null): self
|
||||
{
|
||||
$code = $code ?: 0;
|
||||
|
||||
return new self($reason, $code, $previous);
|
||||
}
|
||||
}
|
||||
28
vendor/kreait/firebase-tokens/src/JWT/Error/IdTokenVerificationFailed.php
vendored
Normal file
28
vendor/kreait/firebase-tokens/src/JWT/Error/IdTokenVerificationFailed.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Error;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
use const PHP_EOL;
|
||||
|
||||
final class IdTokenVerificationFailed extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* @param array<non-empty-string> $reasons
|
||||
*/
|
||||
public static function withTokenAndReasons(string $token, array $reasons): self
|
||||
{
|
||||
if (mb_strlen($token) > 18) {
|
||||
$token = mb_substr($token, 0, 15).'...';
|
||||
}
|
||||
|
||||
$summary = implode(PHP_EOL.'- ', $reasons);
|
||||
|
||||
$message = "The value '{$token}' is not a verified ID token:".PHP_EOL.'- '.$summary.PHP_EOL;
|
||||
|
||||
return new self($message);
|
||||
}
|
||||
}
|
||||
28
vendor/kreait/firebase-tokens/src/JWT/Error/SessionCookieVerificationFailed.php
vendored
Normal file
28
vendor/kreait/firebase-tokens/src/JWT/Error/SessionCookieVerificationFailed.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Error;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
use const PHP_EOL;
|
||||
|
||||
final class SessionCookieVerificationFailed extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* @param array<int|string, string> $reasons
|
||||
*/
|
||||
public static function withSessionCookieAndReasons(string $token, array $reasons): self
|
||||
{
|
||||
if (mb_strlen($token) > 18) {
|
||||
$token = mb_substr($token, 0, 15).'...';
|
||||
}
|
||||
|
||||
$summary = implode(PHP_EOL.'- ', $reasons);
|
||||
|
||||
$message = "The value '{$token}' is not a verified session cookie:".PHP_EOL.'- '.$summary.PHP_EOL;
|
||||
|
||||
return new self($message);
|
||||
}
|
||||
}
|
||||
42
vendor/kreait/firebase-tokens/src/JWT/GooglePublicKeys.php
vendored
Normal file
42
vendor/kreait/firebase-tokens/src/JWT/GooglePublicKeys.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT;
|
||||
|
||||
use Beste\Clock\SystemClock;
|
||||
use GuzzleHttp\Client;
|
||||
use Kreait\Firebase\JWT\Action\FetchGooglePublicKeys;
|
||||
use Kreait\Firebase\JWT\Action\FetchGooglePublicKeys\Handler;
|
||||
use Kreait\Firebase\JWT\Action\FetchGooglePublicKeys\WithGuzzle;
|
||||
use Kreait\Firebase\JWT\Contract\Expirable;
|
||||
use Kreait\Firebase\JWT\Contract\Keys;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
final class GooglePublicKeys implements Keys
|
||||
{
|
||||
private readonly ClockInterface $clock;
|
||||
|
||||
private readonly Handler $handler;
|
||||
|
||||
private ?Keys $keys = null;
|
||||
|
||||
public function __construct(?Handler $handler = null, ?ClockInterface $clock = null)
|
||||
{
|
||||
$this->clock = $clock ?: SystemClock::create();
|
||||
$this->handler = $handler ?: new WithGuzzle(new Client(['http_errors' => false]), $this->clock);
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
$keysAreThereButExpired = $this->keys instanceof Expirable && $this->keys->isExpiredAt($this->clock->now());
|
||||
|
||||
if (!$this->keys || $keysAreThereButExpired) {
|
||||
$this->keys = $this->handler->handle(FetchGooglePublicKeys::fromGoogle());
|
||||
// There is a small chance that we get keys that are already expired, but at this point we're happy
|
||||
// that we got keys at all. The next time this method gets called, we will re-fetch.
|
||||
}
|
||||
|
||||
return $this->keys->all();
|
||||
}
|
||||
}
|
||||
101
vendor/kreait/firebase-tokens/src/JWT/IdTokenVerifier.php
vendored
Normal file
101
vendor/kreait/firebase-tokens/src/JWT/IdTokenVerifier.php
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT;
|
||||
|
||||
use Beste\Clock\SystemClock;
|
||||
use GuzzleHttp\Client;
|
||||
use InvalidArgumentException;
|
||||
use Kreait\Firebase\JWT\Action\FetchGooglePublicKeys\WithGuzzle;
|
||||
use Kreait\Firebase\JWT\Action\FetchGooglePublicKeys\WithPsr6Cache;
|
||||
use Kreait\Firebase\JWT\Action\VerifyIdToken;
|
||||
use Kreait\Firebase\JWT\Action\VerifyIdToken\Handler;
|
||||
use Kreait\Firebase\JWT\Action\VerifyIdToken\WithLcobucciJWT;
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Kreait\Firebase\JWT\Error\IdTokenVerificationFailed;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
|
||||
final class IdTokenVerifier
|
||||
{
|
||||
/**
|
||||
* @var non-empty-string|null
|
||||
*/
|
||||
private ?string $expectedTenantId = null;
|
||||
|
||||
public function __construct(private readonly Handler $handler)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $projectId
|
||||
*/
|
||||
public static function createWithProjectId(string $projectId): self
|
||||
{
|
||||
$clock = SystemClock::create();
|
||||
$keyHandler = new WithGuzzle(new Client(['http_errors' => false]), $clock);
|
||||
|
||||
$keys = new GooglePublicKeys($keyHandler, $clock);
|
||||
$handler = new WithLcobucciJWT($projectId, $keys, $clock);
|
||||
|
||||
return new self($handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $projectId
|
||||
*/
|
||||
public static function createWithProjectIdAndCache(string $projectId, CacheItemPoolInterface $cache): self
|
||||
{
|
||||
$clock = SystemClock::create();
|
||||
|
||||
$innerKeyHandler = new WithGuzzle(new Client(['http_errors' => false]), $clock);
|
||||
$keyHandler = new WithPsr6Cache($innerKeyHandler, $cache, $clock);
|
||||
|
||||
$keys = new GooglePublicKeys($keyHandler, $clock);
|
||||
$handler = new WithLcobucciJWT($projectId, $keys, $clock);
|
||||
|
||||
return new self($handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $tenantId
|
||||
*/
|
||||
public function withExpectedTenantId(string $tenantId): self
|
||||
{
|
||||
$verifier = clone $this;
|
||||
$verifier->expectedTenantId = $tenantId;
|
||||
|
||||
return $verifier;
|
||||
}
|
||||
|
||||
public function execute(VerifyIdToken $action): Token
|
||||
{
|
||||
if ($this->expectedTenantId) {
|
||||
$action = $action->withExpectedTenantId($this->expectedTenantId);
|
||||
}
|
||||
|
||||
return $this->handler->handle($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $token
|
||||
*
|
||||
* @throws IdTokenVerificationFailed
|
||||
*/
|
||||
public function verifyIdToken(string $token): Token
|
||||
{
|
||||
return $this->execute(VerifyIdToken::withToken($token));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $token
|
||||
* @param int<0, max> $leewayInSeconds
|
||||
*
|
||||
* @throws IdTokenVerificationFailed
|
||||
* @throws InvalidArgumentException on invalid leeway
|
||||
*/
|
||||
public function verifyIdTokenWithLeeway(string $token, int $leewayInSeconds): Token
|
||||
{
|
||||
return $this->execute(VerifyIdToken::withToken($token)->withLeewayInSeconds($leewayInSeconds));
|
||||
}
|
||||
}
|
||||
65
vendor/kreait/firebase-tokens/src/JWT/InsecureToken.php
vendored
Normal file
65
vendor/kreait/firebase-tokens/src/JWT/InsecureToken.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT;
|
||||
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Stringable;
|
||||
|
||||
final class InsecureToken implements Token, Stringable
|
||||
{
|
||||
/**
|
||||
* @param non-empty-string $encodedString
|
||||
* @param array<non-empty-string, mixed> $headers
|
||||
* @param array<non-empty-string, mixed> $payload
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly string $encodedString,
|
||||
private readonly array $headers,
|
||||
private readonly array $payload,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->encodedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $encodedString
|
||||
* @param array<non-empty-string, mixed> $headers
|
||||
* @param array<non-empty-string, mixed> $payload
|
||||
*/
|
||||
public static function withValues(string $encodedString, array $headers, array $payload): self
|
||||
{
|
||||
return new self($encodedString, $headers, $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, mixed>
|
||||
*/
|
||||
public function headers(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, mixed>
|
||||
*/
|
||||
public function payload(): array
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->encodedString;
|
||||
}
|
||||
}
|
||||
37
vendor/kreait/firebase-tokens/src/JWT/Keys/ExpiringKeys.php
vendored
Normal file
37
vendor/kreait/firebase-tokens/src/JWT/Keys/ExpiringKeys.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Keys;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Kreait\Firebase\JWT\Contract\Expirable;
|
||||
use Kreait\Firebase\JWT\Contract\ExpirableTrait;
|
||||
use Kreait\Firebase\JWT\Contract\Keys;
|
||||
use Kreait\Firebase\JWT\Contract\KeysTrait;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ExpiringKeys implements Expirable, Keys
|
||||
{
|
||||
use KeysTrait;
|
||||
use ExpirableTrait;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
$this->expirationTime = new DateTimeImmutable('0001-01-01'); // Very distant past :)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<non-empty-string, non-empty-string> $values
|
||||
*/
|
||||
public static function withValuesAndExpirationTime(array $values, DateTimeImmutable $expirationTime): self
|
||||
{
|
||||
$keys = new self();
|
||||
$keys->values = $values;
|
||||
$keys->expirationTime = $expirationTime;
|
||||
|
||||
return $keys;
|
||||
}
|
||||
}
|
||||
36
vendor/kreait/firebase-tokens/src/JWT/Keys/StaticKeys.php
vendored
Normal file
36
vendor/kreait/firebase-tokens/src/JWT/Keys/StaticKeys.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Keys;
|
||||
|
||||
use Kreait\Firebase\JWT\Contract\Keys;
|
||||
use Kreait\Firebase\JWT\Contract\KeysTrait;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class StaticKeys implements Keys
|
||||
{
|
||||
use KeysTrait;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function empty(): self
|
||||
{
|
||||
return new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<non-empty-string, non-empty-string> $values
|
||||
*/
|
||||
public static function withValues(array $values): self
|
||||
{
|
||||
$keys = new self();
|
||||
$keys->values = $values;
|
||||
|
||||
return $keys;
|
||||
}
|
||||
}
|
||||
57
vendor/kreait/firebase-tokens/src/JWT/SecureToken.php
vendored
Normal file
57
vendor/kreait/firebase-tokens/src/JWT/SecureToken.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT;
|
||||
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Stringable;
|
||||
|
||||
final class SecureToken implements Token, Stringable
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $headers
|
||||
* @param array<string, mixed> $payload
|
||||
*/
|
||||
private function __construct(
|
||||
private readonly string $encodedString,
|
||||
private readonly array $headers,
|
||||
private readonly array $payload,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->encodedString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $headers
|
||||
* @param array<string, mixed> $payload
|
||||
*/
|
||||
public static function withValues(string $encodedString, array $headers, array $payload): self
|
||||
{
|
||||
return new self($encodedString, $headers, $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function headers(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function payload(): array
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->encodedString;
|
||||
}
|
||||
}
|
||||
101
vendor/kreait/firebase-tokens/src/JWT/SessionCookieVerifier.php
vendored
Normal file
101
vendor/kreait/firebase-tokens/src/JWT/SessionCookieVerifier.php
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT;
|
||||
|
||||
use Beste\Clock\SystemClock;
|
||||
use GuzzleHttp\Client;
|
||||
use InvalidArgumentException;
|
||||
use Kreait\Firebase\JWT\Action\FetchGooglePublicKeys\WithGuzzle;
|
||||
use Kreait\Firebase\JWT\Action\FetchGooglePublicKeys\WithPsr6Cache;
|
||||
use Kreait\Firebase\JWT\Action\VerifySessionCookie;
|
||||
use Kreait\Firebase\JWT\Action\VerifySessionCookie\Handler;
|
||||
use Kreait\Firebase\JWT\Action\VerifySessionCookie\WithLcobucciJWT;
|
||||
use Kreait\Firebase\JWT\Contract\Token;
|
||||
use Kreait\Firebase\JWT\Error\SessionCookieVerificationFailed;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
|
||||
final class SessionCookieVerifier
|
||||
{
|
||||
/**
|
||||
* @var non-empty-string|null
|
||||
*/
|
||||
private ?string $expectedTenantId = null;
|
||||
|
||||
public function __construct(private readonly Handler $handler)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $projectId
|
||||
*/
|
||||
public static function createWithProjectId(string $projectId): self
|
||||
{
|
||||
$clock = SystemClock::create();
|
||||
$keyHandler = new WithGuzzle(new Client(['http_errors' => false]), $clock);
|
||||
|
||||
$keys = new GooglePublicKeys($keyHandler, $clock);
|
||||
$handler = new WithLcobucciJWT($projectId, $keys, $clock);
|
||||
|
||||
return new self($handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $projectId
|
||||
*/
|
||||
public static function createWithProjectIdAndCache(string $projectId, CacheItemPoolInterface $cache): self
|
||||
{
|
||||
$clock = SystemClock::create();
|
||||
|
||||
$innerKeyHandler = new WithGuzzle(new Client(['http_errors' => false]), $clock);
|
||||
$keyHandler = new WithPsr6Cache($innerKeyHandler, $cache, $clock);
|
||||
|
||||
$keys = new GooglePublicKeys($keyHandler, $clock);
|
||||
$handler = new WithLcobucciJWT($projectId, $keys, $clock);
|
||||
|
||||
return new self($handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $tenantId
|
||||
*/
|
||||
public function withExpectedTenantId(string $tenantId): self
|
||||
{
|
||||
$generator = clone $this;
|
||||
$generator->expectedTenantId = $tenantId;
|
||||
|
||||
return $generator;
|
||||
}
|
||||
|
||||
public function execute(VerifySessionCookie $action): Token
|
||||
{
|
||||
if ($this->expectedTenantId) {
|
||||
$action = $action->withExpectedTenantId($this->expectedTenantId);
|
||||
}
|
||||
|
||||
return $this->handler->handle($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $sessionCookie
|
||||
*
|
||||
* @throws SessionCookieVerificationFailed
|
||||
*/
|
||||
public function verifySessionCookie(string $sessionCookie): Token
|
||||
{
|
||||
return $this->execute(VerifySessionCookie::withSessionCookie($sessionCookie));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $sessionCookie
|
||||
* @param int<0, max> $leewayInSeconds
|
||||
*
|
||||
* @throws InvalidArgumentException on invalid leeway
|
||||
* @throws SessionCookieVerificationFailed
|
||||
*/
|
||||
public function verifySessionCookieWithLeeway(string $sessionCookie, int $leewayInSeconds): Token
|
||||
{
|
||||
return $this->execute(VerifySessionCookie::withSessionCookie($sessionCookie)->withLeewayInSeconds($leewayInSeconds));
|
||||
}
|
||||
}
|
||||
28
vendor/kreait/firebase-tokens/src/JWT/Signer/None.php
vendored
Normal file
28
vendor/kreait/firebase-tokens/src/JWT/Signer/None.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Signer;
|
||||
|
||||
use Lcobucci\JWT\Signer;
|
||||
use Lcobucci\JWT\Signer\Key;
|
||||
|
||||
final class None implements Signer
|
||||
{
|
||||
public function algorithmId(): string
|
||||
{
|
||||
return 'none';
|
||||
}
|
||||
|
||||
public function sign(string $payload, Key $key): string
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return '';
|
||||
}
|
||||
|
||||
public function verify(string $expected, string $payload, Key $key): bool
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
return $expected === '';
|
||||
}
|
||||
}
|
||||
178
vendor/kreait/firebase-tokens/src/JWT/Token/InsecureParser.php
vendored
Normal file
178
vendor/kreait/firebase-tokens/src/JWT/Token/InsecureParser.php
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Token;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Lcobucci\JWT\Decoder;
|
||||
use Lcobucci\JWT\Parser as ParserInterface;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Token\DataSet;
|
||||
use Lcobucci\JWT\Token\InvalidTokenStructure;
|
||||
use Lcobucci\JWT\Token\Plain;
|
||||
use Lcobucci\JWT\Token\RegisteredClaims;
|
||||
use Lcobucci\JWT\Token\Signature;
|
||||
use Lcobucci\JWT\Token\UnsupportedHeaderFound;
|
||||
|
||||
use function array_key_exists;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* This is an almost 1:1 copy of the parser in `lcobucci/jwt`, with only the signature verification
|
||||
* removed; hence the name `InsecureParser`.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class InsecureParser implements ParserInterface
|
||||
{
|
||||
private const MICROSECOND_PRECISION = 6;
|
||||
|
||||
public function __construct(private readonly Decoder $decoder)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $jwt
|
||||
*/
|
||||
public function parse(string $jwt): Token
|
||||
{
|
||||
[$encodedHeaders, $encodedClaims] = $this->splitJwt($jwt);
|
||||
|
||||
if ($encodedHeaders === '') {
|
||||
throw new InvalidTokenStructure('The JWT string is missing the Header part');
|
||||
}
|
||||
|
||||
if ($encodedClaims === '') {
|
||||
throw new InvalidTokenStructure('The JWT string is missing the Claim part');
|
||||
}
|
||||
|
||||
$header = $this->parseHeader($encodedHeaders);
|
||||
|
||||
return new Plain(
|
||||
new DataSet($header, $encodedHeaders),
|
||||
new DataSet($this->parseClaims($encodedClaims), $encodedClaims),
|
||||
new Signature('none', 'none'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the JWT string into an array.
|
||||
*
|
||||
* @param non-empty-string $jwt
|
||||
*
|
||||
* @throws InvalidTokenStructure when JWT doesn't have all parts
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function splitJwt(string $jwt): array
|
||||
{
|
||||
return explode('.', $jwt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the header from a string.
|
||||
*
|
||||
* @param non-empty-string $data
|
||||
*
|
||||
* @throws InvalidTokenStructure when parsed content isn't an array
|
||||
* @throws UnsupportedHeaderFound when an invalid header is informed
|
||||
*
|
||||
* @return array<non-empty-string, mixed>
|
||||
*/
|
||||
private function parseHeader(string $data): array
|
||||
{
|
||||
$header = $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
|
||||
|
||||
if (!is_array($header)) {
|
||||
throw InvalidTokenStructure::arrayExpected('headers');
|
||||
}
|
||||
|
||||
$this->guardAgainstEmptyStringKeys($header, 'headers');
|
||||
|
||||
if (array_key_exists('enc', $header)) {
|
||||
throw UnsupportedHeaderFound::encryption();
|
||||
}
|
||||
|
||||
if (!array_key_exists('typ', $header)) {
|
||||
$header['typ'] = 'JWT';
|
||||
}
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the claim set from a string.
|
||||
*
|
||||
* @param non-empty-string $data
|
||||
*
|
||||
* @throws InvalidTokenStructure when parsed content isn't an array or contains non-parseable dates
|
||||
*
|
||||
* @return array<non-empty-string, mixed>
|
||||
*/
|
||||
private function parseClaims(string $data): array
|
||||
{
|
||||
$claims = $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));
|
||||
|
||||
if (!is_array($claims)) {
|
||||
throw InvalidTokenStructure::arrayExpected('claims');
|
||||
}
|
||||
|
||||
$this->guardAgainstEmptyStringKeys($claims, 'claims');
|
||||
|
||||
if (array_key_exists(RegisteredClaims::AUDIENCE, $claims)) {
|
||||
$claims[RegisteredClaims::AUDIENCE] = (array) $claims[RegisteredClaims::AUDIENCE];
|
||||
}
|
||||
|
||||
foreach (RegisteredClaims::DATE_CLAIMS as $claim) {
|
||||
if (!array_key_exists($claim, $claims)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_scalar($claims[$claim])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_bool($claims[$claim])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$claims[$claim] = $this->convertDate($claims[$claim]);
|
||||
}
|
||||
|
||||
return $claims;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array-key, mixed> $array
|
||||
* @param non-empty-string $part
|
||||
*
|
||||
* @phpstan-assert array<non-empty-string, mixed> $array
|
||||
*/
|
||||
private function guardAgainstEmptyStringKeys(array $array, string $part): void
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
if ($key === '') {
|
||||
throw InvalidTokenStructure::arrayExpected($part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @throws InvalidTokenStructure */
|
||||
private function convertDate(int|float|string $timestamp): DateTimeImmutable
|
||||
{
|
||||
if (!is_numeric($timestamp)) {
|
||||
throw InvalidTokenStructure::dateIsNotParseable($timestamp);
|
||||
}
|
||||
|
||||
$normalizedTimestamp = number_format((float) $timestamp, self::MICROSECOND_PRECISION, '.', '');
|
||||
|
||||
$date = DateTimeImmutable::createFromFormat('U.u', $normalizedTimestamp);
|
||||
|
||||
if ($date === false) {
|
||||
throw InvalidTokenStructure::dateIsNotParseable($normalizedTimestamp);
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
30
vendor/kreait/firebase-tokens/src/JWT/Token/Parser.php
vendored
Normal file
30
vendor/kreait/firebase-tokens/src/JWT/Token/Parser.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Token;
|
||||
|
||||
use Kreait\Firebase\JWT\Util;
|
||||
use Lcobucci\JWT\Decoder;
|
||||
use Lcobucci\JWT\Parser as ParserInterface;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Token\Parser as SecureParser;
|
||||
|
||||
final class Parser implements ParserInterface
|
||||
{
|
||||
private ParserInterface $parser;
|
||||
|
||||
public function __construct(public readonly Decoder $decoder)
|
||||
{
|
||||
if (Util::authEmulatorHost() !== '') {
|
||||
$this->parser = new InsecureParser($decoder);
|
||||
} else {
|
||||
$this->parser = new SecureParser($decoder);
|
||||
}
|
||||
}
|
||||
|
||||
public function parse(string $jwt): Token
|
||||
{
|
||||
return $this->parser->parse($jwt);
|
||||
}
|
||||
}
|
||||
38
vendor/kreait/firebase-tokens/src/JWT/Util.php
vendored
Normal file
38
vendor/kreait/firebase-tokens/src/JWT/Util.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT;
|
||||
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Util
|
||||
{
|
||||
/**
|
||||
* @param non-empty-string $name
|
||||
*/
|
||||
public static function getenv(string $name): ?string
|
||||
{
|
||||
$value = $_SERVER[$name] ?? $_ENV[$name] ?? getenv($name);
|
||||
|
||||
if (is_string($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function authEmulatorHost(): string
|
||||
{
|
||||
$emulatorHost = self::getenv('FIREBASE_AUTH_EMULATOR_HOST');
|
||||
|
||||
if (!in_array($emulatorHost, [null, ''], true)) {
|
||||
return $emulatorHost;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
187
vendor/kreait/firebase-tokens/src/JWT/Value/Duration.php
vendored
Normal file
187
vendor/kreait/firebase-tokens/src/JWT/Value/Duration.php
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Kreait\Firebase\JWT\Value;
|
||||
|
||||
use DateInterval;
|
||||
use DateTimeImmutable;
|
||||
use InvalidArgumentException;
|
||||
use Stringable;
|
||||
use Throwable;
|
||||
|
||||
use function is_int;
|
||||
|
||||
/**
|
||||
* Adapted duration class from gamez/duration.
|
||||
*
|
||||
* @see https://github.com/jeromegamez/duration-php
|
||||
*/
|
||||
final class Duration implements Stringable
|
||||
{
|
||||
public const NONE = 'PT0S';
|
||||
|
||||
private function __construct(private readonly DateInterval $value)
|
||||
{
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
public static function make(self|DateInterval|int|string $value): self
|
||||
{
|
||||
if ($value instanceof self) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($value instanceof DateInterval) {
|
||||
return self::fromDateInterval($value);
|
||||
}
|
||||
|
||||
if (is_int($value)) {
|
||||
return self::inSeconds($value);
|
||||
}
|
||||
|
||||
if (mb_strpos($value, 'P') === 0) {
|
||||
return self::fromDateIntervalSpec($value);
|
||||
}
|
||||
|
||||
try {
|
||||
$interval = DateInterval::createFromDateString($value);
|
||||
} catch (Throwable) {
|
||||
throw new InvalidArgumentException("Unable to determine a duration from '{$value}'");
|
||||
}
|
||||
|
||||
if ($interval === false) {
|
||||
throw new InvalidArgumentException("Unable to determine a duration from '{$value}'");
|
||||
}
|
||||
|
||||
$duration = self::fromDateInterval($interval);
|
||||
// If the string doesn't contain a zero, but the result equals to zero
|
||||
// the value must be invalid.
|
||||
if (mb_strpos($value, '0') !== false) {
|
||||
return $duration;
|
||||
}
|
||||
|
||||
if (!$duration->equals(self::none())) {
|
||||
return $duration;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Unable to determine a duration from '{$value}'");
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function inSeconds(int $seconds): self
|
||||
{
|
||||
if ($seconds < 0) {
|
||||
throw new InvalidArgumentException('A duration can not be negative');
|
||||
}
|
||||
|
||||
return self::fromDateIntervalSpec('PT'.$seconds.'S');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function fromDateIntervalSpec(string $spec): self
|
||||
{
|
||||
try {
|
||||
$interval = new DateInterval($spec);
|
||||
} catch (Throwable) {
|
||||
throw new InvalidArgumentException("'{$spec}' is not a valid DateInterval specification");
|
||||
}
|
||||
|
||||
return self::fromDateInterval($interval);
|
||||
}
|
||||
|
||||
public static function fromDateInterval(DateInterval $interval): self
|
||||
{
|
||||
$now = new DateTimeImmutable();
|
||||
$then = $now->add($interval);
|
||||
|
||||
if ($then < $now) {
|
||||
throw new InvalidArgumentException('A duration can not be negative');
|
||||
}
|
||||
|
||||
return new self($interval);
|
||||
}
|
||||
|
||||
public static function none(): self
|
||||
{
|
||||
return self::fromDateIntervalSpec(self::NONE);
|
||||
}
|
||||
|
||||
public function value(): DateInterval
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function isLargerThan(self|DateInterval|int|string $other): bool
|
||||
{
|
||||
return 1 === $this->compareTo($other);
|
||||
}
|
||||
|
||||
public function equals(self|DateInterval|int|string $other): bool
|
||||
{
|
||||
return 0 === $this->compareTo($other);
|
||||
}
|
||||
|
||||
public function isSmallerThan(self|DateInterval|int|string $other): bool
|
||||
{
|
||||
return -1 === $this->compareTo($other);
|
||||
}
|
||||
|
||||
public function compareTo(self|DateInterval|int|string $other): int
|
||||
{
|
||||
$other = self::make($other);
|
||||
|
||||
$now = self::now();
|
||||
|
||||
return $now->add($this->value) <=> $now->add($other->value);
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return self::toDateIntervalSpec(self::normalizeInterval($this->value));
|
||||
}
|
||||
|
||||
private static function now(): DateTimeImmutable
|
||||
{
|
||||
return new DateTimeImmutable('@'.time());
|
||||
}
|
||||
|
||||
private static function normalizeInterval(DateInterval $value): DateInterval
|
||||
{
|
||||
$now = self::now();
|
||||
$then = $now->add($value);
|
||||
|
||||
return $now->diff($then);
|
||||
}
|
||||
|
||||
private static function toDateIntervalSpec(DateInterval $value): string
|
||||
{
|
||||
$spec = 'P';
|
||||
$spec .= 0 !== $value->y ? $value->y.'Y' : '';
|
||||
$spec .= 0 !== $value->m ? $value->m.'M' : '';
|
||||
$spec .= 0 !== $value->d ? $value->d.'D' : '';
|
||||
|
||||
$spec .= 'T';
|
||||
$spec .= 0 !== $value->h ? $value->h.'H' : '';
|
||||
$spec .= 0 !== $value->i ? $value->i.'M' : '';
|
||||
$spec .= 0 !== $value->s ? $value->s.'S' : '';
|
||||
|
||||
if ('T' === mb_substr($spec, -1)) {
|
||||
$spec = mb_substr($spec, 0, -1);
|
||||
}
|
||||
|
||||
if ('P' === $spec) {
|
||||
return self::NONE;
|
||||
}
|
||||
|
||||
return $spec;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user