Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
next-firebase-auth
Advanced tools
Simple Firebase authentication for all Next.js rendering strategies
Simple Firebase authentication for all Next.js rendering strategies.
This package makes it simple to get the authenticated Firebase user and ID token during both client-side and server-side rendering (SSR).
We treat the Firebase JS SDK as the source of truth for auth status. When the user signs in, we call an endpoint to generate a refresh token and store the user info, ID token, and refresh token in cookies. Future requests to SSR pages receive the user info and ID token from cookies, refreshing the ID token as needed. When the user logs out, we unset the cookies.
See a live demo of the example app.
Depending on your app's needs, other approaches might work better for you.
If your app only uses static pages or doesn't need the Firebase user for SSR, use the Firebase JS SDK directly to load the user on the client side.
getServerSideProps
.If your app needs the Firebase user for SSR (but does not need the ID token server-side), you could consider one of these approaches:
This package will likely be helpful if you expect to use both static pages and SSR or if you need access to Firebase ID tokens server-side. Please check out current limitations before diving in.
Install:
yarn add next-firebase-auth
Make sure peer dependencies are also installed:
yarn add firebase firebase-admin next react react-dom
Create a module to initialize next-firebase-auth
.
// ./initAuth.js
import { init } from 'next-firebase-auth'
const initAuth = () => {
init({
authPageURL: '/auth',
appPageURL: '/',
loginAPIEndpoint: '/api/login', // required
logoutAPIEndpoint: '/api/logout', // required
// Required in most cases.
firebaseAdminInitConfig: {
credential: {
projectId: 'my-example-app-id',
clientEmail: 'example-abc123@my-example-app.iam.gserviceaccount.com',
// The private key must not be accesssible on the client side.
privateKey: process.env.FIREBASE_PRIVATE_KEY,
},
databaseURL: 'https://my-example-app.firebaseio.com',
},
firebaseClientInitConfig: {
apiKey: 'MyExampleAppAPIKey123', // required
authDomain: 'my-example-app.firebaseapp.com',
databaseURL: 'https://my-example-app.firebaseio.com',
projectId: 'my-example-app-id',
},
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,
},
})
}
export default initAuth
Set the private environment variables FIREBASE_PRIVATE_KEY
, COOKIE_SECRET_CURRENT
, and COOKIE_SECRET_CURRENT
in .env.local
. See the config documentation for details.
Initialize next-firebase-auth
in _app.js
:
// ./pages/_app.js
import initAuth from '../initAuth' // the module you created above
initAuth()
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
Create login and logout API endpoints that set auth cookies:
// ./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
Finally, use the authenticated user in a page:
// ./pages/demo
import React from 'react'
import {
useAuthUser,
withAuthUser,
withAuthUserTokenSSR,
} from 'next-firebase-auth'
const Demo = () => {
const AuthUser = useAuthUser()
return (
<div>
<p>Your email is {AuthUser.email ? AuthUser.email : "unknown"}.</p>
</div>
)
}
// Note that this is a higher-order function.
export const getServerSideProps = withAuthUserTokenSSR()()
export default withAuthUser()(Demo)
init(config)
Initializes next-firebase-auth
, taking a config object. Must be called before calling any other method.
withAuthUser({ ...options })(PageComponent)
A higher-order function to provide the AuthUser
context to a component. Use this with any Next.js page that will access the authed user via the useAuthUser
hook. Optionally, it can client-side redirect based on the user's auth status.
It accepts the following options:
Option | Description | Default |
---|---|---|
whenAuthed | The action to take if the user is authenticated. One of AuthAction.RENDER or AuthAction.REDIRECT_TO_APP . | AuthAction.RENDER |
whenUnauthedBeforeInit | The action to take if the user is not authenticated but the Firebase client JS SDK has not yet initialized. One of: AuthAction.RENDER , AuthAction.REDIRECT_TO_LOGIN , AuthAction.SHOW_LOADER . | AuthAction.RENDER |
whenUnauthedAfterInit | The action to take if the user is not authenticated and the Firebase client JS SDK has already initialized. One of: AuthAction.RENDER , AuthAction.REDIRECT_TO_LOGIN . | AuthAction.RENDER |
appPageURL | The redirect destination URL when we should redirect to the app. | config.appPageURL |
authPageURL | The redirect destination URL when we should redirect to the login page. | config.authPageURL |
LoaderComponent | The component to render when the user is unauthed and whenUnauthedBeforeInit is set to AuthAction.SHOW_LOADER . | null |
For example, this page will redirect to the login page if the user is not authenticated:
import { withAuthUser, AuthAction } from 'next-firebase-auth'
const DemoPage = () => <div>My demo page</div>
export default withAuthUser({
whenUnauthedAfterInit: AuthAction.REDIRECT_TO_LOGIN,
authPageURL: '/my-login-page/'
})(DemoPage)
Here's an example of a login page that shows a loader until Firebase is initialized, then redirects to the app if the user is already logged in:
import { withAuthUser, AuthAction } from 'next-firebase-auth'
const MyLoader = () => <div>Loading...</div>
const LoginPage = () => <div>My login page</div>
export default withAuthUser({
whenAuthed: AuthAction.REDIRECT_TO_APP,
whenUnauthedBeforeInit: AuthAction.SHOW_LOADER,
whenUnauthedAfterInit: AuthAction.RENDER,
LoaderComponent: MyLoader,
})(LoginPage)
withAuthUserTokenSSR({ ...options })(getServerSidePropsFunc = ({ AuthUser }) => {})
A higher-order function that wraps a Next.js pages's getServerSideProps
function to provide the AuthUser
context during server-side rendering. Optionally, it can server-side redirect based on the user's auth status. A wrapped function is optional; if provided, it will be called with a context
object that contains an AuthUser
property.
It accepts the following options:
Option | Description | Default |
---|---|---|
whenAuthed | The action to take if the user is authenticated. Either AuthAction.RENDER or AuthAction.REDIRECT_TO_APP . | AuthAction.RENDER |
whenUnauthed | The action to take if the user is not authenticated. Either AuthAction.RENDER or AuthAction.REDIRECT_TO_LOGIN . | AuthAction.RENDER |
appPageURL | The redirect destination URL when we should redirect to the app. | config.appPageURL |
authPageURL | The redirect destination URL when we should redirect to the login page. | config.authPageURL |
For example, this page will SSR for authenticated users, fetching props using their Firebase ID token, and will server-side redirect to the login page if the user is not authenticated:
import { withAuthUser, AuthAction } from 'next-firebase-auth'
const DemoPage = ({ thing }) => <div>The thing is: {thing}</div>
export const getServerSideProps = withAuthUserTokenSSR({
whenUnauthed: AuthAction.REDIRECT_TO_LOGIN,
})(async ({ AuthUser }) => {
// Optionally, get other props.
const token = await AuthUser.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 withAuthUser()(DemoPage)
withAuthUserSSR({ ...options })(getServerSidePropsFunc = ({ AuthUser }) => {})
Behaves nearly identically to withAuthUserTokenSSR
, with one key difference: it does not validate an ID token. Instead, it simply uses the AuthUser
data from a cookie. Consequently:
AuthUser
provided via context will resolve to null when you call AuthUser.getIdToken()
.withAuthUserTokenSSR
.withAuthUserTokenSSR
.This takes the same options as withAuthUserTokenSSR
.
useAuthUser()
A hook that returns the current AuthUser
. To use this, the Next.js page must be wrapped in withAuthUser
. If the user is not authenticated, useAuthUser
will return an AuthUser
instance with a null id
.
For example:
import { useAuthUser, withAuthUser } from 'next-firebase-auth'
const Demo = () => {
const AuthUser = useAuthUser()
return (
<div>
<p>Your email is {AuthUser.email ? AuthUser.email : "unknown"}.</p>
</div>
)
}
export default withAuthUser()(DemoPage)
setAuthCookies(req, res)
Sets cookies to store the authenticated user's info. Call this from your "login" API endpoint.
Cookies are managed with cookies
. See the config for cookie options.
The req
argument should be an IncomingMessage
/ Next.js request object. The res
argument should be a ServerResponse
/ Next.js response object. It requires that the Authorization
request header be set to the Firebase user ID token, which this package handles automatically.
This can only be called on the server side.
unsetAuthCookies(req, res)
Unsets (expires) the auth cookies. Call this from your "logout" API endpoint.
The req
argument should be an IncomingMessage
/ Next.js request object. The res
argument should be a ServerResponse
/ Next.js response object.
This can only be called on the server side.
verifyIdToken(token) => Promise<AuthUser>
Verifies a Firebase ID token and resolves to an AuthUser
instance. This serves a similar purpose as Firebase admin SDK's verifyIdToken.
AuthAction
An object that defines rendering/redirecting options for withAuthUser
and withAuthUserTokenSSR
. See AuthAction.
See an example config here. Provide the config when you call init
.
authPageURL: The default URL to navigate to when withAuthUser
or withAuthUserTokenSSR
need to redirect to login. Optional unless using the AuthAction.REDIRECT_TO_LOGIN
auth action.
appPageURL: The default URL to navigate to when withAuthUser
or withAuthUserTokenSSR
need to redirect to the app. Optional unless using the AuthAction.REDIRECT_TO_APP
auth action.
loginAPIEndpoint: The API endpoint to call when the auth state changes for an authenticated Firebase user. Must be set unless tokenChangedHandler
is set.
logoutAPIEndpoint: The API endpoint to call when the auth state changes for an unauthenticated Firebase user. Must be set unless tokenChangedHandler
is set.
tokenChangedHandler: A callback that runs when the auth state changes for a particular user. Use this if you want to customize how your client-side app calls your login/logout API endpoints (for example, to use a custom fetcher or add custom headers). tokenChangedHandler
receives an AuthUser
as an argument and is called when the user's ID token changes, similarly to Firebase's onIdTokenChanged
event.
If this callback is specified, user is responsible for:
Cannot be set with loginAPIEndpoint
or logoutAPIEndpoint
.
Configuration passed to firebase-admin
's initializeApp
. It should contain a credential
property (a plain object) and a databaseURL
property. Required unless you initialize firebase-admin
yourself before initializing next-firebase-auth
.
The firebaseAdminInitConfig.credential.privateKey
cannot be defined on the client side and should live in a secret environment variable.
Note: if using environent variables in Vercel, add the private key with double quotes via the CLI:
vercel secrets add firebase-private-key '"my-key-here"'
Then, use
JSON.parse
in thefirebaseAdminInitConfig.credential.privateKey
property:privateKey: process.env.FIREBASE_PRIVATE_KEY ? JSON.parse(process.env.FIREBASE_PRIVATE_KEY) : undefined
See this Vercel issue for more information.
firebaseClientInitConfig: Configuration passed to the Firebase JS SDK's initializeApp
. The "firebaseClientInitConfig.apiKey" value is always required. Other properties are required unless you initialize the firebase
app yourself before initializing next-firebase-auth
.
Settings used for auth cookies. We use cookies
to manage cookies.
Properties include:
name
: Used as a base for cookie names: if name
is set to "MyExample", cookies will be named MyExample.AuthUser
and MyExample.AuthUserTokens
(plus MyExample.AuthUser.sig
and MyExample.AuthUserTokens.sig
if cookies are signed). Required.keys
: Used to sign cookies, as described in cookies
. Required unless signed
is set to false
.cookies.set
.The keys
value cannot be defined on the client side and should live in a secret environment variable.
For security, the maxAge
value must be two weeks or less. Note that maxAge
is defined in milliseconds.
Note: The cookies' expirations will be extended automatically when the user loads the Firebase JS SDK.
The Firebase JS SDK is the source of truth for authentication, so if the cookies expire but the user is still authed with Firebase, the cookies will be automatically set again when the user loads the Firebase JS SDK—but the user will not be authed during SSR on that first request.
Defines actions to take depending on on a user's auth status, using the following constants:
AuthAction.RENDER
: render the child component
AuthAction.SHOW_LOADER
: show a loader component
AuthAction.RETURN_NULL
: return null instead of any component
AuthAction.REDIRECT_TO_LOGIN
: redirect to the login page
AuthAction.REDIRECT_TO_APP
: redirect to the app
The auth user object used across server- and client-side contexts. This is a normalized representation of a Firebase user.
id - String|null
The Firebase user's ID, or null if the user is not authenticated.
email - String|null
The Firebase user's email address, or null if the user has no email address.
emailVerified - Boolean
Whether the user's email address is verified.
getIdToken - Function => Promise<String|null>
An async function that resolves to a valid Firebase ID token string, or null if no valid token is available.
clientInitialized - Boolean
Whether the Firebase JS SDK has initialized. If true
, we are no longer using any user info from server-side props.
firebaseUser - FirebaseUser
|null
The user from the Firebase JS SDK, if it has initialized. Otherwise, null.
signOut - Function => Promise<void>
A method that calls Firebase's signOut
if the Firebase JS SDK has initialized. If the SDK has not initialized, this method is a noop.
We expect some apps will need some additional customization that's not currently available:
authPageURL
and appPageURL
are static. We may want to allow a function so the app can determine the redirect destination based on, for example, a URL parameter value. Check out this enhancement for details. It is however possible to perform custom routing at SSR by leveraging the officially supported notFound
and redirect
objects that getServerSideProps
may return.AuthUser
object does not include any Firebase custom claims.We'd love to hear your feedback on these or other features. Please feel free to open a discussion!
We welcome contributions! Please feel free to jump into any open issues.
It can be helpful to use an in-development version of next-firebase-auth
in another app:
yarn global add yalc
next-firebase-auth
, publish a local version: yarn run dev:publish
-- this builds your local package code, then publishes it with Yalcyalc add next-firebase-auth
next-firebase-auth
, use yarn run dev:publish
again to use the latest local code in your appFAQs
Simple Firebase authentication for all Next.js rendering strategies
The npm package next-firebase-auth receives a total of 939 weekly downloads. As such, next-firebase-auth popularity was classified as not popular.
We found that next-firebase-auth demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.