-
-
Save avosalmon/b4fd7bb5c4666ed39cdc06c9767afb84 to your computer and use it in GitHub Desktop.
<?php | |
namespace App\Services; | |
use UnexpectedValueException; | |
use Firebase\JWT\JWT; | |
use Illuminate\Support\Facades\Cache; | |
use Illuminate\Support\Facades\Http; | |
use Illuminate\Support\Str; | |
class FirebaseToken | |
{ | |
/** | |
* The list of allowed signing algorithms used in the JWT. | |
* | |
* @var array | |
*/ | |
const ALLOWED_ALGOS = ['RS256']; | |
/** | |
* The public key used for verifying that the token is signed by the right private key. | |
* | |
* @var string | |
*/ | |
const PUBLIC_KEY_URL = 'https://www.googleapis.com/robot/v1/metadata/x509/[email protected]'; | |
/** | |
* The cache key for Firebase JWT public keys. | |
* | |
* @var string | |
*/ | |
const CACHE_KEY = 'FIREBASE_JWT_PUBLIC_KEYS'; | |
/** | |
* Firebase ID token. | |
* | |
* @var string | |
*/ | |
private string $token; | |
/** | |
* @param string $token | |
*/ | |
public function __construct(string $token) | |
{ | |
$this->token = $token; | |
} | |
/** | |
* Verify the ID token and return the decoded payload. | |
* | |
* @param string $projectId | |
* @return object | |
* @throws UnexpectedValueException|Exception | |
*/ | |
public function verify(string $projectId): object | |
{ | |
$keys = $this->getPublicKeys(); | |
$payload = JWT::decode($this->token, $keys, self::ALLOWED_ALGOS); | |
$this->validatePayload($payload, $projectId); | |
return $payload; | |
} | |
/** | |
* Fetch JWT public keys. | |
* | |
* @return array | |
*/ | |
private function getPublicKeys(): array | |
{ | |
if (Cache::has(self::CACHE_KEY)) { | |
return Cache::get(self::CACHE_KEY); | |
} | |
$response = Http::get(self::PUBLIC_KEY_URL); | |
if (!$response->successful()) { | |
throw new \Exception('Failed to fetch JWT public keys.'); | |
} | |
$publicKeys = $response->json(); | |
$cacheControl = $response->header('Cache-Control'); | |
$maxAge = Str::of($cacheControl)->match('/max-age=(\d+)/'); | |
Cache::put(self::CACHE_KEY, $publicKeys, now()->addSeconds($maxAge)); | |
return $publicKeys; | |
} | |
/** | |
* Validate decoded payload. | |
* | |
* @param object $payload | |
* @param string $projectId | |
* @return void | |
* @throws UnexpectedValueException | |
* | |
* @see https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_a_third-party_jwt_library | |
*/ | |
private function validatePayload(object $payload, string $projectId): void | |
{ | |
if ($payload->aud !== $projectId) { | |
throw new UnexpectedValueException("Invalid audience: {$payload->aud}"); | |
} | |
if ($payload->iss !== "https://securetoken.google.com/{$projectId}") { | |
throw new UnexpectedValueException("Invalid issuer: {$payload->iss}"); | |
} | |
// `sub` corresponds to the `uid` of the Firebase user. | |
if (empty($payload->sub)) { | |
throw new UnexpectedValueException('Payload subject is empty.'); | |
} | |
} | |
} |
@g-calcagno The php-jwt library validates kid
. So, we just need to pass public keys.
https://github.com/firebase/php-jwt/blob/master/src/JWT.php#L114-L123
Good afternoon, thanks for the reply and the material. I have been trying to integrate it, but I had some problems probably due to my ignorance. The constant 'PUBLIC_KEY_URL' is obtained from the JSON (from the google service account) "client_x509_cert_url".
I overwritten my passport private key with the private key from the JSON and generated a public key from the private key. Even so, I keep getting a KID from the KEY [kid]. In the url provided by google, at first glance you can already see that no index of the key segments has the same key [id] as the private key, always returning a '' "kid" invalid, unable to lookup correct key " I do not quite understand how it works.
@ g-calcagno Are you using Laravel Passport? I'm not using it but only using Firebase Auth.
Following this guide, I fetched the JSON that contains public keys and passed it to the JWT::decode method. It will decode the JWT and check whether the kid
claim in the JWT header matches one of the keys in the JSON as mentioned in the guide.
so, since Firebase-JWT has major break changes related to Key() i did some changes to this code to make this work well in my API, if you want i can send you this or open a PR if you allow me.
If anyone is looking dor this to use with today library there is the changes i did:
https://gist.github.com/rochaeterno/cfb1bb0c8987201c1e4b78d885f607f0
has defined 'alg', but and 'kid' ?