所有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,以及有關私鑰格式的討論。
我們預計某些應用程序將需要一些當前尚未可用的功能:
我們很想听聽您對這些或其他功能的反饋。請隨時進行討論!
我們歡迎捐款!請參閱貢獻文檔以開始。