Thomas
Thomas

Reputation: 1354

Migrate an existing project to Firebase : Authentication handling & Preserve existing JWT

I've an existing project that is built on :

I'm in process of rewriting the frontend to move to Angular 8 and I want to leverage the firebase features.

I'm currently working on integrating the authentication feature (username/password, google, twitter, facebook etc...)

And I'm thinking about my next step :

I was imagining something like :

Would this work ? any suggestions ?

Upvotes: 2

Views: 225

Answers (1)

Thomas
Thomas

Reputation: 1354

So here is how I did it :

On the client side (Angular Application), I use ngx-auth-firebaseui, to display the login form.

On the form, I set the call back that handle an authentication success:

login.component.html

<ngx-auth-firebaseui (onSuccess)="successfulLogin($event)"
                     (onError)="printError($event)">
</ngx-auth-firebaseui>

The code of the callback is here. From the Firebase User object, I call the method getIdTokenResult() to get the firebase JWT. And I then call my php backend via the authenticationService

login.component.ts

successfulLogin(user:User) {

    console.log(user);
    user.getIdTokenResult().then((idTokenResult:IdTokenResult)=> {
      console.log(idTokenResult.token);

      let token : string = idTokenResult.token;
      let rcqJWTToken = this.authenticationService.authenticate( { token } as FirebaseJWT);


      rcqJWTToken.subscribe((rcqToken:string)=> console.log("RCQ JWT Token : '"+rcqToken+"'"));

      this.router.navigate['/welcome'];
    });    
}

Here I transmit the Firebase JWT to my php backend authentication.service.ts

  authenticate(firebaseJWTToken:FirebaseJWT):Observable<String>{

    return this.http.post<String>(this.authenticationURL, firebaseJWTToken, httpOptions)
    .pipe(
      tap(_ => console.log('fetched RCQ JWT')),
      catchError(this.handleError<String>('authenticate', ""))
    );
  }

On the server side :

I set the GOOGLE_APPLICATION_CREDENTIALS as an env var, like it is when deployed on Google App Engine

putenv("GOOGLE_APPLICATION_CREDENTIALS=/Users/myuser/.cred/GCP-project-ID.json");

I use Slimframework, so I instanciate the Firebase object in my dependencies.php file. With the env var, Firebase do not need anything else. check here : https://firebase-php.readthedocs.io/en/4.32.0/setup.html


use Kreait\Firebase;
use Kreait\Firebase\Factory;

/**
 * Used to authenticate a firebase user, from it's Firebase JWT
 * @property Firebase $firebase
 * @param    \Slim\Container $c
 * @return   Firebase
 */
$container['firebase'] = function (\Slim\Container $c)
{
  $firebase = (new Factory)->create();
  return $firebase;
};

and here comes the route where the authentication is done :

$app->post(getPrefix().'/firebase-authenticate', function($request, $response, $args) use ($app)
{
  $token    = $this->clientInputValidator->validateString("token"   , $request->getParsedBodyParam("token"    ), 1500 , true );
  $username = "";
  Logger::dataForLogging(new LoggingEntity(null,  ["username"=>$username]));

  try
  {
    $verifiedIdToken = $this->firebase->getAuth()->verifyIdToken($token);
  }
  catch (InvalidToken $e)
  {
    $response401 = $response->withStatus(401);
    $response401->getBody()->write(json_encode(["error" =>"Authentication error"]));

    $this->logger->error("Firebase authentication error", array('username' => $username, 'token' => $token));

    return $response401;
  }
  $uid  = $verifiedIdToken->getClaim('sub');
  $user = $this->firebase->getAuth()->getUser($uid);


  $this->logger->debug("Firebase JWT checked successfully", array('uid' => $uid,'user' => $user));
});

The main thing is here : $verifiedIdToken = $this->firebase->getAuth()->verifyIdToken($token);

And the user details are retrieved here:

$user = $this->firebase->getAuth()->getUser($uid);

I can get the uid, email, and all the info in the Firebase JWT.

the token itself has a TTL of 1 hour, so I'll probably have to refresh the token and revalidate it against my backend.

Upvotes: 0

Related Questions