Skip to content

Latest commit

 

History

History
222 lines (171 loc) · 7.46 KB

3-firebase.mdx

File metadata and controls

222 lines (171 loc) · 7.46 KB
sidebar_position sidebar_label description keywords toc_max_heading_level seoFrontMatterUpdated
4
Firebase
Learn how to connect Firebase to your Hasura DDN supergraph.
hasura
hasura ddn
authentication
jwt
firebase
tutorial
guide
4
false

Firebase

Introduction

In this tutorial, you'll learn how to configure an existing Firebase project and generate a JWT which you can pass in the header of your requests to Hasura. After setting up your AuthConfig object to use JWT mode, this will allow you to validate users' identities and create permission rules which can limit access to underlying data served by Hasura DDN.

:::info Prerequisites

Before continuing, ensure you have:

  • A Firebase project created, with at least one authentication method enabled.
  • A local application that can integrate with Firebase for authentication. We'll provide an example Node.js application below.
  • A private key from the project's Service Accounts section via the Firebase console.
  • A local Hasura DDN project.

:::

Tutorial

Step 1. Add Firebase to your local application

Step 1.1 Add the firebase-admin package

Firebase works with a number of languages and frameworks. In the example(s) we show below, we'll use a Node.js application.

npm i firebase-admin

Step 1.2 Initialize firebase-admin

Then, initialize the module in your application:

const admin = require("firebase-admin");

// service_account.json points to the private key from the prerequisites
admin.initializeApp({ credential: admin.credential.cert(require("./service_account.json")) });

Step 1.3 Add the custom claims

// Here, you'll need to fetch the user's role from Hasura DDN using an admin-level authenticated request
// Learn more here: https://hasura.io/docs/3.0/auth/authentication/jwt/special-roles
// Below, we're hard-coding the value for now
const user_role = "user"; // the role returned from your request ☝️
const customClaims = {
  "claims.jwt.hasura.io": {
    "x-hasura-default-role": user_role,
    "x-hasura-allowed-roles": ["user"],
    "x-hasura-user-id": decodedToken.uid,
  },
};

// Set custom claims for the user based on their uid
await admin.auth().setCustomUserClaims(decodedToken.uid, customClaims);

This will add the required Hasura namespace with the keys that Hasura DDN expects when decoding a JWT. You can modify the keys to suit your Hasura DDN roles.

:::tip Custom claims

You can create any custom keys you wish and reference them in your permissions using session variables. Above, x-hasura-user-id is simply an example. Any claim prefixed with x-hasura- is accessible to the Hasura DDN Engine. :::

Step 2. Update your AuthConfig

Update your AuthConfig object to use JWT mode and your Firebase JWKs and audience:

kind: AuthConfig
version: v3
definition:
  mode:
    jwt:
      audience: ["your-firebase-project-name"]
      claimsConfig:
        namespace:
          claimsFormat: Json
          location: "/claims.jwt.hasura.io"
      key:
        jwkFromUrl: "https://www.googleapis.com/service_accounts/v1/jwk/[email protected]"
      tokenLocation:
        type: Header
        name: Auth-Token

:::info Firebase JWKs

Firebase uses the same set of JWKs for all applications. It distinguishes between different apps by specifying the audience (aud) claim in the JWT. Make sure to set the audience field to match your Firebase project name in the AuthConfig object to ensure proper validation.

:::

Then, create a new build of your supergraph:

ddn supergraph build local

Step 3. Test your configuration

Generate a new JWT by logging into your application. These values aren't typically displayed to users, so you'll need to log them while in development. You can then add that value as a header in the console and test any permissions you have in your metadata.

Here's the complete sample Node.js server.
const express = require("express");
const admin = require("firebase-admin");
const bodyParser = require("body-parser");
const axios = require("axios");

// Initialize Firebase Admin SDK
admin.initializeApp({
  credential: admin.credential.cert(require("./service_account.json")),
});

const app = express();
app.use(bodyParser.json());

// Firebase API key from your Firebase project settings
const FIREBASE_API_KEY = "your API key found on the Firebase project's console";

// Route to handle user login with email and password
app.post("/login", async (req, res) => {
  const { email, password } = req.body;

  if (!email || !password) {
    return res.status(400).json({ message: "Email and password are required" });
  }

  try {
    // Call Firebase REST API to sign in the user with email and password
    const response = await axios.post(
      `https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${FIREBASE_API_KEY}`,
      {
        email,
        password,
        returnSecureToken: true,
      }
    );

    const { idToken } = response.data;

    // Verify the token using Firebase Admin SDK
    const decodedToken = await admin.auth().verifyIdToken(idToken);

    // Here, you'll need to fetch the user's role from Hasura DDN using an admin-level authenticated request
    // Learn more here: https://hasura.io/docs/3.0/auth/authentication/jwt/special-roles
    // Below, we're hard-coding the value for now
    const user_role = "user"; // the role returned from your request ☝️
    const customClaims = {
      "claims.jwt.hasura.io": {
        "x-hasura-default-role": user_role,
        "x-hasura-allowed-roles": ["user"],
        "x-hasura-user-id": decodedToken.uid,
      },
    };

    // Set custom claims for the user based on their uid
    await admin.auth().setCustomUserClaims(decodedToken.uid, customClaims);

    // Send the updated JWT back in the response
    res.status(200).json({
      idToken,
    });
  } catch (error) {
    console.error("Error logging in:", error.response?.data || error.message);
    res.status(401).json({ message: "Invalid credentials", error: error.response?.data || error.message });
  }
});

app.listen(4000, () => {
  console.log("Server running on port 4000");
});

Wrapping up

In this guide, you learned how to integrate Firebase with Hasura DDN to create a secure and scalable identity management solution using JWTs. By leveraging custom claims in conjunction with permissions, you can define precise access-control rules, ensuring that your application remains secure and meets your users' needs.

As you continue building out your supergraph, keep in mind that authentication and authorization are crucial components. Always validate your configuration and regularly test your setup to ensure it functions as expected across different roles and environments.

If you encounter issues or need further customization, consider reviewing our related documentation or exploring additional Firebase features that can enhance your authentication flows.