Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion components/welcome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function Welcome() {
<div className="flex flex-col items-center justify-center p-24">
<div className="text-center">
<Logomark />
<h1 className="text-3xl font-bold mt-4">Welcome to {appName}</h1>
<h1 className="text-2xl font-bold mt-4">Welcome to {appName}</h1>
<p className="mt-2 text-muted-foreground">
Get started by building out your dashboard.
</p>
Expand Down
31 changes: 30 additions & 1 deletion lib/const.ts
Original file line number Diff line number Diff line change
@@ -1 +1,30 @@
export const appName = "AuthFire Admin"
export const appName = process.env.NEXT_PUBLIC_APP_NAME || "AuthFire Admin";
export const baseUrl = `https://${process.env.NEXT_PUBLIC_HOSTNAME}`;
export const emailLinkLoginUrl = `${baseUrl}/email-link-login`;
export const logoImageUrl = process.env.NEXT_PUBLIC_LOGO_IMAGE_URL || `${baseUrl}/images/logo.png`;
export const logoDarkImageUrl = process.env.NEXT_PUBLIC_LOGO_DARK_IMAGE_URL || `${baseUrl}/images/logo-dark.png`;
export const privacyPolicyUrl = process.env.NEXT_PUBLIC_PRIVACY_POLICY_URL || `${baseUrl}/privacy-policy`;
export const termsOfServiceUrl = process.env.NEXT_PUBLIC_TERMS_OF_SERVICE_URL || `${baseUrl}/terms-of-service`;
export const idTokenVerificationUrl = process.env.NEXT_PUBLIC_ID_TOKEN_VERIFICATION_URL || "";
export const serverSignOutUrl = process.env.NEXT_PUBLIC_SERVER_SIGN_OUT_URL || "";
export const serverTokenUrl = process.env.NEXT_PUBLIC_SERVER_TOKEN_URL || "";
export const primaryColor = process.env.NEXT_THEME_PRIMARY_COLOR || "#171717";
export const primaryForegroundColor = process.env.NEXT_THEME_PRIMARY_FOREGROUND_COLOR || "#fafafa";
export const legalBusinessName = process.env.NEXT_PUBLIC_LEGAL_BUSINESS_NAME || appName;
export const recaptchaSiteKey = process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY;

export const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
}

export const openIdConfig = {
providerId: process.env.NEXT_PUBLIC_OPENID_CONNECT_PROVIDER_ID,
name: process.env.NEXT_PUBLIC_OPENID_CONNECT_NAME,
logoUrl: process.env.NEXT_PUBLIC_OPENID_CONNECT_LOGO_URL,
}
106 changes: 106 additions & 0 deletions lib/firebase/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { getApp, initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import { getStorage } from "firebase/storage";
import { getToken, initializeAppCheck, ReCaptchaV3Provider } from "firebase/app-check";
import { getAnalytics, logEvent as _logEvent } from "firebase/analytics";
import { useStore } from '@nanostores/react';
import { $analytics, $app, $appCheck, $auth, $firestore, $loading, $storage, setAnalytics, setApp, setAppCheck, setAuth, setFirestore, setLoading, setStorage } from "./store";
import { baseUrl, firebaseConfig, idTokenVerificationUrl, recaptchaSiteKey, serverSignOutUrl, serverTokenUrl } from "../const";
import { initialize } from "@authfire/core"
import { useEffect } from "react";

const useFirebase = () => {
let isLoading = useStore($loading);
let app = useStore($app);
let appCheck = useStore($appCheck);
let auth = useStore($auth);
let firestore = useStore($firestore);
let storage = useStore($storage);
let analytics = useStore($analytics);

useEffect(() => {
if (!app) {
try {
app = getApp() || initializeApp(firebaseConfig)
} catch {
app = initializeApp(firebaseConfig)
}
setApp(app);
}

if (!appCheck && recaptchaSiteKey) {
appCheck = initializeAppCheck(app, {
provider: new ReCaptchaV3Provider(recaptchaSiteKey),
isTokenAutoRefreshEnabled: true
});
setAppCheck(appCheck);
}

if (!auth) {
auth = getAuth(app);
setAuth(auth);
}

if (!firestore) {
firestore = getFirestore(app);
setFirestore(firestore);
}

if (!storage) {
storage = getStorage(app);
setStorage(storage);
}

if (!analytics) {
analytics = getAnalytics(app);
setAnalytics(analytics);
}

isLoading = false;
setLoading(isLoading);
}, [app])

return {
isLoading,
app,
appCheck,
auth,
firestore,
storage,
analytics
}
}

const getAppCheckToken = async (forceRefresh: boolean = false) => {
const appCheck = $appCheck.get();
if (typeof window === 'undefined') {
throw new Error("App Check is not available on the server side.");
} else if (!appCheck) {
throw new Error("App Check is not initialized. Ensure recaptchaSiteKey is set.");
}
const result = await getToken(appCheck, forceRefresh);
return result.token;
}

/* eslint-disable @typescript-eslint/no-explicit-any */
const logEvent = (eventName: string, eventParams?: Record<string, any>) => {
const analytics = $analytics.get();
if (!analytics) {
console.warn("Analytics is not available in this environment.");
return;
}
_logEvent(analytics, eventName, eventParams);
}

initialize({
baseUrl,
idTokenVerificationUrl,
serverTokenUrl,
serverSignOutUrl,
useFirebase,
getAppCheckToken,
logEvent
})

export { useFirebase, getAppCheckToken, logEvent };
52 changes: 52 additions & 0 deletions lib/firebase/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Analytics } from 'firebase/analytics';
import { FirebaseApp } from 'firebase/app'
import { AppCheck } from 'firebase/app-check';
import { Auth } from 'firebase/auth';
import { Firestore } from 'firebase/firestore';
import { FirebaseStorage } from 'firebase/storage';
import { atom } from 'nanostores'

const $loading = atom<boolean>(true)
const setLoading = (loading: boolean) => {
$loading.set(loading);
}

const $app = atom<FirebaseApp | undefined>()

const setApp = (app: FirebaseApp) => {
$app.set(app);
}

const $appCheck = atom<AppCheck | undefined>(undefined)

const setAppCheck = (appCheck: AppCheck) => {
$appCheck.set(appCheck);
}

const $auth = atom<Auth | undefined>(undefined)

const setAuth = (auth: Auth) => {
$auth.set(auth);
}

const $firestore = atom<Firestore | undefined>(undefined)

const setFirestore = (firestore: Firestore) => {
$firestore.set(firestore);
}

const $storage = atom<FirebaseStorage | undefined>(undefined)

const setStorage = (storage: FirebaseStorage) => {
$storage.set(storage);
}

const $analytics = atom<Analytics | undefined>(undefined)

const setAnalytics = (analytics: Analytics) => {
$analytics.set(analytics);
}

export {
$app, setApp, $appCheck, setAppCheck, $auth, setAuth, $firestore, setFirestore, $storage, setStorage, $analytics, setAnalytics, $loading, setLoading
}
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"@authfire/core": "^1.0.3",
"@nanostores/react": "^1.0.0",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-dialog": "^1.1.15",
Expand All @@ -20,7 +22,9 @@
"@tailwindcss/postcss": "^4.1.14",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"firebase": "^12.4.0",
"lucide-react": "^0.545.0",
"nanostores": "^1.0.1",
"next": "^15.5.4",
"next-themes": "^0.4.6",
"postcss": "^8.5.6",
Expand Down
Loading