How to verify firebase ID token with PHP(JWT)?

前端 未结 4 1384
盖世英雄少女心
盖世英雄少女心 2020-12-14 17:32

I have a shared hosting plan which has only PHP(no Java, no node.js). I need to send firebase ID token from my android app and verify it by PHP-JWT.

I am following t

4条回答
  •  [愿得一人]
    2020-12-14 18:17

    Working example of accepted answer. differences of note:

    • Tested and Working

    • works in non-class environments

    • More code showing how to use it for Firebase (simple, one-liner to send the code for verification)

    • UnexpectedValueException covers all sorts of errors you might see (such as expired/invalid keys)

    • Well commented and easy-to-follow

    • returns an array of VERIFIED data from the Firebase Token (you can securely use this data for anything you need)

    This is basically a broken-out, easy-to-read/understand PHP version of https://firebase.google.com/docs/auth/admin/verify-id-tokens

    NOTE: You can use the getKeys(), refreshKeys(), checkKeys() functions to generate keys for use in any secure api situation (mimicking the features of 'verify_firebase_token' function with your own).

    USE:

    $verified_array = verify_firebase_token()
    

    THE CODE:

    $keys_file = "securetoken.json"; // the file for the downloaded public keys
    $cache_file = "pkeys.cache"; // this file contains the next time the system has to revalidate the keys
    //////////  MUST REPLACE  with your own!
    $fbProjectId = ;
    
    /////// FROM THIS POINT, YOU CAN COPY/PASTE - NO CHANGES REQUIRED
    ///  (though read through for various comments!)
    function verify_firebase_token($token = '')
    {
        global $fbProjectId;
        $return = array();
        $userId = $deviceId = "";
        checkKeys();
        $pkeys_raw = getKeys();
        if (!empty($pkeys_raw)) {
            $pkeys = json_decode($pkeys_raw, true);
            try {
                $decoded = \Firebase\JWT\JWT::decode($token, $pkeys, ["RS256"]);
                if (!empty($_GET['debug'])) {
                    echo "
    BOTTOM LINE - the decoded data
    "; print_r($decoded); echo "
    "; } if (!empty($decoded)) { // do all the verifications Firebase says to do as per https://firebase.google.com/docs/auth/admin/verify-id-tokens // exp must be in the future $exp = $decoded->exp > time(); // ist must be in the past $iat = $decoded->iat < time(); // aud must be your Firebase project ID $aud = $decoded->aud == $fbProjectId; // iss must be "https://securetoken.google.com/" $iss = $decoded->iss == "https://securetoken.google.com/$fbProjectId"; // sub must be non-empty and is the UID of the user or device $sub = $decoded->sub; if ($exp && $iat && $aud && $iss && !empty($sub)) { // we have a confirmed Firebase user! // build an array with data we need for further processing $return['UID'] = $sub; $return['email'] = $decoded->email; $return['email_verified'] = $decoded->email_verified; $return['name'] = $decoded->name; $return['picture'] = $decoded->photo; } else { if (!empty($_GET['debug'])) { echo "NOT ALL THE THINGS WERE TRUE!
    "; echo "exp is $exp
    ist is $iat
    aud is $aud
    iss is $iss
    sub is $sub
    "; } /////// DO FURTHER PROCESSING IF YOU NEED TO // (if $sub is false you may want to still return the data or even enter the verified user into the database at this point.) } } } catch (\UnexpectedValueException $unexpectedValueException) { $return['error'] = $unexpectedValueException->getMessage(); if (!empty($_GET['debug'])) { echo "
    ERROR! " . $unexpectedValueException->getMessage() . "
    "; } } } return $return; } /** * Checks whether new keys should be downloaded, and retrieves them, if needed. */ function checkKeys() { global $cache_file; if (file_exists($cache_file)) { $fp = fopen($cache_file, "r+"); if (flock($fp, LOCK_SH)) { $contents = fread($fp, filesize($cache_file)); if ($contents > time()) { flock($fp, LOCK_UN); } elseif (flock($fp, LOCK_EX)) { // upgrading the lock to exclusive (write) // here we need to revalidate since another process could've got to the LOCK_EX part before this if (fread($fp, filesize($cache_file)) <= time()) { refreshKeys($fp); } flock($fp, LOCK_UN); } else { throw new \RuntimeException('Cannot refresh keys: file lock upgrade error.'); } } else { // you need to handle this by signaling error throw new \RuntimeException('Cannot refresh keys: file lock error.'); } fclose($fp); } else { refreshKeys(); } } /** * Downloads the public keys and writes them in a file. This also sets the new cache revalidation time. * @param null $fp the file pointer of the cache time file */ function refreshKeys($fp = null) { global $keys_file; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 1); $data = curl_exec($ch); $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $headers = trim(substr($data, 0, $header_size)); $raw_keys = trim(substr($data, $header_size)); if (preg_match('/age:[ ]+?(\d+)/i', $headers, $age_matches) === 1) { $age = $age_matches[1]; if (preg_match('/cache-control:.+?max-age=(\d+)/i', $headers, $max_age_matches) === 1) { $valid_for = $max_age_matches[1] - $age; $fp = fopen($keys_file, "w"); ftruncate($fp, 0); fwrite($fp, "" . (time() + $valid_for)); fflush($fp); // $fp will be closed outside, we don't have to $fp_keys = fopen($keys_file, "w"); if (flock($fp_keys, LOCK_EX)) { fwrite($fp_keys, $raw_keys); fflush($fp_keys); flock($fp_keys, LOCK_UN); } fclose($fp_keys); } } } /** * Retrieves the downloaded keys. * This should be called anytime you need the keys (i.e. for decoding / verification). * @return null|string */ function getKeys() { global $keys_file; $fp = fopen($keys_file, "r"); $keys = null; if (flock($fp, LOCK_SH)) { $keys = fread($fp, filesize($keys_file)); flock($fp, LOCK_UN); } fclose($fp); return $keys; }

提交回复
热议问题