Skip to content

Auth

To ensure that the users of your Mesop application are authenticated, this guide provides a detailed, step-by-step process on how to integrate Firebase Authentication with Mesop using a web component.

Mesop is designed to be auth provider agnostic, allowing you to integrate any auth library you prefer, whether it's on the client-side (JavaScript) or server-side (Python). You can support sign-ins, including social sign-ins like Google's or any others that you prefer. The general approach involves signing in on the client-side first, then transmitting an auth token to the server-side.

Firebase Authentication

This guide will walk you through the process of integrating Firebase Authentication with Mesop using a custom web component.

Pre-requisites: You will need to create a Firebase account and project. It's free to get started with Firebase and use Firebase auth for small projects, but refer to the pricing page for the most up-to-date information.

We will be using three libraries from Firebase to build an end-to-end auth flow:

  • Firebase Web SDK: Allows you to call Firebase services from your client-side JavaScript code.
  • FirebaseUI Web: Provides a simple, customizable auth UI integrated with the Firebase Web SDK.
  • Firebase Admin SDK (Python): Provides server-side libraries to integrate Firebase services, including Authentication, into your Python applications.

Let's dive into how we will use each one in our Mesop app.

Web component

The Firebase Authentication web component is a custom component built for handling the user authentication process. It's implemented using Lit, a simple library for building lightweight web components.

JS code

firebase_auth_component.js
import {
  LitElement,
  html,
} from 'https://cdn.jsdelivr.net/gh/lit/dist@3/core/lit-core.min.js';

import 'https://www.gstatic.com/firebasejs/10.0.0/firebase-app-compat.js';
import 'https://www.gstatic.com/firebasejs/10.0.0/firebase-auth-compat.js';
import 'https://www.gstatic.com/firebasejs/ui/6.1.0/firebase-ui-auth.js';

// TODO: replace this with your web app's Firebase configuration
const firebaseConfig = {
  apiKey: 'AIzaSyAQR9T7sk1lElXTEUBYHx7jv7d_Bs2zt-s',
  authDomain: 'mesop-auth-test.firebaseapp.com',
  projectId: 'mesop-auth-test',
  storageBucket: 'mesop-auth-test.appspot.com',
  messagingSenderId: '565166920272',
  appId: '1:565166920272:web:4275481621d8e5ba91b755',
};

// Initialize Firebase
firebase.initializeApp(firebaseConfig);

const uiConfig = {
  // TODO: change this to your Mesop page path.
  signInSuccessUrl: '/web_component/firebase_auth/firebase_auth_app',
  signInFlow: 'popup',
  signInOptions: [firebase.auth.GoogleAuthProvider.PROVIDER_ID],
  // tosUrl and privacyPolicyUrl accept either url string or a callback
  // function.
  // Terms of service url/callback.
  tosUrl: '<your-tos-url>',
  // Privacy policy url/callback.
  privacyPolicyUrl: () => {
    window.location.assign('<your-privacy-policy-url>');
  },
};

// Initialize the FirebaseUI Widget using Firebase.
const ui = new firebaseui.auth.AuthUI(firebase.auth());

class FirebaseAuthComponent extends LitElement {
  static properties = {
    isSignedIn: {type: Boolean},
    authChanged: {type: String},
  };

  constructor() {
    super();
    this.isSignedIn = false;
  }

  createRenderRoot() {
    // Render in light DOM so firebase-ui-auth works.
    return this;
  }

  firstUpdated() {
    firebase.auth().onAuthStateChanged(
      async (user) => {
        if (user) {
          this.isSignedIn = true;
          const token = await user.getIdToken();
          this.dispatchEvent(new MesopEvent(this.authChanged, token));
        } else {
          this.isSignedIn = false;
          this.dispatchEvent(new MesopEvent(this.authChanged, ''));
        }
      },
      (error) => {
        console.log(error);
      },
    );

    ui.start('#firebaseui-auth-container', uiConfig);
  }

  signOut() {
    firebase.auth().signOut();
  }

  render() {
    return html`
      <div
        id="firebaseui-auth-container"
        style="${this.isSignedIn ? 'display: none' : ''}"
      ></div>
      <div
        class="firebaseui-container firebaseui-page-provider-sign-in firebaseui-id-page-provider-sign-in firebaseui-use-spinner"
        style="${this.isSignedIn ? '' : 'display: none'}"
      >
        <button
          style="background-color:#ffffff"
          class="firebaseui-idp-button mdl-button mdl-js-button mdl-button--raised firebaseui-idp-google firebaseui-id-idp-button"
          @click="${this.signOut}"
        >
          <span class="firebaseui-idp-text firebaseui-idp-text-long"
            >Sign out</span
          >
        </button>
      </div>
    `;
  }
}

customElements.define('firebase-auth-component', FirebaseAuthComponent);

What you need to do:

  • Replace firebaseConfig with your Firebase project's config. Read the Firebase docs to learn how to get yours.
  • Replace the URLs signInSuccessUrl with your Mesop page path and tosUrl and privacyPolicyUrl to your terms and services and privacy policy page respectively.

How it works:

  • This creates a simple and configurable auth UI using FirebaseUI Web.
  • Once the user has signed in, then a sign out button is shown.
  • Whenever the user signs in or out, the web component dispatches an event to the Mesop server with the auth token, or absence of it.
  • See our web component docs for more details.

Python code

firebase_auth_component.py
from typing import Any, Callable

import mesop.labs as mel


@mel.web_component(path="./firebase_auth_component.js")
def firebase_auth_component(on_auth_changed: Callable[[mel.WebEvent], Any]):
  return mel.insert_web_component(
    name="firebase-auth-component",
    events={
      "authChanged": on_auth_changed,
    },
  )

How it works:

  • Implements the Python side of the Mesop web component. See our web component docs for more details.

Integrating into the app

Let's put it all together:

firebase_auth_app.py
import firebase_admin
from firebase_admin import auth

import mesop as me
import mesop.labs as mel
from mesop.examples.web_component.firebase_auth.firebase_auth_component import (
  firebase_auth_component,
)

# Avoid re-initializing firebase app (useful for avoiding warning message because of hot reloads).
if firebase_admin._DEFAULT_APP_NAME not in firebase_admin._apps:
  default_app = firebase_admin.initialize_app()


@me.page(
  path="/web_component/firebase_auth/firebase_auth_app",
  stylesheets=[
    "https://www.gstatic.com/firebasejs/ui/6.1.0/firebase-ui-auth.css"
  ],
  # Loosen the security policy so the firebase JS libraries work.
  security_policy=me.SecurityPolicy(
    dangerously_disable_trusted_types=True,
    allowed_connect_srcs=["*.googleapis.com"],
    allowed_script_srcs=[
      "*.google.com",
      "https://www.gstatic.com",
      "https://cdn.jsdelivr.net",
    ],
  ),
)
def page():
  email = me.state(State).email
  if email:
    me.text("Signed in email: " + email)
  else:
    me.text("Not signed in")
  firebase_auth_component(on_auth_changed=on_auth_changed)


@me.stateclass
class State:
  email: str


def on_auth_changed(e: mel.WebEvent):
  firebaseAuthToken = e.value
  if not firebaseAuthToken:
    me.state(State).email = ""
    return

  decoded_token = auth.verify_id_token(firebaseAuthToken)
  # You can do an allowlist if needed.
  # if decoded_token["email"] != "allowlisted.user@gmail.com":
  #   raise me.MesopUserException("Invalid user: " + decoded_token["email"])
  me.state(State).email = decoded_token["email"]

Note You must add firebase-admin to your Mesop app's requirements.txt file

How it works:

  • The firebase_auth_app.py module integrates the Firebase Auth web component into the Mesop app. It initializes the Firebase app, defines the page where the Firebase Auth web component will be used, and sets up the state to store the user's email.
  • The on_auth_changed function is triggered whenever the user's authentication state changes. If the user is signed in, it verifies the user's ID token and stores the user's email in the state. If the user is not signed in, it clears the email from the state.

Next steps

Congrats! You've now built an authenticated app with Mesop from start to finish. Read the Firebase Auth docs to learn how to configure additional sign-in options and much more.