Skip to main content

Auth integration recipes

This page collects copy-pasteable recipes for each widget auth mode against the most common backend and identity-provider stacks. The conceptual reference is in authentication overview; the per-mode details are under each auth-mode page.

TL;DR

  • backendToken — your backend exposes a token endpoint; recipes per backend framework below.
  • firebase — your frontend reads the Firebase ID token; minimal backend involvement.
  • PubliclySignedJwt — your frontend reads the IDP’s session token; configured server-side in the dashboard.
  • none — local dev only; no auth.

Backend Token recipes

Node — Express

import express from "express";
import { NotifizzClient } from "@notifizz/nodejs";
import { requireAuth } from "./auth-middleware.js";

const notifizz = new NotifizzClient(
  process.env.NOTIFIZZ_AUTH_SECRET_KEY,
  process.env.NOTIFIZZ_SDK_SECRET_KEY,
);

const app = express();

app.get("/api/notifizz-session", requireAuth, (req, res) => {
  const token = notifizz.generateHashedToken(req.user.id);
  res.json({
    token,
    userId: req.user.id,
    userEmail: req.user.email,
  });
});

Node — Fastify

import Fastify from "fastify";
import { NotifizzClient } from "@notifizz/nodejs";

const fastify = Fastify();
const notifizz = new NotifizzClient(/* ... */);

fastify.get("/api/notifizz-session", { preHandler: requireAuth }, async (req) => {
  return {
    token: notifizz.generateHashedToken(req.user.id),
    userId: req.user.id,
    userEmail: req.user.email,
  };
});

Node — NestJS

import { Controller, Get, Req } from "@nestjs/common";
import { NotifizzClient } from "@notifizz/nodejs";

@Controller("api")
export class NotifizzController {
  private notifizz = new NotifizzClient(/* ... */);

  @Get("notifizz-session")
  getSession(@Req() req) {
    return {
      token: this.notifizz.generateHashedToken(req.user.id),
      userId: req.user.id,
      userEmail: req.user.email,
    };
  }
}

PHP — Laravel

use Notifizz\NotifizzClient;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Auth;

Route::middleware('auth')->get('/api/notifizz-session', function () {
    $client = new NotifizzClient(
        config('services.notifizz.auth_secret'),
        config('services.notifizz.sdk_secret'),
    );
    $user = Auth::user();
    return [
        'token' => $client->generateHashedToken((string) $user->id),
        'userId' => (string) $user->id,
        'userEmail' => $user->email,
    ];
});

Python — Django

import hashlib
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
import os

@login_required
def notifizz_session(request):
    user_id = str(request.user.id)
    auth_secret = os.environ["NOTIFIZZ_AUTH_SECRET_KEY"]
    token = hashlib.sha256((user_id + auth_secret).encode()).hexdigest()
    return JsonResponse({
        "token": token,
        "userId": user_id,
        "userEmail": request.user.email,
    })
There’s no official Python SDK yet — the helper is two lines: hashlib.sha256((user_id + auth_secret).encode()).hexdigest(). The hash format is the same as client.generateHashedToken() in the Node/Java/PHP SDKs.

Ruby — Rails

require "digest"

class NotifizzController < ApplicationController
  before_action :authenticate_user!

  def session
    user_id = current_user.id.to_s
    token = Digest::SHA256.hexdigest(user_id + ENV["NOTIFIZZ_AUTH_SECRET_KEY"])
    render json: {
      token: token,
      userId: user_id,
      userEmail: current_user.email,
    }
  end
end

Java — Spring

import com.notifizz.NotifizzClient;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class NotifizzController {
    private final NotifizzClient notifizz = new NotifizzClient(
        System.getenv("NOTIFIZZ_AUTH_SECRET_KEY"),
        System.getenv("NOTIFIZZ_SDK_SECRET_KEY"));

    @GetMapping("/notifizz-session")
    public Map<String, String> getSession(Principal principal) {
        return Map.of(
            "token", notifizz.generateHashedToken(principal.getName()),
            "userId", principal.getName(),
            "userEmail", lookupEmail(principal.getName())
        );
    }
}

Frontend consumption

Pair any of the backend recipes above with this frontend snippet:
// React example — works the same with Angular / Vanilla, just adapt the lifecycle
import { useEffect, useState } from "react";
import NotifizzInbox from "@notifizz/react";

function App() {
  const [session, setSession] = useState(null);

  useEffect(() => {
    fetch("/api/notifizz-session", { credentials: "include" })
      .then(r => r.json())
      .then(setSession);
  }, []);

  if (!session) return null;

  return (
    <NotifizzInbox
      options={{
        apiKey: process.env.REACT_APP_NOTIFIZZ_FRONT_API_KEY,
        authType: "backendToken",
        token: session.token,
        userId: session.userId,
        userEmail: session.userEmail,
      }}
    />
  );
}

Firebase recipes

Web client

import { getAuth, onAuthStateChanged } from "firebase/auth";

onAuthStateChanged(getAuth(), async (user) => {
  if (!user) return;
  const token = await user.getIdToken();
  mountNotifizz({ apiKey, authType: "firebase", token });
});

Re-mount on token refresh

import { onIdTokenChanged } from "firebase/auth";

onIdTokenChanged(getAuth(), async (user) => {
  if (!user) return;
  const fresh = await user.getIdToken();
  setNotifizzToken(fresh);   // your component re-renders with the new token
});

Publicly-Signed JWT recipes

PubliclySignedJwt is configured server-side via the dashboard. The frontend just reads the IDP session token. Common providers:

Auth0

import { useAuth0 } from "@auth0/auth0-react";

const { getAccessTokenSilently, user } = useAuth0();
const token = await getAccessTokenSilently();
mountNotifizz({ apiKey, token, userId: user.sub, userEmail: user.email });

Clerk

import { useAuth, useUser } from "@clerk/clerk-react";

const { getToken } = useAuth();
const { user } = useUser();
const token = await getToken();
mountNotifizz({
  apiKey,
  token,
  userId: user.id,
  userEmail: user.primaryEmailAddress.emailAddress,
});

Cognito

import { fetchAuthSession, getCurrentUser } from "aws-amplify/auth";

const session = await fetchAuthSession();
const user = await getCurrentUser();
const token = session.tokens.idToken.toString();
mountNotifizz({
  apiKey,
  token,
  userId: user.userId,
  userEmail: user.signInDetails.loginId,
});

Supabase Auth

import { createClient } from "@supabase/supabase-js";

const supabase = createClient(URL, KEY);
const { data: { session } } = await supabase.auth.getSession();
const { user } = session;
mountNotifizz({
  apiKey,
  token: session.access_token,
  userId: user.id,
  userEmail: user.email,
});
The dashboard notificationCenterSetup dev-task captures the JWKS URL + claim paths once per environment — the same frontend code works regardless of provider once the dashboard is configured. See publicly-signed JWT for the dashboard side.

FAQ

By default, Auth0 puts the user id in sub and the email in email (when the email scope is requested). Set paths.id = "sub" and paths.email = "email". With Auth0’s namespaced custom-claims pattern, use the namespaced paths instead.
Clerk puts the user id in sub. Email needs to be added via a Clerk session token template — Clerk doesn’t include it by default. Either add email to the template, or configure paths.email to the metadata path you use.
Supabase Auth supports anonymous users with a stable user.id and a valid JWT. PubliclySignedJwt works for them; bridge to a real identity later if your campaigns expect it.
Only if staging is behind your VPN / SSO and not customer-facing. The threat model with 'none' is “anyone with the API key plus a userId can read that user’s inbox” — fine inside a controlled environment, never on the open internet.
Cross-environment IDP config. Your dev environment is probably wired to dev IDP credentials and a dev JWKS URL; prod needs the prod equivalent. Compare the dashboard auth config side-by-side; most “works on dev only” cases trace to one stale field.
state.isReady === true means auth succeeded. If unreadCount is 0, the auth is fine but no message reached this userId. Check that your campaign’s orchestrator emits this exact userId as a recipient identifier — most “empty bell” cases are auth working + recipient mismatch.

See also

Authentication overview

The four-mode model and when to pick each.

Backend tokens

client.generateHashedToken end-to-end.

Firebase auth

Pass a Firebase ID token to the widget.

Publicly-signed JWT

Auth0 / Clerk / Cognito / Supabase setup.