leandro_rm
leandro_rm

Reputation: 11

Firebase auth with custom token from php

I have a PHP application and I am trying to generate a token to authenticate users so that they can access Firebase from the browser.

I have generated a private key from service account in Firebase console and I use the firebase/php-jwt library. When trying to access with the signInWithCustomToken method from javascript I always get the error>

The custom token format is incorrect. Please see the documentation

The code with which I generate the token is:

use Firebase\JWT\JWT;

require_once("php-jwt-master/src/JWT.php");

$service_account_email = "[email protected]"; // Dots are app name

$key = "-----BEGIN PRIVATE KEY-----\n..........\n-----END PRIVATE KEY-----\n"; // Dots are private key from the downloaded file

$time = time();

$token = array( 'iat' => $time,
                'exp' => $time + 3000,
                'uid' => '1',
                'aud' => 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
                'iss' => $service_account_email,
                'sub' => $service_account_email
               );
               

$jwt = JWT::encode($token, $key, 'HS256');

The javascript code is:

function login(){

  var token = document.getElementById("token").value;

  firebase.auth().signInWithCustomToken(token)      
    .then((user) => {           
      console.log("Autenticado");         
    })      
    .catch((error) => {         
      var errorCode = error.code;
      var errorMessage = error.message;
      
      console.log(errorCode + " - " + errorMessage);          
    });
}

I have checked the jwt with jwt.io tool and it seems to be correct.

I am not sure in the key format, I have tried with the header and the "\ n" in a single line as in the file and also without the "\ n" and using the returns in a multiline variable with "<<<".

I have also tried using the encode function with or without the parameter 'HS256'. But I always get the same error.

Thanks in advance for any response.

Upvotes: 1

Views: 2316

Answers (2)

leandro_rm
leandro_rm

Reputation: 11

Solved, changing the name of the algorith from HS256 to RS256!. Thanks.

Upvotes: 0

Andrew
Andrew

Reputation: 855

I can understand why this might be a little confusing as some of the documentation and code contradict each other. Even the firebase/php-jwt/src/JWT.php file mentions that HS256 is a supported algorithm.

class JWT
    . . .
    public static $supported_algs = array(
        'ES256' => array('openssl', 'SHA256'),
        'HS256' => array('hash_hmac', 'SHA256'),
        'HS384' => array('hash_hmac', 'SHA384'),
        'HS512' => array('hash_hmac', 'SHA512'),
        'RS256' => array('openssl', 'SHA256'),
        'RS384' => array('openssl', 'SHA384'),
        'RS512' => array('openssl', 'SHA512'),
    );

However, according to the Google OAuth 2.0 documentation, it mentions explicitly that:

The only signing algorithm supported by the Google OAuth 2.0 Authorization Server is RSA using SHA-256 hashing algorithm. This is expressed as RS256 in the alg field in the JWT header.

I tested this on my Linux machine by following these documents:

I installed all of the requirements:

$ sudo apt update -y && sudo apt upgrade -y
$ sudo apt install php libapache2-mod-php
$ sudo systemctl start apache2
$ sudo apt install php-cli unzip
$ curl -sS https://getcomposer.org/installer -o composer-setup.php
$ HASH=`curl -sS https://composer.github.io/installer.sig`
$ php -r "if (hash_file('SHA384', 'composer-setup.php') === '$HASH') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$ sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
$ composer require firebase/php-jwt

I created a jwt-auth.php file:

<?php
// Requires: composer require firebase/php-jwt
use Firebase\JWT\JWT;

require __DIR__ . '/vendor/autoload.php';

// Get your service account's email address and private key from the JSON key file
$service_account_email = "firebase-adminsdk-<REDACTED>@<REDACTED>.iam.gserviceaccount.com";

$privateKey = <<<EOD
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQC8kGa . . . <REDACTED> . . .
B2zNzvrlgRmgBrklMTrMYgm1NPcW+bRLGcwgW2PTvNM=
-----END RSA PRIVATE KEY-----
EOD;

$publicKey = <<<EOD
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3D . . . <REDACTED> . . .
ehde/zUxo6UvS7UrBQIDAQAB
-----END PUBLIC KEY-----
EOD;

$now_seconds = time();

$payload = array(
    "iss" => $service_account_email,
    "sub" => $service_account_email,
    "aud" => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
    "iat" => $now_seconds,
    "exp" => $now_seconds+(60*60),  // Maximum expiration time is one hour
    "uid" => "1"
);

$jwt = JWT::encode($payload, $privateKey, 'RS256');
echo "Encode:\n" . print_r($jwt, true) . "\n";

$decoded = JWT::decode($jwt, $publicKey, array('RS256'));

/*
 NOTE: This will now be an object instead of an associative array. To get
 an associative array, you will need to cast it as such:
*/

$decoded_array = (array) $decoded;
echo "Decode:\n" . print_r($decoded_array, true) . "\n";

?>

Ran:

$ php jwt-auth.php

And I was able to authenticate:

Encode:
eyJ0eXAiOiJKV1QiL . . . <REDACTED> . . .ZnverW8YFTaC_ZVFnI
Decode:
Array
(
    [iss] => firebase-adminsdk-<REDACTED>@<REDACTED>.iam.gserviceaccount.com
    [sub] => firebase-adminsdk-<REDACTED>@<REDACTED>.iam.gserviceaccount.com
    [aud] => https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit
    [iat] => 1618406214
    [exp] => 1618409814
    [uid] => 1
)

Upvotes: 2

Related Questions