Flutter Web, a Dart gRPC server and Firebase Authentication


OK, not the sexiest title, but here is a quick overview of how you can create a flutter web application (or mobile for that matter, but more on that later), using Firebase authentication, talking to a pure Dart server using gRPC.  


The goal here is to:


  • Create a SPA Flutter web application.

  • Leverage Firebase authentication. It's inexpensive (essentially free for most users), and supports a wide variety of social login providers.

  • Support a native Dart Server, using gRPC and protobufs.

  • Proxy from gRPC web to gRPC "native" using envoy.


Putting it together, it looks something like this:





Why the choice of gRPC here, instead of json/REST?  This comes down to personal preference, but I have always liked the contract first approach of gRPC of defining interfaces using protobufs. In theory, you can do this with OpenAPI - but I've yet to see a moderately complex interface where the generated API stubs look even remotely usable. gRPC does a good job of generating usable client and server stubs for a wide variety of languages. It's also fast. If json is your thing, protobuf3 provides a json serialization option.


gRPC currently rides over http/2 - which web applications do not support. This is where grpc-Web comes in, which defines a mapping between http and the "native" gRPC protocol. Envoy does the proxying for us here. It's simple to configure starting with the sample envoy.yaml.  Aside - if you have a mobile or desktop application that speaks http/2 you don't need the envoy proxy as the application can make direct gRPC calls against the server.


Basic Flow


  1. The flutter web app authenticates using the FlutterFire authentication plugin. The user will be prompted for their credentials or will use social login. I used the sample FlutterFire Auth application to test this.

  2. Once the user has authenticated, the application gets back an OpenID connect id token.

  3. The application sends the id token over grpc-Web to the Dart server (via the envoy proxy).

  4. The idToken must be validated! I used the excellent openid_client library on pub.dev for validation. This library checks the signature on the token to ensure it was issued by Firebase, and is still valid.  

  5. The id token contains a number of JWT claims that our server will use. The user id assigned by firebase is unique, and makes an excellent primary key for your application.  You can also extract the user's email, photo, and other claims.


You probably don't want to pass the id token on every call from your client to the server. It's large, and you really should not be reusing it. Instead, your server can create a simple session, using a secure random string as the session identifier. There are many approaches here, starting with a simple in memory hashmap of session ids, and a periodic timer to purge older sessions. 


In my test application, I elected to send the id token in a simple authN request:



If the token is valid, the server creates a session and returns it to the client. 


For subsequent calls, you should pass the session id as "metadata" (call options) rather than specify it in every single Request protobuf message. 


On the client, you can send the session id metadata as follows:



On the server we have a gRPC interceptor that looks for a session id. If the session is valid, the call proceeds. If not, the interceptor returns a gRPC error to the client. This looks something like this:




And there you have it. If you have questions, let me know in the comments.


Comments

Popular posts from this blog

Introducing ds-operator, the ForgeRock Directory Services Operator for Kubernetes

Automating OpenDJ backups on Kubernetes

Deploying the ForgeRock platform on Kubernetes using Skaffold and Kustomize