我目前正在一个NextJS应用程序中尝试设置Firebase v9认证。我最初尝试使用Next的服务器端环境变量作为Firebase配置,但注意到我的所有环境变量都未定义。但后来我更新了我的Firebase配置,使用NEXT_PUBLIC_
,然后它就工作正常了。所以我想我的问题是:
1.向浏览器公开Firebase Config变量是否安全?
1.如果它不安全,你如何确保应用程序在服务器端初始化,然后在客户端使用?(链接任何特定的指南或文章将不胜感激)
下面我按顺序提供了我的firebase配置文件、AuthContext提供程序和登录页面。
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET_URL,
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,
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth();
import { createContext, useContext, useEffect, useState } from 'react';
import {
onAuthStateChanged,
signInWithEmailAndPassword,
signOut,
} from 'firebase/auth';
import { auth } from '@/lib/firebaseApp';
const AuthContext = createContext({});
export const useAuth = () => useContext(AuthContext);
export function AuthContextProvider({ children }) {
const [user, setUser] = useState({ email: null, uid: null });
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (user) {
setUser({
email: user.email,
uid: user.uid,
});
} else {
setUser({ email: null, uid: null });
}
});
setLoading(false);
return () => unsubscribe();
}, []);
function login(email, password) {
return signInWithEmailAndPassword(auth, email, password);
}
async function logout() {
setUser({ email: null, uid: null });
await signOut(auth);
}
return (
<AuthContext.Provider value={{ user, login, logout }}>
{loading ? null : children}
</AuthContext.Provider>
);
}
import { useState } from 'react';
import { useRouter } from 'next/router';
import { Button } from '@/components';
import { useAuth } from '@/context/AuthContext';
export default function Login() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { login } = useAuth();
const router = useRouter();
async function handleSubmit(event) {
event.preventDefault();
try {
await login(email, password);
router.push('/dashboard');
} catch (error) {
console.log(error.message);
}
}
return (
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="mx-auto w-11/12 rounded-2xl border border-zinc-100 p-6 py-8 px-4 dark:border-zinc-700/40 sm:px-10 md:w-full">
<form className="space-y-6" onSubmit={(event) => handleSubmit(event)}>
<div>
<label
htmlFor="email"
className="block text-sm font-semibold text-zinc-900 dark:text-zinc-100"
>
Email address
</label>
<div className="mt-1">
<input
id="email"
name="email"
type="email"
autoComplete="email"
required
className="block w-full appearance-none rounded-md border border-zinc-900/10 bg-white px-3 py-2 shadow-sm shadow-zinc-800/5 placeholder:text-zinc-400 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
onChange={(e) => setEmail(e.target.value)}
/>
</div>
</div>
<div>
<label
htmlFor="password"
className="block text-sm font-semibold text-zinc-900 dark:text-zinc-100"
>
Password
</label>
<div className="mt-1">
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
className="block w-full appearance-none rounded-md border border-zinc-900/10 bg-white px-3 py-2 shadow-sm shadow-zinc-800/5 placeholder:text-zinc-400 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
onChange={(e) => setPassword(e.target.value)}
/>
</div>
</div>
<div className="flex w-full justify-center">
<Button type="submit" className="w-10/12 px-6 py-4 md:w-5/6">
Sign In
</Button>
</div>
</form>
</div>
</div>
</div>
);
}
1条答案
按热度按时间14ifxucb1#
为什么无法访问. env变量
您当前正在访问客户端的环境变量。此功能不需要,因此默认情况下不存在,因为客户端上的所有内容对每个人都可见。您可以直接以纯文本形式粘贴API密钥。这是一回事。
Firebase管理员与客户端
firebase有两种选择。要么你使用客户端SDK,那里的API密钥是公开的,并且可以公开显示,没有任何问题。你在firestore控制台中编写自己的规则。(这似乎是你正在使用的)
否则你可以使用firebase管理SDK,它应该只在服务器端运行。然后你通过Nextjs API路由与firebase通信。初始化这个应用的API密钥是绝对私有的,并且显示为不公开。如果公开,任何人都可以获得你项目的完全控制权
您可以在这里阅读更多关于firebase admin SDK的信息:https://firebase.google.com/docs/admin/setup
结论
您在客户端使用的是firebase sdk,因此您的API密钥可以安全地暴露。但是不要忘记设置谁可以读/写的规则。如果您的项目在发布时处于"测试模式",任何人都可以获得对您的应用的控制权