Created
August 22, 2021 16:33
-
-
Save avosalmon/b4fd7bb5c4666ed39cdc06c9767afb84 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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.'); | |
} | |
} | |
} |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@ 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.