This app uses Option 1: Magic Link from firebase_authentication.md. Only whitelisted admin emails can request a login link; clicking the link signs them in with Firebase Auth. All /api/* routes require a valid Firebase ID token when FIREBASE_APP_ID is set.
-
Create or use a Firebase project and note the Project ID and Web app (if you don’t have one, add a Web app in Project settings).
-
Enable Email/Password sign-in method
- Authentication → Sign-in method → Email/Password → Enable, then Save.
- Under “Email link (passwordless sign-in)”, enable Email link so magic links work.
-
Authorized domains
- Authentication → Settings → Authorized domains.
- Add your frontend origin (e.g.
url.taipei,url-taipei.web.app,localhostfor dev).
-
Get Web app config
- Project settings → Your apps → Web app → copy:
apiKey,authDomain,projectId,appId.
- Use these in the frontend
.env(see below). The appId is also used in the backend asFIREBASE_APP_IDfor token verification.
- Project settings → Your apps → Web app → copy:
In frontend/.env (and frontend/.env.production for production build):
VITE_API_BASE_URL=https://your-backend-url
VITE_FIREBASE_API_KEY=your-api-key
VITE_FIREBASE_AUTH_DOMAIN=your-project-id.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=your-project-id
VITE_FIREBASE_APP_ID=1:123456789:web:abcdef- Local dev (no auth): Leave
FIREBASE_APP_IDunset. API calls are not checked. - Production (auth required): Set the same Web app App ID as in the frontend:
FIREBASE_APP_ID=1:123456789:web:abcdef
# Optional, for reference:
# FIREBASE_PROJECT_ID=your-project-idThe callable function sendAdminLoginLink:
- Checks the given email against an admin whitelist.
- Generates a Firebase Auth sign-in link.
- Sends the link by email via SMTP.
Set a comma-separated list of allowed admin emails:
firebase functions:config:set admin.whitelist="[email protected],[email protected]"Or with Firebase Functions env (Node 18+):
# In functions/.env (do not commit)
[email protected],[email protected]firebase functions:config:set app.url="https://url.taipei"Or in functions/.env: APP_URL=https://url.taipei
Configure your SMTP provider (e.g. Gmail with an app password):
firebase functions:config:set smtp.user="[email protected]" smtp.pass="your-app-password" smtp.from="[email protected]"Optional: smtp.host (default smtp.gmail.com), smtp.port (default 587).
Or in functions/.env:
SMTP_USER=[email protected]
SMTP_PASS=your-app-password
SMTP_FROM=[email protected]Admin emails can be managed from the app’s Admins page (nav: Admins). The list is stored in Firestore (collection admin_emails).
- Firestore: In Firebase Console → Build → Firestore Database, create a database if you don’t have one (e.g. start in production mode; you can restrict rules later).
- Initial list: If the collection is empty, the function still uses the env whitelist (
ADMIN_WHITELIST). The first time someone requests a magic link (and is allowed), the full env list is copied into Firestore; after that, only Firestore is used. - Add/remove: Signed-in admins can add or remove emails on the Admins page. You cannot remove yourself or the last admin.
-
Install and deploy Cloud Functions
cd functions npm install cd .. firebase deploy --only functions
-
Backend: Set
FIREBASE_APP_IDin production env and redeploy (e.g. Cloud Run). -
Frontend: Set the Firebase env vars, build, then:
firebase deploy --only hosting
- User opens the app → if not signed in, they are sent to /login.
- On /login, they enter their email and click “Send login link”.
- Frontend calls the callable sendAdminLoginLink; the function checks the whitelist, generates the link, and sends the email.
- User clicks the link in the email → they land on your app → Firebase completes sign-in (
signInWithEmailLink). - After sign-in, the frontend sends the Firebase ID token in
Authorization: Bearer <token>on every API request. - Backend (when
FIREBASE_APP_IDis set) verifies the token withgoogle.oauth2.id_token.verify_firebase_tokenand rejects unauthenticated requests with 401.