所有Next的简单firebase身份验证。JS渲染策略。
此软件包使得在客户端和服务器端渲染(SSR)期间获得身份验证的Firebase用户和ID令牌变得简单。
我们将firebase JS SDK视为验证状态的真理来源。当用户登录时,我们调用一个端点来生成刷新令牌,并存储用户信息,id代币和刷新令牌。对SSR页面的未来请求从Cookie接收用户信息和ID令牌,根据需要刷新ID令牌。当用户注销时,我们会删除cookie。
请参阅示例应用程序的实时演示。
根据您的应用程序的需求,其他方法可能对您更有效。
如果您的应用仅使用静态页面或不需要SSR的Firebase用户,请直接使用Firebase JS SDK将用户加载到客户端。
getServerSideProps时,您将无法访问Firebase用户。如果您的应用需要SSR的firebase用户(但不需要ID令牌服务器端) ,则可以考虑以下一种方法:
如果您的应用需要通用的身份验证解决方案(不是尤其是Firebase身份验证),则可以考虑使用NextAuth.js。 NextAuth.js不使用Firebase身份验证,而是支持包括Google在内的各种身份提供者。在此处阅读有关next-firebase-auth和NextAuth.js之间的差异,以了解最适合您的需求。
如果您的应用程序使用Next.js的应用程序路由器,则此软件包尚未支持它。您可以在#568中遵循进度。
如果您期望同时使用静态页面和SSR,或者需要访问Firebase ID令牌服务器端,则此软件包可能会有所帮助。
快速注意此软件包不做什么:
- 它不提供身份验证UI。考虑Firebaseui-web或建造自己的。
- 它不会扩展Firebase功能,而不是提供对验证用户的通用访问权限。使用Firebase Admin SDK和Firebase JS SDK来满足其他需求。
安装:
yarn add next-firebase-auth或npm i next-firebase-auth
确保还安装了同伴依赖性:
yarn add firebase firebase-admin next react react-dom
创建一个模块以初始化next-firebase-auth 。
有关详细信息,请参见配置文档
// ./initAuth.js
import { initializeApp } from 'firebase/app'
import { init } from 'next-firebase-auth'
const initAuth = ( ) => {
const firebaseClientInitConfig = {
apiKey : 'MyExampleAppAPIKey123' , // required
authDomain : 'my-example-app.firebaseapp.com' ,
databaseURL : 'https://my-example-app.firebaseio.com' ,
projectId : 'my-example-app-id' ,
}
initializeApp ( firebaseClientInitConfig )
init ( {
authPageURL : '/auth' ,
appPageURL : '/' ,
loginAPIEndpoint : '/api/login' ,
logoutAPIEndpoint : '/api/logout' ,
onLoginRequestError : ( err ) => {
console . error ( err )
} ,
onLogoutRequestError : ( err ) => {
console . error ( err )
} ,
firebaseAuthEmulatorHost : 'localhost:9099' ,
firebaseAdminInitConfig : {
credential : {
projectId : 'my-example-app-id' ,
clientEmail : '[email protected]' ,
// The private key must not be accessible on the client side.
privateKey : process . env . FIREBASE_PRIVATE_KEY ,
} ,
databaseURL : 'https://my-example-app.firebaseio.com' ,
} ,
// Use application default credentials (takes precedence over firebaseAdminInitConfig if set)
// useFirebaseAdminDefaultCredential: true,
firebaseClientInitConfig ,
// tenantId: 'example-tenant-id', // Optional, only necessary in multi-tenant configuration
cookies : {
name : 'ExampleApp' , // required
// Keys are required unless you set `signed` to `false`.
// The keys cannot be accessible on the client side.
keys : [
process . env . COOKIE_SECRET_CURRENT ,
process . env . COOKIE_SECRET_PREVIOUS ,
] ,
httpOnly : true ,
maxAge : 12 * 60 * 60 * 24 * 1000 , // twelve days
overwrite : true ,
path : '/' ,
sameSite : 'strict' ,
secure : true , // set this to false in local (non-HTTPS) development
signed : true ,
} ,
onVerifyTokenError : ( err ) => {
console . error ( err )
} ,
onTokenRefreshError : ( err ) => {
console . error ( err )
} ,
} )
}
export default initAuth设置私有环境变量FIREBASE_PRIVATE_KEY , COOKIE_SECRET_CURRENT和COOKIE_SECRET_PREVIOUS in .env.local 。如果启用了Firebase身份验证模拟器,则还需要设置FIREBASE_AUTH_EMULATOR_HOST环境变量。
在_app.js中初始化next-firebase-auth :
// ./pages/_app.js
import initAuth from '../initAuth' // the module you created above
initAuth ( )
function MyApp ( { Component , pageProps } ) {
return < Component { ... pageProps } />
}
export default MyApp创建设置auth cookie的登录和注销API端点:
// ./pages/api/login
import { setAuthCookies } from 'next-firebase-auth'
import initAuth from '../../initAuth' // the module you created above
initAuth ( )
const handler = async ( req , res ) => {
try {
await setAuthCookies ( req , res )
} catch ( e ) {
return res . status ( 500 ) . json ( { error : 'Unexpected error.' } )
}
return res . status ( 200 ) . json ( { success : true } )
}
export default handler // ./pages/api/logout
import { unsetAuthCookies } from 'next-firebase-auth'
import initAuth from '../../initAuth' // the module you created above
initAuth ( )
const handler = async ( req , res ) => {
try {
await unsetAuthCookies ( req , res )
} catch ( e ) {
return res . status ( 500 ) . json ( { error : 'Unexpected error.' } )
}
return res . status ( 200 ) . json ( { success : true } )
}
export default handler最后,在页面中使用已验证的用户:
// ./pages/demo
import React from 'react'
import {
useUser ,
withUser ,
withUserTokenSSR ,
} from 'next-firebase-auth'
const Demo = ( ) => {
const user = useUser ( )
return (
< div >
< p > Your email is { user . email ? user . email : 'unknown' } . </ p >
</ div >
)
}
// Note that this is a higher-order function.
export const getServerSideProps = withUserTokenSSR ( ) ( )
export default withUser ( ) ( Demo ) init(config)初始化next-firebase-auth施以使用配置对象。
withUser({ ...options })(PageComponent)一个高阶功能,可为组件提供User上下文。将其与Next.js页面一起使用,该页面将通过useUser钩访问验证的用户。可选地,它可以根据用户的auth状态来客户端重定向。
它接受以下选项:
| 选项 | 描述 | 默认 |
|---|---|---|
whenAuthed | 如果用户经过身份验证,则采取的措施。 AuthAction.RENDER或AuthAction.REDIRECT_TO_APP之一。 | AuthAction.RENDER |
whenAuthedBeforeRedirect | 等待浏览器重定向时要采取的措施。当用户进行身份验证并设置为Authaction.redirect_to_app时相关。之一: AuthAction.RENDER或AuthAction.SHOW_LOADER或AuthAction.RETURN_NULL 。 | AuthAction.RETURN_NULL |
whenUnauthedBeforeInit | 如果用户未经身份验证,则需要采取的措施,但firebase客户端JS SDK尚未初始化。之一: AuthAction.RENDER , AuthAction.REDIRECT_TO_LOGIN , AuthAction.SHOW_LOADER 。 | AuthAction.RENDER |
whenUnauthedAfterInit | 如果未对用户进行身份验证,并且Firebase客户端JS SDK已经初始化了该操作。之一: AuthAction.RENDER , AuthAction.REDIRECT_TO_LOGIN 。 | AuthAction.RENDER |
appPageURL | 当我们应该重定向到应用程序时,重定向目标URL。 pageurl。 | config.appPageURL |
authPageURL | 重定向目标URL时,我们应该重定向到登录页面。 pageurl。 | config.authPageURL |
LoaderComponent | 当用户未成真whenUnauthedBeforeInit设置为AuthAction.SHOW_LOADER时,要渲染的组件。 | 无效的 |
例如,如果用户未经认证,则此页面将重定向到登录页面:
import { withUser , AuthAction } from 'next-firebase-auth'
const DemoPage = ( ) => < div > My demo page </ div >
export default withUser ( {
whenUnauthedAfterInit : AuthAction . REDIRECT_TO_LOGIN ,
authPageURL : '/my-login-page/' ,
} ) ( DemoPage )这是一个登录页面的示例,该页面显示了加载程序,直到初始化firebase,然后将用户登录到应用程序,然后重定向到应用程序:
import { withUser , AuthAction } from 'next-firebase-auth'
const MyLoader = ( ) => < div > Loading... </ div >
const LoginPage = ( ) => < div > My login page </ div >
export default withUser ( {
whenAuthed : AuthAction . REDIRECT_TO_APP ,
whenUnauthedBeforeInit : AuthAction . SHOW_LOADER ,
whenUnauthedAfterInit : AuthAction . RENDER ,
LoaderComponent : MyLoader ,
} ) ( LoginPage )对于打字稿使用,请在此处查看。
withUserTokenSSR({ ...options })(getServerSidePropsFunc = ({ user }) => {})包装Next.js页面的getServerSideProps函数的高阶功能在服务器端渲染过程中提供User上下文。可选地,它可以基于用户的auth状态的服务器端重定向。包装功能是可选的;如果提供,将使用包含user属性的context对象调用它。
它接受以下选项:
| 选项 | 描述 | 默认 |
|---|---|---|
whenAuthed | 如果用户经过身份验证,则采取的措施。 AuthAction.RENDER或AuthAction.REDIRECT_TO_APP 。 | AuthAction.RENDER |
whenUnauthed | 如果用户未经认证,则采取的措施。 AuthAction.RENDER或AuthAction.REDIRECT_TO_LOGIN 。 | AuthAction.RENDER |
appPageURL | 当我们应该重定向到应用程序时,重定向目标URL。 pageurl。 | config.appPageURL |
authPageURL | 重定向目标URL时,我们应该重定向到登录页面。 pageurl。 | config.authPageURL |
例如,此页面将为身份验证的用户提供SSR,使用其firebase ID令牌获取道具,如果未对用户进行身份验证,则将服务器端重定向到登录页面:
import {
useUser ,
withUser ,
withUserTokenSSR ,
} from 'next-firebase-auth'
const DemoPage = ( { thing } ) => < div > The thing is: { thing } </ div >
export const getServerSideProps = withUserTokenSSR ( {
whenUnauthed : AuthAction . REDIRECT_TO_LOGIN ,
} ) ( async ( { user } ) => {
// Optionally, get other props.
const token = await user . getIdToken ( )
const response = await fetch ( '/api/my-endpoint' , {
method : 'GET' ,
headers : {
Authorization : token ,
} ,
} )
const data = await response . json ( )
return {
props : {
thing : data . thing ,
} ,
}
} )
export default withUser ( ) ( DemoPage ) withUserSSR({ ...options })(getServerSidePropsFunc = ({ user }) => {})与withUserTokenSSR行为几乎相同,具有一个关键区别: user将不包含ID令牌。
此方法依赖于cookie授权的用户数据,而不是验证或刷新Firebase ID令牌。最后:
user将在您调用user.getIdToken()时将解析为null。withUserTokenSSR要快。withUserTokenSSR 。cookies.signed设置为false时,请勿使用它。这样做是一种潜在的安全风险,因为验证的用户cookie值可以由客户端修改。
这采用与withUserTokenSSR相同的选项。
useUser()返回当前user钩子。要使用此功能,必须将withUser包裹。如果未对用户进行身份验证,则useUser将返回具有NULL id的User实例。
例如:
import { useUser , withUser } from 'next-firebase-auth'
const Demo = ( ) => {
const user = useUser ( )
return (
< div >
< p > Your email is { user . email ? user . email : 'unknown' } . </ p >
</ div >
)
}
export default withUser ( ) ( Demo ) setAuthCookies(req, res)设置cookie来存储已验证的用户的信息。从您的“登录” API端点调用此。
饼干用cookies管理。请参阅cookie选项的配置。
req参数应为IncomingMessage / next.js请求对象。 res参数应为ServerResponse / Next.js响应对象。它要求将Authorization请求标头设置为Firebase用户ID令牌,该软件包会自动处理。
这只能在服务器端调用。
unsetAuthCookies(req, res)unset(过期)auth cookies。从您的“注销” API端点调用此。
req参数应为IncomingMessage / next.js请求对象。 res参数应为ServerResponse / Next.js响应对象。
这只能在服务器端调用。
verifyIdToken(token) => Promise<User>验证Firebase ID令牌并解决User实例。这具有与Firebase Admin SDK的验证性类似的目的。
getUserFromCookies({ ...options })在V1中添加
验证并从身份cookies返回user 。这是verifyIdToken的替代方法,它可以从ID代币中验证用户。
通常,我们建议API端点使用ID令牌而不是Cookie来识别用户,从而避免了一些潜在的CSRF漏洞。但是,此方法对于端点将很有帮助,必须仅依靠cookie值来识别用户。
这只能在服务器端调用。
有关在Next.js之外使用的独立后端环境中使用此信息的更多信息,请参见此示例。
选项参数可以包括:
Object - IncomingMessage / next.js请求对象
cookie标头值的请求对象将用于验证用户。需要req值或authCookieValue 。
Boolean
返回的用户是否应包含Firebase ID令牌。默认为true。如果是的,则行为遵循withUserTokenSSR的行为。当false时,它遵循withUserSSR 。阅读有关withuserssr文档中的区别的更多信息。
String
作为提供req对象的替代方案,您可以直接提供用于使用的auth cookie值。例如,如果您的auth cookie被命名为MyAuth ,则将提供cookie MyAuth.AuthUser (如果includeToken在false)或MyAuth.AuthUserTokens (如果includeToken话)的值。
需要req值或authCookieValue 。
String
如果使用签名的cookie,则身份cookie签名的价值。例如,如果您的auth cookie命名为MyAuth ,则将提供cookie MyAuth.AuthUser.sig (如果includeToken false)或MyAuth.AuthUserTokens.sig (如果includeToken话)的值。
AuthAction定义withUser和withUserTokenSSR的渲染/重定向选项的对象。请参阅Authaction。
请参阅此处的示例配置。致电init时提供配置。
String|Function|Object - pageurl
默认URL要导航到withUser或withUserTokenSSR时需要重定向到登录。可选,除非使用AuthAction.REDIRECT_TO_LOGIN auth Action。
String|Function|Object - pageurl
默认网址要导航到withUser或withUserTokenSSR时需要重定向到该应用程序。可选,除非使用AuthAction.REDIRECT_TO_APP auth Action。
String
当AUTH状态更改身份验证的Firebase用户时,该模块将调用API端点。
String
当AUTH状态更改未经身份验证的Firebase用户时,该模块将调用API端点。
Function (可选)
如果登录API端点返回非2000响应,则称为处理程序。如果未定义处理程序,则该库将对任何非2000响应进行投入。
如果设置了自定义tokenChangedHandler ,则不使用或允许。
Function (可选)
如果注销API端点返回非2000响应,则打电话给处理程序。如果未定义处理程序,则该库将对任何非2000响应进行投入。
如果设置了自定义tokenChangedHandler ,则不使用或允许。
Function
当特定用户更改auth状态时运行的回调。如果要自定义客户端应用程序如何调用您的登录/注销API端点(例如,使用自定义fetcher或添加自定义标头),请使用此。 tokenChangedHandler接收User作为参数,并在用户的ID令牌更改时被调用,类似于Firebase的onIdTokenChanged事件。
如果指定此回调,则用户负责:
有关指导,请参见默认处理程序。
String
本地Firebase Auth Mimulator的主机和端口。如果设置了此值,则将使用提供的主机和端口初始化AUTH模拟器。
必须匹配FIREBASE_AUTH_EMULATOR_HOST环境变量的值,例如localhost:9099 。
Object
配置传递给了firebase-admin的initializeApp 。它应包含credential属性(普通对象)和databaseURL属性。除非您在初始化next-firebase-auth之前先初始化firebase-admin初始初始化firebase-admin。
firebaseAdminInitConfig.credential.privateKey不能在客户端定义,并且应该生活在秘密环境变量中。
使用Vercel?有关指导,请参见在Vercel中添加私钥。
Boolean
如果是的,则firebase-admin将在initializeApp期间隐式找到您的托管环境服务帐户。这既适用于firebase和Google Cloud平台,并且建议通过文件路径或直接值将服务帐户密钥添加到代码。
注意:要设置firebase-admin ,必须提供firebaseAdminInitConfig或useFirebaseAdminDefaultCredential 。使用默认的凭据将覆盖firebaseAdminInitConfig.credential的值,如果两者都呈现。
Object
配置匹配Firebase JS SDK的initializeApp 。始终需要firebaseClientInitConfig.apiKey值。我们建议您在初始化next-firebase-auth批准之前先对firebase客户端SDK初始化;但是,如果尚不存在Firebase应用程序next-firebase-auth将尝试初始化firebase。
Object
用于身份cookie的设置。我们使用cookies管理饼干。
属性包括:
name :用作cookie名称的基础:如果将name设置为“ myexample”,则cookie将被命名为MyExample.AuthUser和MyExample.AuthUserTokens (plus MyExample.AuthUser.sig和MyExample.AuthUserTokens.sig cookies,如果签名)。必需的。keys :一系列将用于签名cookie的字符串;例如, ['xD$WVv3qrP3ywY', '2x6#msoUeNhVHr'] 。由于这些字符串是秘密的,因此通过秘密环境变量提供它们,例如[ process.env.COOKIE_SECRET_CURRENT, process.env.COOKIE_SECRET_PREVIOUS ] 。 keys阵列如cookies软件包中所述传递给键格构造函数。除非将signed设置为false ,否则需要。cookies.set的所有选项。 keys不能在客户端定义,并且应该生活在秘密环境变量中。
为了安全性, maxAge必须为两个星期或更短。请注意, maxAge以毫秒为单位定义。
注意:当用户加载Firebase JS SDK时,Cookie的到期将自动扩展。
Firebase JS SDK是身份验证的真实源,因此,如果cookie到期,但是用户仍使用firebase构建,则当用户加载firebase js sdk时,cookies将再次设置,但是用户将在SSR期间以第一个请求在SSR期间签发用户。
Function (可选)
如果出现意外错误,则在验证用户的ID令牌服务器端时将调用错误处理程序。它将收到Firebase auth错误。
当该库无法验证ID令牌时,该库将不会投掷。相反,它将为应用程序提供未经验证的用户。它通常会处理与Auth相关的常见错误,例如auth/id-token-expired和auth/user-disabled而无需投掷。有关其他背景,请参见#366和#174。
Function (可选)
如果在刷新用户的ID令牌服务器端时出现意外错误,则将调用错误处理程序。
当该库无法刷新ID令牌时,该库将不会投掷。相反,它将为应用程序提供未经验证的用户。有关其他背景,请参见#366和#174。
使用以下常数定义操作取决于用户的auth状态:
AuthAction.RENDER :渲染子部分
AuthAction.SHOW_LOADER :显示加载程序组件
AuthAction.RETURN_NULL :返回null而不是任何组件
AuthAction.REDIRECT_TO_LOGIN :重定向到登录页面
AuthAction.REDIRECT_TO_APP :重定向到应用
用户对象在服务器端和客户端上下文上使用。这是firebase用户的归一化表示。
ID- String|null
firebase用户的ID,或者如果未对用户进行身份验证,则为null。
电子邮件- String|null
firebase用户的电子邮件地址,如果用户没有电子邮件地址为止。
电子邮件verified Boolean
是否验证了用户的电子邮件地址。
Phonenumber- String|null
在v0.13.1中添加
firebase用户的电话号码,如果用户没有电话号码为止。
DisplayName String|null
在v0.13.1中添加
firebase用户的显示名称,如果用户没有显示名称,则为null。
photourl- String|null
在v0.13.1中添加
Firebase用户的照片URL或用户没有照片URL(如果没有照片URL)。
主张- Object
在v0.13.0中添加
任何自定义的火箱要求。
getIdtoken- Function => Promise<String|null>
解决有效的firebase ID令牌字符串的异步函数,如果没有有效的令牌,则无效。
客户化- Boolean
Firebase JS SDK是否已初始化。如果为true ,我们将不再使用服务器端道具的任何用户信息。
firebaseuser FirebaseUser |null
如果已经初始化了Firebase JS SDK的用户。否则,null。
签名- Function => Promise<void>
如果firebase JS SDK已初始化,则称为Firebase signOut的方法。如果尚未初始化SDK,则此方法为NO-OP。
String|Function|Object
PageUrl在配置和高阶组件中用于appPageURL和authPageURL ,定义了重定向目标URL或路径。
它可以是字符串: /my-url/here/
或对象:
{
destination : '/my-url/here/' , // Required string: the URL destination of a redirect
basePath : false , // whether to use the Next.js base path.
}或接收{ ctx, user }并返回字符串或redirectObject的函数:
const redirect = ( { ctx , user } ) => {
// any custom logic here
return `/my-url/here/?username= ${ user . displayName } `
}如果服务器端,则ctx是下一个。JS上下文值,如果客户端端,则不确定。
请参阅示例。
卡住?搜索讨论或打开您自己的问答讨论,描述您已经尝试过的内容。
这是您可以采取一些初始步骤来调试问题:
onVerifyTokenError和onTokenRefreshError ,并检查任何错误日志。debug: true ,并通过服务器端和客户端调试日志阅读,以获取任何有用的消息。我们希望某些敏感的配置值在客户端是虚假的(请参阅配置验证代码)。这是一种预防措施,以确保开发人员不会意外地将诸如Firebase私钥与客户JS捆绑在一起。
要解决此问题,请通过将配置设置记录到浏览器控制台,确保客户端undefined 。您可以使用Next的.env支持来设置仅服务器的变量。切勿将NEXT_PUBLIC*前缀用于任何秘密值。
当需要刷新令牌服务器端时,该软件包将调用Google端点。您正在看到该请求的错误。
要解决此问题,请确认您的firebaseAdminInitConfig.credential.clientEmail是正确的。它应该是与Firebase私钥配对的电子邮件。
如果这无济于事,请尝试检查自定义令牌以手动验证值和结构。当他们的服务器时间不正确时,有些人会遇到此问题。
withUserTokenSSR时,用户和令牌始终为null,但客户端auth有效。如果Auth在客户端工作,但在服务器端不工作,则auth Cookie很可能未设置。
为了解决此问题,请确认Auth cookie是在浏览器的开发工具中设置的。如果未设置,请检查在next-firebase-auth配置中通过的secure , sameSite和path选项是否对您的环境有意义。例如,如果您要在非HTTPS Localhost上进行测试,请确保secure是错误的。
此外,请仔细检查您的服务器日志是否有任何错误,以确保Firebase Admin应用程序正常初始化。
通常,这是由Firebase凭据中的不正确的电子邮件引起的。请验证电子邮件是正确的,并且来自与您的私钥的同一Firebase帐户,或尝试生成新密钥:https://firebase.google.com/docs/admin/setup
您可以尝试在示例应用程序中设置凭据,以确保您的应用程序代码不是问题。
在本地开发中,如果您先前与另一个Firebase帐户签署并仍然具有另一个私钥签署的auth Cookie,请尝试清除localhost的数据/cookie。
您也可以尝试禁用Firebase身份验证模拟器。从您的配置中删除firebaseAuthEmulatorHost ,然后从.env文件中删除FIREBASE_AUTH_EMULATOR_HOST 。
请参阅将私有密钥添加到Vercel,以及有关私钥格式的讨论。
我们预计某些应用程序将需要一些当前尚未可用的功能:
我们很想听听您对这些或其他功能的反馈。请随时进行讨论!
我们欢迎捐款!请参阅贡献文档以开始。