Coder Social home page Coder Social logo

gladly-team / next-firebase-auth Goto Github PK

View Code? Open in Web Editor NEW
1.3K 11.0 287.0 3.08 MB

Simple Firebase authentication for all Next.js rendering strategies

Home Page: https://nfa-example-git-v1x-gladly-team.vercel.app/

License: MIT License

JavaScript 14.08% CSS 0.06% TypeScript 85.87%
next nextjs firebase firebase-js-sdk firebase-authentication cookies

next-firebase-auth's Introduction

Build Status codecov npm Bundle size Contributor Covenant

next-firebase-auth

Simple Firebase authentication for all Next.js rendering strategies.

What It Does

This package makes it simple to get the authenticated Firebase user and ID token during both client-side and server-side rendering (SSR).

     🌍   Support for all Next.js rendering strategies
     🔒   Signed, secure, HTTP-only cookies by default
     🆔   Server-side access to the user's Firebase ID token
     🍪   Built-in cookie management
     ↩️   Built-in support for redirecting based on the user's auth status

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.

Demo

See a live demo of the example app.

When (Not) to Use this Package

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.

  • Pros: It's simpler and removes this package as a dependency.
  • Cons: You will not have access to the Firebase user when you use 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:

  1. On the client, set a JavaScript cookie with the Firebase user information once the Firebase JS SDK loads.
    • Pros: You won't need login/logout API endpoints. You can structure the authed user data however you'd like.
    • Cons: The cookie will be unsigned and accessible to other JavaScript, making this approach less secure. You won't always have access to the Firebase ID token server side, so you won't be able to access other Firebase services. (Note that you can set the ID token in the cookie, but it will expire after an hour and be invalid for future server-side-rendered pages.)
  2. Use Firebase's session cookies.
    • Pros: It removes this package as a dependency.
    • Cons: You won't have access to the Firebase ID token server side, so you won't be able to access other Firebase services. You'll need to implement the logic for verifying the session and managing the session state.

If your app needs a generalized authentication solution—not specifically Firebase authentication—you could consider using NextAuth.js. NextAuth.js does not use Firebase authentication but supports a wide variety of identity providers, including Google. Read more here about the differences between next-firebase-auth and NextAuth.js to see which works best for your needs.

If your app uses Next.js's app router, this package does not yet support it. You can follow progress in #568.

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.

A quick note on what this package does not do:

  • It does not provide authentication UI. Consider firebaseui-web or build your own.
  • It does not extend Firebase functionality beyond providing universal access to the authed user. Use the Firebase admin SDK and Firebase JS SDK for any other needs.

Get Started

Install:

yarn add next-firebase-auth or npm i 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.

Example config:

See config documentation for details

// ./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

Set the private environment variables FIREBASE_PRIVATE_KEY, COOKIE_SECRET_CURRENT, and COOKIE_SECRET_PREVIOUS in .env.local. If you have enabled the Firebase Authentication Emulator, you will also need to set the FIREBASE_AUTH_EMULATOR_HOST environment variable.

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 {
  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)

API


init(config)

Initializes next-firebase-auth, taking a config object.

  • This must before calling any other method.
  • We recommend initializing the Firebase client SDK prior to calling this.

withUser({ ...options })(PageComponent)

A higher-order function to provide the User context to a component. Use this with any Next.js page that will access the authed user via the useUser 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
whenAuthedBeforeRedirect The action to take while waiting for the browser to redirect. Relevant when the user is authenticated and whenAuthed is set to AuthAction.REDIRECT_TO_APP. One of: AuthAction.RENDER or AuthAction.SHOW_LOADER or AuthAction.RETURN_NULL. AuthAction.RETURN_NULL
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. A PageURL. config.appPageURL
authPageURL The redirect destination URL when we should redirect to the login page. A PageURL. 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 { 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)

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 { 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)

For TypeScript usage, take a look here.

withUserTokenSSR({ ...options })(getServerSidePropsFunc = ({ user }) => {})

A higher-order function that wraps a Next.js pages's getServerSideProps function to provide the User 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 user 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. A PageURL. config.appPageURL
authPageURL The redirect destination URL when we should redirect to the login page. A PageURL. 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 {
  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 }) => {})

Behaves nearly identically to withUserTokenSSR, with one key difference: the user will not contain an ID token.

This method relies on authed user data from a cookie rather than verify or refresh a Firebase ID token. Consequently:

  • It does not provide an ID token on the server side. The user provided via context will resolve to null when you call user.getIdToken().
  • It does not need to make a network request to refresh an expired ID token, so it will, on average, be faster than withUserTokenSSR.
  • It does not check for token revocation. If you need verification that the user's credentials haven't been revoked, you should always use withUserTokenSSR.

⚠️ Do not use this when cookies.signed is set to false. Doing so is a potential security risk, because the authed user cookie values could be modified by the client.

This takes the same options as withUserTokenSSR.

useUser()

A hook that returns the current user. To use this, the Next.js page must be wrapped in withUser. If the user is not authenticated, useUser will return a User instance with a null id.

For example:

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)

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<User>

Verifies a Firebase ID token and resolves to an User instance. This serves a similar purpose as Firebase admin SDK's verifyIdToken.

getUserFromCookies({ ...options })

Added in v1

Verifies and returns the user from auth cookies. This is an alternative to verifyIdToken, which verifies the user from an ID token.

In general, we recommend that API endpoints use an ID token rather than cookies to identify the user, which avoids some potential CSRF vulnerabilities. However, this method will be helpful for endpoints must rely exclusively on cookie values to identify the user.

This can only be called on the server side.

See this example for more information on using this in a standalone backend environment outside of Next.js.

The options argument can include:

req

Object – an IncomingMessage / Next.js request object

A request object whose cookie header value will be used to verify a user. Either the req value or authCookieValue are required.

includeToken

Boolean

Whether or not the returned user should include a Firebase ID token. Defaults to true. When true, the behavior follows that of withUserTokenSSR; when false, it follows that of withUserSSR. Read more about the distinction in the docs for withUserSSR.

authCookieValue

String

As an alternative to providing the req object, you can directly provide the auth cookie value to use. For example, if your auth cookie is named MyAuth, you would provide the value of the cookie MyAuth.AuthUser (if includeToken is false) or MyAuth.AuthUserTokens (if includeToken is true).

Either the req value or authCookieValue are required.

authCookieSigValue

String

The value of the auth cookie's signature, if using signed cookies. For example, if your auth cookie is named MyAuth, you would provide the value of the cookie MyAuth.AuthUser.sig (if includeToken is false) or MyAuth.AuthUserTokens.sig (if includeToken is true).

AuthAction

An object that defines rendering/redirecting options for withUser and withUserTokenSSR. See AuthAction.

Config

See an example config here. Provide the config when you call init.

authPageURL

String|Function|Object – a PageURL

The default URL to navigate to when withUser or withUserTokenSSR need to redirect to login. Optional unless using the AuthAction.REDIRECT_TO_LOGIN auth action.

appPageURL

String|Function|Object – a PageURL

The default URL to navigate to when withUser or withUserTokenSSR need to redirect to the app. Optional unless using the AuthAction.REDIRECT_TO_APP auth action.

loginAPIEndpoint

String

The API endpoint this module will call when the auth state changes for an authenticated Firebase user.

logoutAPIEndpoint

String

The API endpoint this module will call when the auth state changes for an unauthenticated Firebase user.

onLoginRequestError

Function (optional)

A handler called if the login API endpoint returns a non-200 response. If a handler is not defined, this library will throw on any non-200 responses.

Not used or allowed if a custom tokenChangedHandler is set.

onLogoutRequestError

Function (optional)

A handler called if the logout API endpoint returns a non-200 response. If a handler is not defined, this library will throw on any non-200 responses.

Not used or allowed if a custom tokenChangedHandler is set.

tokenChangedHandler

Function

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 a User 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:

  1. Calling their login/logout endpoints depending on the user's auth state.
  2. Passing the user's ID token in the Authorization header
  3. Ensuring it allows the request to set cookies.

See the default handler for guidance.

firebaseAuthEmulatorHost

String

The host and port for the local Firebase Auth Emulator. If this value is set, the auth emulator will be initialized with the provided host and port.

Must match the value of the FIREBASE_AUTH_EMULATOR_HOST environment variable, e.g., localhost:9099.

firebaseAdminInitConfig

Object

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.

ℹ️ Using Vercel? See adding a private key to Vercel for guidance.

useFirebaseAdminDefaultCredential

Boolean

When true, firebase-admin will implicitly find your hosting environment service account during initializeApp. This is applicable for both Firebase, and Google Cloud Platform, and recommended over adding service account key to the code via file path or direct value.

Note: To setup firebase-admin, either firebaseAdminInitConfig or useFirebaseAdminDefaultCredential must be provided. Using the default credentials will override values passed to firebaseAdminInitConfig.credential if both are presented.

firebaseClientInitConfig

Object

Configuration matching Firebase JS SDK's initializeApp. The firebaseClientInitConfig.apiKey value is always required. We recommend initializing the Firebase client SDK yourself prior to initializing next-firebase-auth; however, next-firebase-auth will attempt to initialize Firebase if a Firebase app does not already exist.

cookies

Object

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: An array of strings that will be used to sign cookies; for instance, ['xD$WVv3qrP3ywY', '2x6#msoUeNhVHr']. As these strings are secrets, provide them via secret environment variables, such as [ process.env.COOKIE_SECRET_CURRENT, process.env.COOKIE_SECRET_PREVIOUS ]. The keys array is passed to the Keygrip constructor as described in the cookies package. Required unless signed is set to false.
  • All options for 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.

onVerifyTokenError

Function (optional)

Error handler that will be called if there's an unexpected error while verifying the user's ID token server side. It will receive a Firebase auth error.

This library will not throw when it cannot verify an ID token. Instead, it will provide an unauthenticated user to the app. It will typically handle common auth-related errors such as auth/id-token-expired and auth/user-disabled without throwing. See #366 and #174 for additional background.

onTokenRefreshError

Function (optional)

Error handler that will be called if there's an unexpected error while refreshing the user's ID token server side.

This library will not throw when it cannot refresh an ID token. Instead, it will provide an unauthenticated user to the app. See #366 and #174 for additional background.

Types

AuthAction

Defines actions to take depending 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

User

The user object is used across server-side 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.

phoneNumber - String|null

Added in v0.13.1

The Firebase user's phone number, or null if the user has no phone number.

displayName - String|null

Added in v0.13.1

The Firebase user's display name, or null if the user has no display name.

photoURL - String|null

Added in v0.13.1

The Firebase user's photo URL, or null if the user has no photo URL.

claims - Object

Added in v0.13.0

Any custom Firebase claims.

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 been initialized. Otherwise, null.

signOut - Function => Promise<void>

A method that calls Firebase's signOut if the Firebase JS SDK has been initialized. If the SDK has not been initialized, this method is a no-op.

PageURL

String|Function|Object

Used in appPageURL and authPageURL in the config and higher-order components, the PageURL defines a redirect destination URL or path.

It can be a string: /my-url/here/

Or an object:

{
  destination: '/my-url/here/', // Required string: the URL destination of a redirect
  basePath: false, // whether to use the Next.js base path.
}

Or a function that receives { ctx, user } and returns a string or RedirectObject:

const redirect = ({ ctx, user }) => {
  // any custom logic here
  return `/my-url/here/?username=${user.displayName}`
}

The ctx is the Next.js context value if server side, or undefined if client side.

Examples

See EXAMPLES.md.

Troubleshooting

Stuck? Search discussions or open your own Q&A discussion describing what you've already tried.

Something's not working.

Here are some initial steps you can take to debug problems:

  1. Define onVerifyTokenError and onTokenRefreshError in your config and check for any error logs.
  2. Set debug: true in your config and read through server-side and client-side debug logs for any helpful messages.
  3. Try the example app with your own Firebase credentials.
  4. Read through other troubleshooting tips below!

I get the error "[Some setting] should not be available on the client side."

We expect certain sensitive config values to be falsy on the client side (see the config validation code). This is a precaution to make sure developers aren't accidentally bundling something like their Firebase private key with client JS.

To fix this, ensure the config setting is undefined on the client side by logging it to your browser console. You can use Next's .env support to set server-only variables. Never use the NEXT_PUBLIC* prefix for any secret values.

I get an "INVALID_CUSTOM_TOKEN" error when trying to get a refresh token.

This package will call a Google endpoint when it needs to refresh a token server side. You're seeing an error from that request.

To fix this, confirm that your firebaseAdminInitConfig.credential.clientEmail is correct. It should be the email paired with your Firebase private key.

If that doesn't help, try inspecting the custom token to manually validate the values and structure. Some people encounter this problem when their server time is incorrect.

Server-side auth is not working. The user and token are always null when using withUserTokenSSR, but client-side auth works.

If auth is working on the client side but not on the server side, the auth cookies are most likely not set.

To fix this, confirm the auth cookies are set in your browser's dev tools. If they're not set, please check that the secure, sameSite, and path options passed in the next-firebase-auth config make sense for your environment. For example, if you're testing on non-HTTPS localhost, make sure secure is false.

In addition, please double-check your server logs for any errors to ensure the Firebase admin app is initializing properly.

I get an "auth/argument-error" with message "Firebase ID token has invalid signature".

Often, this is caused by an incorrect email in Firebase credentials. Please verify that the email is correct and is from the same Firebase account as your private key, or try generating a new key: https://firebase.google.com/docs/admin/setup

You can try setting up your credentials in the example app to be sure your app code isn't a problem.

In local development, try clearing data/cookies for localhost in case you previously signed in with another Firebase account and still have auth cookies signed by another private key.

You can also try disabling the Firebase Authentication Emulator. Remove firebaseAuthEmulatorHost from your config and remove FIREBASE_AUTH_EMULATOR_HOST from your .env file.

I get the error, "Failed to parse private key: Error: Invalid PEM formatted message."

See adding a private key to Vercel and this discussion on private key formatting.

Limitations & Feedback

We expect some apps will need some features that are not currently available:

  • Supporting custom session logic: Currently, this package doesn't allow using a custom cookie or session module. Some developers may need this flexibility to, for example, keep user data in server-side session storage.
  • Setting a single auth cookie: This package currently sets more than one cookie to store authentication state. It's not currently possible to use a single cookie with a customized name: #190

We'd love to hear your feedback on these or other features. Please feel free to open a discussion!

Contributing

We welcome contributions! Please see contributing docs to get started.

next-firebase-auth's People

Contributors

abusada avatar andreyk-code avatar briandonahue avatar chimericdream avatar edwardsmoses avatar grahaml avatar hofmannz avatar izhaki avatar jagritvats avatar jedtan avatar juhanakristian avatar kmishmael avatar kmjennison avatar litewarp avatar mvremmerden avatar tlamarre91 avatar tlays avatar valeriangalliat avatar velddev avatar yantakus avatar zaverden avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

next-firebase-auth's Issues

Question: how do I get Id token from signout request?

The case is: I need to call admin.auth().revokeRefreshTokens(uid) at the sign out request.

The problems:

  1. tokenChangedHandler only has a token at sign in and I can't set a header at sign out
  2. It's said in readme Future requests to SSR pages receive the user info and ID token from cookies, but there is no example how to verify cookies, and admin.auth().verifySessionCookie fails to accept cookies I can take from req and there is no ID token in req.

How to use this package with SMS verification

Hello,

I would like to know how SMS 2FA could be enabled using this package. Is it actually supported and if yes how would an example code look like? I could not find any example so far.

Thanks

Getting 500 INTERNAL_SERVER_ERROR on SSR page

Describe the bug

I've had a user let me know that they are getting a 500 Internal Server error when trying to hit an SSR page that uses next-firebase-auth.

Not sure if this is me doing something wrong or a bug as I can't seem to replicate this on my end.

I checked the logs in Vercel and saw this:

[GET] /cart
15:29:43:52
2021-03-16T04:29:45.461Z	a5539b48-1846-4b8a-8914-d28700cb65a6	ERROR	Unhandled error during request: FirebaseAuthError: Firebase ID token has "kid" claim which does not correspond to a known public key. Most likely the ID token is expired, so get a fresh token from your client app and try again.
    at FirebaseAuthError.FirebaseError [as constructor] (/var/task/node_modules/firebase-admin/lib/utils/error.js:44:28)
    at FirebaseAuthError.PrefixedFirebaseError [as constructor] (/var/task/node_modules/firebase-admin/lib/utils/error.js:90:28)
    at new FirebaseAuthError (/var/task/node_modules/firebase-admin/lib/utils/error.js:149:16)
    at /var/task/node_modules/firebase-admin/lib/auth/token-verifier.js:177:39
    at processTicksAndRejections (internal/process/task_queues.js:93:5) {
  errorInfo: {
    code: 'auth/argument-error',
    message: 'Firebase ID token has "kid" claim which does not correspond to a known public key. Most likely the ID token is expired, so get a fresh token from your client app and try again.'
  },
  codePrefix: 'auth'
}
2021-03-16T04:29:45.461Z	a5539b48-1846-4b8a-8914-d28700cb65a6	ERROR	FirebaseAuthError: Firebase ID token has "kid" claim which does not correspond to a known public key. Most likely the ID token is expired, so get a fresh token from your client app and try again.
    at FirebaseAuthError.FirebaseError [as constructor] (/var/task/node_modules/firebase-admin/lib/utils/error.js:44:28)
    at FirebaseAuthError.PrefixedFirebaseError [as constructor] (/var/task/node_modules/firebase-admin/lib/utils/error.js:90:28)
    at new FirebaseAuthError (/var/task/node_modules/firebase-admin/lib/utils/error.js:149:16)
    at /var/task/node_modules/firebase-admin/lib/auth/token-verifier.js:177:39
    at processTicksAndRejections (internal/process/task_queues.js:93:5) {
  errorInfo: {
    code: 'auth/argument-error',
    message: 'Firebase ID token has "kid" claim which does not correspond to a known public key. Most likely the ID token is expired, so get a fresh token from your client app and try again.'
  },
  codePrefix: 'auth'
}
2021-03-16T04:29:45.462Z	a5539b48-1846-4b8a-8914-d28700cb65a6	ERROR	Unhandled Promise Rejection 	{"errorType":"Runtime.UnhandledPromiseRejection","errorMessage":"Error: Firebase ID token has \"kid\" claim which does not correspond to a known public key. Most likely the ID token is expired, so get a fresh token from your client app and try again.","reason":{"code":"auth/argument-error","message":"Firebase ID token has \"kid\" claim which does not correspond to a known public key. Most likely the ID token is expired, so get a fresh token from your client app and try again."},"promise":{},"stack":["Runtime.UnhandledPromiseRejection: Error: Firebase ID token has \"kid\" claim which does not correspond to a known public key. Most likely the ID token is expired, so get a fresh token from your client app and try again.","    at process.<anonymous> (/var/runtime/index.js:35:15)","    at process.emit (events.js:327:22)","    at processPromiseRejections (internal/process/promises.js:245:33)","    at processTicksAndRejections (internal/process/task_queues.js:94:32)"]}
Unknown application error occurred

Version

"next-firebase-auth": "^0.13.0-alpha.0",

To Reproduce
I haven't been able to reproduce this bug on my end, so not sure what's causing it.

Expected behavior
No 500 error 😃

Additional context

Here are the exports for the SSR page that is causing the error:

const getServerSideProps = withAuthUserTokenSSR({
  whenAuthed: AuthAction.RENDER,
  whenUnauthed: AuthAction.RENDER,
})(async ({ AuthUser }) => {
  const topSelling = await getTopSelling({
    query: `available_for_sale:true`,
  });
  const carouselSlides = await getAllSlides();

  return {
    props: {
      topSelling,
      carouselSlides,
    },
  };
});

export { getServerSideProps };
export default withAuthUser({
  whenAuthed: AuthAction.RENDER,
  whenUnauthedBeforeInit: AuthAction.RENDER,
  whenUnauthedAfterInit: AuthAction.RENDER,
})(CartPage);

I'm also using useAuthUser hook in a nested component, but I don't think it's related to this error.

Photo of error page from user:
20210316_153010

Simple way to protect an API route

Is your feature request related to a problem? Please describe.
Im currently investigating if I would use in an upcoming project firebase with NextJS. Is there a simple way to protect an API route? I've seen that you pass the Authentification token to fetch the API example but would it not be simpler to check the cookie on server side to validate if the API is protected? I was using for an Auth0 project nextjs-auth0 (https://github.com/auth0/nextjs-auth0#api-reference) which did a similar job just instead of Firebase it was Auth0

Describe the solution you'd like and how you'd implement it
Similar to protect SSR routes protecting API routes

Is this a breaking change?
I don't think so

Describe alternatives you've considered
Currently pass the token from client to server via headers but I think there could be a simpler approach

Provide better support for Google Cloud native environments

Is your feature request related to a problem? Please describe.
When you run on Google Cloud, you often can rely on the default application credentials. So there isn't a need to store the private key in an environment variable. Instead it would be preferred to load the firebase admin using this code:

var admin = require('firebase-admin');
var app = admin.initializeApp();

For reference see: https://firebase.google.com/docs/admin/setup

Describe the solution you'd like and how you'd implement it
Change the behaviour such that when no firebaseAdminInitConfig is specified it will initialize the app using the following code:

admin.initializeApp()

Is this a breaking change?
Would this require existing users to change how they're using next-firebase-auth?
It wouldn't be a breaking change because existing users that have the firebaseAdminConfig will continue to work as is

I will happily provide a PR for this change if this is something that you would accept.

InitConfig type does not include `debug: boolean` property

Describe the bug
In index.d.ts, the InitConfig type does not include the "debug" property, so you have to work around that to use the debug property in TypeScript.

Version
From my package.json:

    "cookies": "^0.8.0",
    "firebase": "^8.4.1",
    "firebase-admin": "^9.2.0",
    "next": "^10.0.0",
    "next-firebase-auth": "^0.13.1-alpha.0",
    "react": "17.0.1",
    "react-dom": "17.0.1",
    "react-firebaseui": "^5.0.2",

To Reproduce
Steps to reproduce the behavior:

  1. In a TypeScript project, import { init } from "next-firebase-auth".
  2. Define an object config to be passed as argument to init.
  3. Include a debug: true (or debug: false) property on config.
  4. Try to call init(config)

Expected behavior
Object with debug property can be passed to init.

Observed
TypeScript error: Argument of type '{ debug: boolean; ... }' is not assignable to parameter of type 'InitConfig'

Additional context
The debug option does seem to work correctly! To work around this problem, you can say const config: any to disable type checking and pass it to init, but I think we'd rather not have to.

Document mocking next-firebase-auth for test suites

Did you have difficulty using or understanding an aspect of next-firebase-auth?
The documentation does not currently cover examples of testing components/pages wrapped with next-firebase-auth. Conversation about this has been ongoing in discussion #124 for a couple of days. I think it would be helpful for other users to include some documentation about setting this up.

What documentation would you like to add or change?
I would like to see a new section in the documentation that describes how to mock the library, and how to use the mocks in tests.

Version
0.13.0-alpha0

Support setting Firebase private key only at runtime

Describe the bug
I'm using a Docker image to deploy my nextjs app however when I run yarn build it seems to expect the private key. This doesn't make sense for my scenario. The Firebase private key will be supplied at runtime as environment variable so it shouldn't be needed during yarn build.

See logs:

Step #0: �[91m
Step #0: > Build error occurred
Step #0: �[0m�[91mFirebaseAppError: Service account object must contain a string "private_key" property.
Step #0:     at FirebaseAppError.FirebaseError [as constructor] (/build/node_modules/firebase-admin/lib/utils/error.js:44:28)
Step #0:     at FirebaseAppError.PrefixedFirebaseError [as constructor] (/build/node_modules/firebase-admin/lib/utils/error.js:90:28)
Step #0:     at new FirebaseAppError (/build/node_modules/firebase-admin/lib/utils/error.js:125:28)
Step #0:     at new ServiceAccount (/build/node_modules/firebase-admin/lib/credential/credential-internal.js:134:19)
Step #0:     at new ServiceAccountCredential (/build/node_modules/firebase-admin/lib/credential/credential-internal.js:68:15)
Step #0:     at Object.exports.cert (/build/node_modules/firebase-admin/lib/credential/credential.js:34:54)
Step #0:     at /build/node_modules/next-firebase-auth/build/index.node.js:2:16259
Step #0:     at init (/build/node_modules/next-firebase-auth/build/index.node.js:2:16290)
Step #0:     at initAuth (/build/.next/server/pages/_app.js:2976:66)
Step #0:     at Module.1TCz (/build/.next/server/pages/_app.js:416:36)
Step #0:     at __webpack_require__ (/build/.next/server/pages/_app.js:23:31)
Step #0:     at Object.0 (/build/.next/server/pages/_app.js:116:18)
Step #0:     at __webpack_require__ (/build/.next/server/pages/_app.js:23:31)
Step #0:     at /build/.next/server/pages/_app.js:91:18
Step #0:     at Object.<anonymous> (/build/.next/server/pages/_app.js:94:10)
Step #0:     at Module._compile (node:internal/modules/cjs/loader:1108:14) {
Step #0:   errorInfo: {
Step #0:     code: 'app/invalid-credential',
Step #0:     message: 'Service account object must contain a string "private_key" property.'
Step #0:   },
Step #0:   codePrefix: 'app'
Step #0: }
Step #0: �[0m�[91merror Command failed with exit code 1.
Step #0: �[0minfo Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Step #0: The command '/bin/sh -c export COOKIE_SECRET_CURRENT=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) &&     export COOKIE_SECRET_PREVIOUS=$COOKIE_SECRET_CURRENT &&     yarn build &&     rm -rf .next/cache' returned a non-zero code: 1

Version
Latest, ^0.12.0

Expected result:
Ability to run yarn build without having to set the private key

I followed the example application.

AuthUser is not updating when using plain javascript for Auth.

Describe the bug
I'm not using Firebase UI, i'm using vanilla javascript to sign in user (via phone auth), so when the user is signed in, I can confirm it in firebase.auth().currentUser, but the useAuthUser() is not returning the user nor I can see a api request to login, as this is supposed to be done automatically when user's logs in.

confirmResult.confirm(otp.current.value).then((result) => { 
    console.log(firebase,auth().currentUser); //this shows me user is logged successfully
}

useAuthUser() returns this:

AuthUser 
{id: null, email: null, emailVerified: false, claims: {}, getIdToken: ƒ,}
claims: {}
clientInitialized: false
email: null
emailVerified: false
firebaseUser: null
getIdToken: ƒ ()
id: null
serialize: ƒ ()
signOut: ƒ ()
[[Prototype]]: Object

Version
0.13.1-alpha.0"

To Reproduce
Steps to reproduce the behavior:

  1. Follow the steps for vanilla javascript phone auth -
const AuthUser = useAuthUser()
  useEffect(() => {
    console.log("AuthUser", AuthUser);
    //not changed even when user is logged
  }, [AuthUser])

Expected behavior
Automatically update AuthUser when user is logged

Property "firebaseUser" on "AuthUser" is null even though the token is valid and returns a user.

Describe the bug

The bug occurred during the use of withAuthUserTokenSSR.

The property firebaseUser on the object AuthUser returns null even though the token is valid and returns a user.

Here's a log output of AuthUser.user:

{
  id: '*redacted*',
  email: '*redacted*',
  emailVerified: true,
  getIdToken: [Function (anonymous)],
  clientInitialized: false,
  firebaseUser: null,
  signOut: [Function (anonymous)],
  serialize: [Function: serialize]
}

Version
v0.12.2

To Reproduce/Context

A snippet is more pertinent.

... = withAuthUserTokenSSR()(async ({ AuthUser: user, ...ctx }) => {
    if (!user || !user.id) {
      const { resolvedUrl } = ctx;
      const path = resolvedUrl.match(/^(\/[a-z|\/]+)?/)[0];
      return {
        redirect: {
          statusCode: 301,
          destination:
            path && path != PATH_LOGOUT
              ? PATH_LOGIN + `?${QUERY_KEY_REDIRECT_TO}=${path.replace(/\//g, '%2F')}`
              : PATH_LOGIN,
        },
        props: {} as never,
      };
    }

    const token = await user.getIdToken();
>   const { claims } = await user.firebaseUser.getIdTokenResult();

    const result = await Promise.all([
      apiRequest<IOrganisation>({
        url: API_BASE_SERVICE_PATH_ORGANISATION,
        method: 'GET',
        token,
      }),
      apiRequest<IMember>({
        url: `${API_BASE_SERVICE_PATH_MEMBERS}/${claims.member_id}`,
        method: 'GET',
        token,
      }),
    ])

    return {
      props: {
        currentOrg: result[0].data,
        currentMember: result[1].data,
      }
    }
  });

Expected behavior
The property firebaseUser is present on the AuthUser object.

How to solve 'Failed to parse private key: Error: Invalid PEM formatted message.'

Hi there,

Thanks for creating this great plugin!

I have been attempting to setup my firebase configuration and have had quite some trouble. I've followed the example (initAuth.js & .env.local) as best as possible, however I run into an error upon authenticating through firebase.

I see this error client side: uncaught (in promise) Error: Received 500 response from login API endpoint: {"error":"Unexpected error."}

And server side I've logged out the error: error in login.js: FirebaseAppError: Failed to parse private key: Error: Invalid PEM formatted message.

I've also read through the issue thread that seems to be related but can't figure out exactly what I've done wrong. Any help would be greatly appreciated!

Thank you!

Invalid custom tokens while using Google authentification

Describe the bug

I would like to add Google authentication to the example you provided in the repository. Specifically, I modified signInOptions in example/components/FirebaseAuth.js as follows:

  signInOptions: [
    firebase.auth.GoogleAuthProvider.PROVIDER_ID,
  ],

Setting up my firebase config info, when I try to login I get the following error:

Error: Problem getting a refresh token: {"error":{"code":400,"message":"INVALID_CUSTOM_TOKEN","errors":[{"message":"INVALID_CUSTOM_TOKEN","domain":"global","reason":"invalid"}]}}

I checked the token in https://jwt.io/ and it also raised that the token is invalid.

Version

0.13.0-alpha.0

RFC: Cannot pass react.VFC typed component to withAuthUser argument

Describe the bug

Since withAuthUser argument type is react.ComponentType (ref:

}) => (component: ComponentType<P>) => ComponentType<P>
), react.VFC cannot be passed.

However, IDK which is better, to fix next-firebase-auth type definition or DefinetelyTyped react type definition.
If you have any suggestions, please let me know.

Version

0.12.2

To Reproduce

Pass react.VFC typed component to withAuthUser argument.

Expected behavior

Does not produce type error.

Additional context

(About react.Component)
react.ComponentType definition:
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L78
This lacks react.VoidFunctionalComponent
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L539-L555

(About react.VFC)
Currently, with react.FC typed component seems implicitly receiving children.
So React team recommends to use react.VFC (which we have to write children explicitly when we want), and from React v18 react.FC will be modified behaviorally equivalent to react.VFC.

Null Token Using withAuthUserTokenSSR

Describe the bug
Token is always null when using getServerSideProps and withAuthUserTokenSSr

Version
0.13.0-alpha.0

To Reproduce
I'm using the basic setup from the docs and example site, trying to load some api data in server side props.

const Post = ({ post }) => { 
 return  <NewEdit content={post.content} title={post.title} pid={pid} id={1} />
  }
export const getServerSideProps = withAuthUserTokenSSR({
    whenUnauthed: AuthAction.REDIRECT_TO_LOGIN,
})(async ({ AuthUser, params }) => {
    const { pid } = params
    console.log(pid)
    const token = await AuthUser.getIdToken()
    const response = await fetch(`http://localhost:3000/api/posts/${pid}`, {
        method: 'GET',
        headers: {
            Authorization: token || 'unauthenticated',
        },
    })
    const data = await response.json()
    return {
        props: {
            post: data
        }
    }
})
export default withAuthUser()(Post)

However, it always thinks I'm unautheticated, and redirects me to the login page (which then redirects me to my dashboard page because I'm already logged in). If I comment out whenUnauthed: AuthAction.REDIRECT_TO_LOGIN,, I can see that the token is always null. So however it gets authed, (not sure if it's accessing cookies or what) doesn't seem to trigger.

If I don't use server side props, I'm able to get the token just fine:

const Post = ({ }) => {
    const AuthUser = useAuthUser()
    const getData = async (url) => {
        const token = await AuthUser.getIdToken()
        let res
        await fetch(url, {
            method: 'GET',
            headers: {
                Authorization: token,
            }
        }).then(async (r) => {
            // console.log(await r.json())
            res = await r.json()
        })
        return res
    }

    const { data: post, error } = useSWR(`/api/posts/${pid}`, getData)
 return      <NewEdit content={post.content} title={post.title} pid={pid} id={1} />

        
export default withAuthUser({ whenUnauthedBeforeInit: AuthAction.SHOW_LOADER, whenUnauthedAfterInit: AuthAction.REDIRECT_TO_LOGIN, LoaderComponent: FullPageLoader, })(Post)

Any help on getting serversideprops working would be greatly appreciated, thanks!

Expected behavior
Using const token = await AuthUser.getIdToken() would give the current user token.

Support custom fetch logic for calling login/logout API endpoints

Is your feature request related to a problem? Please describe.
As noted in the limitations, there's currently no way to customize how the app calls the login/logout API endpoints to set/unset the auth cookies. It would be useful to set a custom header or use a custom fetcher.

Describe the solution you'd like and how you'd implement it
Add an optional config property (tokenChangedHandler) where the user can define a callback handler to call their endpoints themselves. If this property is set, call it rather than calling the API endpoints in next-firebase-auth. The user is responsible for setting the Authorization header to the token value and ensuring cookies can be set.

For example:

// ...config
tokenChangedHandler: async ({ token }) => {
  // Whatever custom logic the user needs.
  if (token) {
    await fetch('/api/login', {
      method: 'POST',
      headers: {
        Authorization: token,
        'X-My-Custom-Header': 'something'
      },
      credentials: 'include',
    })
  } else {
    await fetch('/api/logout', {
      method: 'POST',
      credentials: 'include',
    })
  }
}

In addition, if tokenChangedHandler is set, the loginAPIEndpoint and logoutAPIEndpoint config properties are no longer required.

Is this a breaking change?
No

Describe alternatives you've considered
We could ask for a custom fetcher function and call it from next-firebase-auth, but this seems unnecessarily restrictive.

Allow using withAuthUser with the _app.tsx

Not sure if that's a miss-pattern, but it looks reasonable to have the ability to only call withAuthUser once in the app, and make user available to all pages.

Basically, it works, but the typings not OK.

the code:

const MyApp = ({ Component, pageProps }) => {
  return (
    <AuthScreen>
      <Component {...pageProps} />
    </AuthScreen>
  )
}
export default withAuthUser()(MyApp)

the TS error:
image

Version 0.13.0 doesn't initiate firebase-admin.

Describe the bug
Version 0.12.2 works fine, next build finishes fine.
After upgrade to 0.13.0, I get error:

$next build
....
info  - Creating an optimized production build  
info  - Compiled successfully

Build error occurred
Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services.
    at FirebaseAppError.FirebaseError [as constructor] (/home/bot/trip-finder/node_modules/firebase-admin/lib/utils/error.js:44:28)
    at FirebaseAppError.PrefixedFirebaseError [as constructor] (/home/bot/trip-finder/node_modules/firebase-admin/lib/utils/error.js:90:28)
    at new FirebaseAppError (/home/bot/trip-finder/node_modules/firebase-admin/lib/utils/error.js:125:28)
    at FirebaseNamespaceInternals.app (/home/bot/trip-finder/node_modules/firebase-admin/lib/firebase-namespace.js:97:19)
    at FirebaseNamespace.app (/home/bot/trip-finder/node_modules/firebase-admin/lib/firebase-namespace.js:365:30)
    at FirebaseNamespace.ensureApp (/home/bot/trip-finder/node_modules/firebase-admin/lib/firebase-namespace.js:379:24)
    at FirebaseNamespace.fn (/home/bot/trip-finder/node_modules/firebase-admin/lib/firebase-namespace.js:239:30)
    at getLocationSlugs (/home/bot/trip-finder/.next/server/pages/location/[location].js:5324:87)
    at getStaticPaths (/home/bot/trip-finder/.next/server/pages/location/[location].js:7226:72)
    at buildStaticPaths (/home/bot/trip-finder/node_modules/next/dist/build/utils.js:16:86) {
  type: 'FirebaseAppError',
  errorInfo: {
    code: 'app/no-app',
    message: 'The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services.'
  },
  codePrefix: 'app'
}
error Command failed with exit code 1.

Version
0.12.2 - works fine
0.13.0 - fails

To Reproduce
$next build

Expected behavior

Additional context
My ininAuth.js file:

import { init } from 'next-firebase-auth';
import { HOME_PAGE, LOGIN_PAGE, API_LOGIN, API_LOGOUT } from 'settings/constant';

const TWELVE_DAYS_IN_MS = 12 * 60 * 60 * 24 * 1000;

const initAuth = () => {
  init({
    debug: false,
    authPageURL: LOGIN_PAGE,
    appPageURL: HOME_PAGE,
    loginAPIEndpoint: API_LOGIN,
    logoutAPIEndpoint: API_LOGOUT,
    firebaseAdminInitConfig: {
      credential: {
        projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
        clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
        // Using JSON to handle newline problems when storing the
        // key as a secret in Vercel. See:
        // https://github.com/vercel/vercel/issues/749#issuecomment-707515089
        privateKey: process.env.FIREBASE_PRIVATE_KEY ? JSON.parse(process.env.FIREBASE_PRIVATE_KEY) : undefined,
      },
      databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
    },
    firebaseClientInitConfig: {
      apiKey: process.env.NEXT_PUBLIC_FIREBASE_PUBLIC_API_KEY,
      authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
      projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
      storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
      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,
    },
    cookies: {
      name: 'authorization',
      keys: [process.env.COOKIE_SECRET_CURRENT, process.env.COOKIE_SECRET_PREVIOUS],
      httpOnly: true,
      maxAge: TWELVE_DAYS_IN_MS,
      overwrite: true,
      path: HOME_PAGE,
      sameSite: 'strict',
      secure: process.env.NEXT_PUBLIC_COOKIE_SECURE === 'true',
      signed: true,
    },
  });
};

export default initAuth;

TypeError: e.getIdToken is not a function

Describe the bug
Type error when launching next-firebase-auth linked to getIdToken function.

Version

  "firebase": "^8.3.2",
   "firebase-admin": "^9.6.0",
   "next": "^10.1.2",
   "next-firebase-auth": "^0.13.0-alpha.3",
   "react": "^17.0.2",
   "react-dom": "^17.0.2",

To Reproduce
Steps to reproduce the behavior:

  1. Wrap a static landing page with withAuthUser
    export default withAuthUser()(LandingPage);

  2. Launch
    yarn dev

  3. The error is linked to createAuthUser

Uncaught (in promise) TypeError: e.getIdToken is not a function
    at Object.eval (index.browser.js?3144:1)
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js?c973:3)
    at _next (asyncToGenerator.js?c973:25)
    at eval (asyncToGenerator.js?c973:32)
    at new Promise (<anonymous>)
    at Object.eval (asyncToGenerator.js?c973:21)
    at Object.eval [as getIdToken] (index.browser.js?3144:1)
    at eval (index.browser.js?3144:1)
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js?c973:3)
    at _next (asyncToGenerator.js?c973:25)
    at eval (asyncToGenerator.js?c973:32)
    at new Promise (<anonymous>)
    at eval (asyncToGenerator.js?c973:21)
    at eval (index.browser.js?3144:1)
    at eval (index.browser.js?3144:1)
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js?c973:3)
    at _next (asyncToGenerator.js?c973:25)
    at eval (asyncToGenerator.js?c973:32)
    at new Promise (<anonymous>)
    at eval (asyncToGenerator.js?c973:21)
    at eval (index.browser.js?3144:1)
    at Object.eval (index.browser.js?3144:1)
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js?c973:3)
    at _next (asyncToGenerator.js?c973:25)

Expected behavior
Understand where the error comes from and correct it

Additional context
Add any other context about the problem here.

Thank you for your help!

Support Modular Firebase 9

Is your feature request related to a problem? Please describe.
Firebase bundle size is huge. Version 9 introduces a new import strategy that significantly reduces bundle size.

Describe the solution you'd like and how you'd implement it
Ideally a beta or "@next" version of the package that opts into the 'modular' (and not the 'compat' bridge) version 9 loading strategy.

Is this a breaking change?
Would this require existing users to change how they're using next-firebase-auth?

Likely. Functions are now modular which means you need to pass the app instance into the function.

Support dynamic redirect destinations

Is your feature request related to a problem? Please describe.
As mentioned in the limitations, there isn't built-in support for custom redirect destinations based on the user's auth status. This limits the usefulness of the redirects.

Describe the solution you'd like and how you'd implement it
Allow users to provide functions for the authPageURL and appPageURL config properties that should return the appropriate URL. We could pass either ctx or window to the function, depending on the context.

// ctx would be defined when server-side redirecting, window for client-side
({ ctx, window }) => {
  // some logic
  return '/my-auth-page?next=/my-app/'
}

Is this a breaking change?
No. We would still support static values for the config options.

Describe alternatives you've considered
None

Add documentation and example for dynamic redirects

Document feature #121:

  • Update the README to include info on using functions to resolve redirect URLs.
  • Remove the existing redirect example in the example app (see this comment). Users have found this example confusing, because it redirects unexpectedly when you're logged in.
  • In the example, add dynamic redirects on the auth page and one or two of the example pages.

Unable to trigger /api/login with firebase.auth().createUserWithEmailAndPassword() or firebase.auth().signInWithEmailAndPassword()

Describe the bug
Unable to trigger /api/login with firebase.auth().createUserWithEmailAndPassword() or firebase.auth().signInWithEmailAndPassword()

Version
0.12.0

To Reproduce
Using vanilla setup, very similar to example. Only difference is I'm not using Firebase UI. Not sure if that is required for this package.

When either of these firebase auth methods are triggered, I don't see /api/login being hit. Later on when trying to access AuthUser, all entries are null.

I'm using a custom form that is in /components/login.js, which triggers the firebase methods. Also tried wrapping the login component in withAuthUser, but that didn't help.

Expected behavior
For /api/login to be triggered when user logs in using email + password

Additional context
Logout works successfully and /api/logout gets triggered when firebase.auth().signOut(); is triggered

Persistence cookies with Auth Emulator in Development

Describe the bug

In development using the Auth Emulator both after creating the user auth.createUserWithEmailAndPassword() or attempting to login an existing user auth.signInWithEmailAndPassword() by refreshing the page that should be accessible only with logged users (protected route, eg /profile), the logout endpoint is always called, clearing the cookie and not persisting the logged in user.

ps. The described bug does not happen in production (using firebase hosting and cloud functions), in production everything works perfectly!!!

Version

0.13.1-alpha.1

To Reproduce

  1. User tries to access the protected route (eg: /profile);
  2. As he is not logged in yet, he is redirected to the login page (eg: /profile/login);
  3. User logs in (or registers), cookies are created in the browser and is redirected to the protected route (eg: /profile);
  4. So far everything works as expected, but if the user just refreshes the browser on this protected route, the cookies are automatically cleared and the user is redirected back to the login page, as if the user is no longer authenticated 😩

Expected behavior

That the authenticated user persists after the login or registration process to continue navigating the protected routes.

Additional context

Here are all the settings for the files in the project: (sorry if it got too long, just wanted to make sure that all relevant files were in scope 😅 )

// utils/auth.js

import { init } from 'next-firebase-auth';

const IS_DEV = process.env.NODE_ENV === 'development';
const TWELVE_DAYS_IN_MS = 12 * 60 * 60 * 24 * 1000;

const initAuth = () => {

  let config = {
    authPageURL: '/profile/login',
    appPageURL: '/profile',
    loginAPIEndpoint: '/api/login',
    logoutAPIEndpoint: '/api/logout',
    firebaseAdminInitConfig: {
      // ...same of the README
    },
    firebaseClientInitConfig: {
      // ...same of the README
    },
    cookies: {
      name: 'ExampleApp',
      keys: [
        process.env.COOKIE_SECRET_CURRENT,
        process.env.COOKIE_SECRET_PREVIOUS
      ],
      httpOnly: true,
      maxAge: TWELVE_DAYS_IN_MS,
      overwrite: true,
      path: '/',
      sameSite: 'strict',
      secure: !IS_DEV,
      signed: true,
    }
  }

  if (IS_DEV) {
    config = {
      ...config,
      debug: true,
      firebaseAuthEmulatorHost: process.env.FIREBASE_AUTH_EMULATOR_HOST,
    }
  }

  init(config)

}

export default initAuth

--

// ./_app.js

import initAuth from 'utils/auth';
initAuth()

  // ... rest of code 

export default App;

--

// pages/api/login.js

import { setAuthCookies } from 'next-firebase-auth'
import initAuth  from 'utils/auth';

initAuth()

const handler = async (req, res) => {
  try {
    await setAuthCookies(req, res)
  } catch (e) {
    console.error(e)
    return res.status(500).json({ error: 'Unexpected error.' })
  }
  return res.status(200).json({ status: true })
}

export default handler

--

// pages/api/logout.js

import { unsetAuthCookies } from 'next-firebase-auth'
import initAuth from 'utils/auth';

initAuth()

const handler = async (req, res) => {
  try {
    await unsetAuthCookies(req, res)
  } catch (e) {
    console.error(e)
    return res.status(500).json({ error: 'Unexpected error.' })
  }
  return res.status(200).json({ status: true })
}

export default handler

--

// pages/profile/index.js

import {
  getFirebaseClient,
  AuthAction,
  withAuthUser
} from 'next-firebase-auth';

import 'firebase/auth';

const IS_DEV = process.env.NODE_ENV === 'development';
const MyLoader = () => <div>Loading...</div>

const Profile = () => {

   let auth = getFirebaseClient().auth();
   if (IS_DEV) auth.useEmulator('http://localhost:9099');
  
  const logout = () => {
    const user = auth.currentUser;
    if (user) auth.signOut()
  }

  return (
    <div>
      <h1>Profile</h1>
      <button onClick={logout}>Logout</button>
    </div>
  )

}

export default withAuthUser({
  whenAuthed: AuthAction.RENDER,
  whenUnauthedBeforeInit: AuthAction.SHOW_LOADER,
  whenUnauthedAfterInit: AuthAction.REDIRECT_TO_LOGIN,
  LoaderComponent: MyLoader,
})(Profile);

--

// pages/profile/login.js

import { 
  getFirebaseClient, 
  AuthAction, 
  withAuthUser 
} from 'next-firebase-auth';

import 'firebase/auth';

const IS_DEV = process.env.NODE_ENV === 'development';
const MyLoader = () => <div>Loading...</div>

const Login = () => {

  const emailRef_signin = useRef(null);
  const passwordRef_signin = useRef(null);
  const emailRef_signup = useRef(null);
  const passwordRef_signup = useRef(null);

  let auth = getFirebaseClient().auth();
  if (IS_DEV) auth.useEmulator('http://localhost:9099');

  const submitForm_signin = () => {
    auth.signInWithEmailAndPassword(emailRef_signin, passwordRef_signin).then((userCredential) => {
      console.log('userCredential', userCredential.user)
    }).catch((error) => console.log('userCredential error', error));
  }

  const submitForm_signup = () => {
    auth.createUserWithEmailAndPassword(emailRef_signup, passwordRef_signup).then((userCredential) => {
      console.log('userCredential', userCredential.user)
    }).catch((error) => console.log('userCredential error', error));
  }

  const logout = () => {
    const user = auth.currentUser
    if (user) auth.signOut()
  }

  return (
    <>
      <div>
        <h1>Sign In</h1>
        <form onSubmit={(e) => e.preventDefault()}>
          <input ref={emailRef_signin} type="text" placeholder="E-mail" />
          <input ref={passwordRef_signin} type="password" placeholder="Password" />
          <button onClick={submitForm_signin}>Sign In</button>
          <button onClick={logout}>Logout</button>
        </form>
      </div>
      <div>
        <h1>Sign Up</h1>
        <form onSubmit={(e) => e.preventDefault()}>
          <input ref={emailRef_signup} type="text" placeholder="E-mail" />
          <input ref={passwordRef_signup} type="password" placeholder="Password" />
          <button onClick={submitForm_signup}>Sign Up</button>
        </form>
      </div>
    </>
  )

}

export default withAuthUser({
  whenAuthed: AuthAction.REDIRECT_TO_APP,
  whenUnauthedBeforeInit: AuthAction.SHOW_LOADER,
  whenUnauthedAfterInit: AuthAction.RENDER,
  LoaderComponent: MyLoader,
})(Login)

--

Am I doing something wrong, or have I lost something in the settings? I really researched the issues a lot before creating this bug and tested a few different approaches without much success 😢

I appreciate any help or insights from what I may be setting up incorrectly o/

Private Config variables are available on client side

Hi,
In my project I created all files same as the example project. However, I am getting "Error: Invalid next-firebase-auth options: The "firebaseAdminInitConfig" private key setting should not be available on the client side. The "cookies.keys" setting should not be available on the client side."

I suppose webpack is adding initConfig to client bundle. Is there a way to prevent this?

Firebase client does not initialized.

Describe the bug
initAuth() does not initialize client-side firebase SDK.

Version
0.13.0-alpha.0

To Reproduce
I have initAuth() in my _app.tsx.
This is the content of my initAuth.ts

import { init } from 'next-firebase-auth';

const TWELVE_DAYS_IN_MS = 12 * 60 * 60 * 24 * 1000;

const initAuth = () => {
  init({
    authPageURL: '/login',
    appPageURL: '/',
    loginAPIEndpoint: '/api/login',
    logoutAPIEndpoint: '/api/logout',
    firebaseAdminInitConfig: {
      credential: {
        projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || '',
        clientEmail: process.env.FIREBASE_CLIENT_EMAIL || '',
        // Using JSON to handle newline problems when storing the
        // key as a secret in Vercel. See:
        // https://github.com/vercel/vercel/issues/749#issuecomment-707515089
        privateKey: process.env.FIREBASE_PRIVATE_KEY
          ? JSON.parse(process.env.FIREBASE_PRIVATE_KEY)
          : undefined,
      },
      databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL || '',
    },
    firebaseClientInitConfig: {
      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,
    },
    cookies: {
      name: 'casino',
      keys: [
        process.env.COOKIE_SECRET_CURRENT,
        process.env.COOKIE_SECRET_PREVIOUS,
      ],
      httpOnly: true,
      maxAge: TWELVE_DAYS_IN_MS,
      overwrite: true,
      path: '/',
      sameSite: 'strict',
      secure: process.env.NEXT_PUBLIC_COOKIE_SECURE === 'true',
      signed: true,
    },
  });
};

export default initAuth;
export const getServerSideProps = withAuthUserSSR()(async (ctx) => {
  const { AuthUser } = ctx;

  // not authenticated
  if (!AuthUser.id) {
    return {
      redirect: {
        destination: '/login',
        permanent: false,
      },
    };
  }

  // staff
  if (AuthUser.id && !AuthUser.firebaseUser?.phoneNumber) {
    return {
      redirect: {
        destination: '/staff',
        permanent: false,
      },
    };
  }

  return {
    props: {},
  };
});

Expected behavior
I expect the client-side firebase SDK to be available, so I can use it in my AuthUser.

Typescript definitions are not included in the distributed package

Describe the bug
Typescript definitions are not included in the built package.

Version
0.12.1

To Reproduce
Steps to reproduce the behavior:

  1. yarn run build
  2. Inspect the build directory

Or, check the distributed package contents.

Expected behavior
index.d.ts should be included in the built/distributed package.

Additional context
See #77

Use Emulator

Is your feature request related to a problem? Please describe.
When you have local/staging/prod environments, it gets quite tricky to test your changes (specially if you have Cloud Functions that handles the user creation (functions.auth.user().onCreate()).

Describe the solution you'd like and how you'd implement it
Currently react-firebaseui supports something like this:

        <StyledFirebaseAuth
          uiConfig={firebaseAuthConfig}
          firebaseAuth={
            process.env.NODE_ENV !== "production"
              ? firebase.auth().useEmulator("http://localhost:9099/")
              : firebase.auth()
          }
        />

But when I tried to use it, it breaks somewhere else:
image

Is this a breaking change?
I don't think so

Describe alternatives you've considered
I have no idea if there's any alternative

Frontend compilation fails with child_process module not found

Describe the bug
I've followed the config examples in the repo but the client JS fails to compile with the following error:

error - ./node_modules/google-auth-library/build/src/auth/googleauth.js:17:0
Module not found: Can't resolve 'child_process'

My init auth file (note that I do not supply server side admin config, I initialize admin SDK outside of this file before importing this):

import { init } from 'next-firebase-auth';
import { COOKIE_SECRET } from './config';

export default function initAuth(): void {
  init({
    authPageURL: '/auth',
    appPageURL: '/',
    loginAPIEndpoint: '/api/auth/login',
    logoutAPIEndpoint: '/api/auth/logout',

    firebaseClientInitConfig: {
      apiKey: 'MyExampleAppAPIKey123', // required
      authDomain: 'my-example-app.firebaseapp.com',
      databaseURL: 'https://my-example-app.firebaseio.com',
      projectId: 'my-example-app-id',
    },

    cookies: {
      name: 'auth-data', // required
      // Keys are required unless you set `signed` to `false`.
      // The keys cannot be accessible on the client side.
      keys: [COOKIE_SECRET],
      httpOnly: true,
      maxAge: 12 * 60 * 60 * 24 * 1000, // twelve days
      overwrite: true,
      path: '/',
      sameSite: 'strict',
      secure: process.env.NODE_ENV === 'production',
      signed: true,
    },
  });
}

My _app.tsx file:

import App from 'next/app';

import { NotificationsProvider } from 'features/notifications/context';
import initAuth from '../lib/auth';

import './fonts.css';
import './global.css';

initAuth();

class MyApp extends App {
  componentDidMount(): void {
    const style = document.getElementById('server-side-styles');

    if (style) {
      style.parentNode?.removeChild(style);
    }
  }

  render(): JSX.Element {
    const { Component, pageProps } = this.props;
    return (
      <NotificationsProvider>
        <Component {...pageProps} />
      </NotificationsProvider>
    );
  }
}


export default MyApp;

If I remove the initAuth import and method call then compilation fails but I'm not able to render pages that require auth, I get an error message saying client side auth needs to be configured.

Looks like this is also referenced here. From what I can tell something in the dependency tree of firebase auth is pulling in a server side auth module which imports child_process. I've also tried supplying the firebaseAdminInitConfig field but the same error occurs.

Version

All dependency versions from package json:

    "axios": "^0.21.1",
    "babel-plugin-superjson-next": "^0.1.9",
    "blurhash": "^1.1.3",
    "classnames": "^2.2.6",
    "email-validator": "^2.0.4",
    "firebase": "^8.3.1",
    "firebase-admin": "^9.5.0",
    "firebase-functions": "^3.13.2",
    "jss": "^10.5.1",
    "lodash": "^4.17.20",
    "mixpanel-browser": "^2.40.1",
    "next": "10.0.9",
    "next-firebase-auth": "^0.13.0-alpha.0",
    "next-i18next": "^7.0.1",
    "pretty-ms": "^7.0.1",
    "react": "^16.12.0",
    "react-blurhash": "^0.1.3",
    "react-dom": "^16.12.0",
    "react-jss": "^10.5.1",
    "superjson": "^1.4.1"

How to reduce bundle size?

Thanks to your awesome package, I could finally get all of my firebase-auth-related use-cases to work in Next. However, I noticed the bundle size (aka next build's "First Load JS shared by all") has doubled from ~64k to ~133k just by following the tutorial.

Is this common/expected? Can you point to any obvious mistakes one can make?

Feature Request: Offer Auth Provider

Is your feature request related to a problem? Please describe.
I'm looking at adding next-firebase-auth to a site I'm working on, but it has a persistent nav which shows the login status.

Unless I'm missing something, I would have to wrap every single page with the withAuthUser() HOC, as well as call the useAuthUser function in each of those pages. I would also need to move my Layout component out of my _app and into every page, meaning even more duplication on every page.

Describe the solution you'd like and how you'd implement it
A React Context Provider could be exposed so that developer can wrap their _app component with this allowing them to call the useAuthUser hook from any nested component.

Is this a breaking change?
I'm not sure if this would be a breaking change, I'm pretty sure this could be achieved without breaking the useAuthUser() API, but I haven't tried so I'm not sure.

Describe alternatives you've considered
Since this functionality does appear to exist, the only way I can think to achieve this is to not use next-firebase-auth at all, and instead write the functionality myself.

Thanks for your consideration.

Firebase admin incorrectly uses an environment variable for the Firebase API key

firebaseAdmin.js refers to process.env.NEXT_PUBLIC_FIREBASE_PUBLIC_API_KEY. Instead, it should refer to the public Firebase API key from the user's config.

Note that this is a breaking change, because it means the config.firebaseClientInitConfig.apiKey field is mandatory even if the user is initializing the Firebase JS SDK themselves.

Typescript type definition has required fields marked as optional.

Describe the bug
The type definition for the interface InitConfig indicates that both loginAPIEndpoint and logoutAPIEndpoint are optional.

Version
What version of next-firebase-auth are you using?

0.13.0-alpha.0

To Reproduce
Steps to reproduce the behavior:

  1. yarn add next-firebase-auth
  2. Pass the init function a configuration object that is missing the keys in question.
  3. Run the application.
  4. Observe the error (and application failure) in console.

Expected behavior
When using typescript it is expected that the language will alert the developer that a required key is missing.

Type '{ authPageURL: string; appPageURL: string; firebaseAdminInitConfig: 
{ credential: { projectId: string; clientEmail: string; privateKey: string; }; databaseURL: string; }; 
firebaseClientInitConfig: { ...; };cookies: { ...; }; }'
is missing the following properties from type 'InitConfig': loginAPIEndpoint, logoutAPIEndpointts(2345)

Additional context
Super easy fix, I can create a PR if needed.

Add examples how to use the other parts of firebase (e.g. firestore) with this package / Best practice

Is your feature request related to a problem? Please describe.
No problem, it's just a few improvements for the docs

Describe the solution you'd like and how you'd implement it
So I used the package the first time yesterday. Everything works perfectly.Nice library good job guys :) I was just missing 1 or 2 examples of how to use firebase parts inside this package. For example how to add a logged in user into the firestore database. Like best practice examples where it's best to do this.

Right now when a user loggs in I added him to the database like so:

import React, { useEffect } from "react";
import {
  useAuthUser,
  withAuthUser,
  withAuthUserTokenSSR,
} from "next-firebase-auth";
import firebase from "firebase";

const Demo = () => {
  const AuthUser = useAuthUser();

  useEffect(() => {
    console.log(AuthUser);
  });

  const login = () => {
    var provider = new firebase.auth.FacebookAuthProvider();
    firebase
      .auth()
      .signInWithPopup(provider)
      .then((result) => {
        console.log(result);
        firebase.firestore().collection("users").doc(result.user.email).set({
          email: result.user.email,
          displayName: result.user.displayName,
        });
      });
  };

  const logout = () => {
    firebase.auth().signOut();
  };

  return (
    <div>
      <p>Your email is {AuthUser.email ? AuthUser.email : "unknown"}.</p>

      <button onClick={() => login()}>Login</button>
      <button onClick={() => logout()}>Logout</button>
    </div>
  );
};

// Note that this is a higher-order function.
export const getServerSideProps = withAuthUserTokenSSR()();

export default withAuthUser()(Demo);

Is this a good way to handle this scenario? Or should this be handled on the server side in an api route?

Update firebaseClientInitConfig TypeScript definition to support all Firebase JS SDK config properties

Describe the bug
I am getting typescript error, I cannot add
storageBucket, messagingSenderId, and appId to the firebaseClientInitConfig?
If I want to use FirebaseClient SDK I must pass the same object I passed in my app, but initAuth doesn't let me add these params.

image

Version
0.13.0-alpha.0

Expected behavior
Allow to pass these params too, also I'm getting process.env.NEXT_PUBLIC_FIREBASE_API_KEY can be undefined error.

More documentation on custom claims

Is your feature request related to a problem? Please describe.
This project is now the officially linked project from the NextJS docs for Firebase auth (congratulations!). The README page of this project tells us

Supporting custom claims: Currently, the AuthUser object does not include any Firebase custom claims.

Now I have already spent a bunch of time getting this package to work, and saw this too late. Now I'm considering a re-write without this package because I need custom claims in order to check what a user should have access too (this is what I have interpreted custom claims to be for).

I am also what you would call a 'noob'-programmer, and I suspect that you actually can use custom claims with this package. You can just not get the results through AuthUser

Describe the solution you'd like and how you'd implement it
Implement an example where where firebase custom claims are used, and an example where they are set (if possible). Through the API or any other method.

Describe alternatives you've considered
firebase NPM package, how ever this has pretty much no documentation on how to handle things server side or how to use the package with NextJS.

Private key setting in firebaseAdminInitConfig

Hi!

First of all, thank you for efforts and neat readable docs and example.

I'm getting Error: Invalid next-firebase-auth options: The "firebaseAdminInitConfig" private key setting should not be available on the client side. error during initialization which is being called in my pages/_app/index.tsx file. The error is being raised in browser console. The private key is being set using environment variables.

I've ran the example app and don't face same error there. I am building my app using NextJS boilerplate. I am new to NextJS.

Is there something wrong I am doing? Thanks in advance.

How do I setup proper user permissions/access?

I've setup the example site on my local machine, and right now, anyone can login into my app (whether they have an account or not). How can I setup criteria for onboarding new users (maybe they have to have a specific email url, or they can submit their signup info that I then approve through the firebase console, or an admin account on the site)?

Some context: I'm trying to build a dashboard for organizations, so I only want people within the organization to have access to their respective data. Would love to have tiers setup for dif users. Appreciate all recommendations and resources

withAuthUser() HOC prevents Fast Refreshing / HMR

Description

When editing a Component that is wrapped with withAuthUser({}), React performs full-page refreshes; for example:

export default withAuthUser({
  whenAuthed: AuthAction.REDIRECT_TO_APP,
  whenUnauthedBeforeInit: AuthAction.RETURN_NULL,
  whenUnauthedAfterInit: AuthAction.RENDER,
})(Auth)

Version

Using the example's version: "0.13.0-alpha.4"

To Reproduce

  1. Clone the gladly-team/next-firebase-auth repo as is
  2. yarn dev in the examples directory
  3. Navigate to auth.js, doesn't matter if you're signed in or not.
  4. Make a trivial change (it doesn't actually even need to cause the DOM to get re-rendered).
  5. Observe that the entire page refreshes.

Additional context

The warning in the console:

[Fast Refresh] performing full reload

Fast Refresh will perform a full reload when you edit a file that's imported by modules outside of the React rendering tree.
You might have a file which exports a React component but also exports a value that is imported by a non-React component file.
Consider migrating the non-React component export to a separate file and importing it into both files.

It is also possible the parent component of the component you edited is a class component, which disables Fast Refresh.
Fast Refresh requires at least one parent function component in your React tree.

Expected behavior

  • HMR works :)

If this is unavoidable without additional configuration, it'd be awesome to capture the necessary steps somewhere in the example or in the doc. And if it's totally unavoidable, it might be a good FAQ note :)

Adding custom claims example

How can I assign firebase roles to a user? I didn't find enough documentation.
Can I have I can an idea how it's done? It's urgent.
Version
What version of next-firebase-auth are you using?
0.13.0-alpha.4

RFC: making it easier to use the Firebase apps

Developers often need to access the Firebase JS app or admin app to use features beyond auth. Currently, our recommendation is to initialize the Firebase apps manually before initializing next-firebase-auth (see this comment for one example). However, this recommendation has proven confusing: #61, #110, #113

I'd love to find a way to support this use case without adding complexity to this package or extraneous props to higher-order functions. Recommendations are welcome here.

Throw if useAuthUser is used without withAuthUser

Is your feature request related to a problem? Please describe.
If a developer accidentally uses useAuthUser without withAuthUser, it won't work: useAuthUser will always return an unauthed user. This is confusing: #150

Describe the solution you'd like and how you'd implement it
Throw an error if useAuthUser is called without withAuthUser above it in the component tree.

Implementation: don't set a default AuthUser value when defining the context, and in the useAuthUser function, throw if the value is undefined.

Is this a breaking change?
Yes, somewhat. If a developer is using this incorrectly, upgrading would throw a new error.

Describe alternatives you've considered

  • Leave as-is, though I don't see the value of useAuthUser without a context provider
  • Warn rather than throw

Rendering Another Component when the user is already logged in?

Hi, I am really enjoying this package, But I'm little stuck, I have made a Register page, I'm wondering if it's possible to render another component like a Modal that say's "Note, you are already logged in, you may continue to dashboard or logout" maybe.

I have tried,

export default withAuthUser({
  whenAuthed: AuthAction.SHOW_LOADER,
  LoaderComponent: alreadyLoggedModal,
})(register);

that using SHOW_LOADER instead to show the modal component as a loader, but this doesn't seem to work, maybe because I'm doing the wrong way.
Just wondering if there's any other way I could achieve this. I have read the documentation, but didn't find any another other way to render a component for whenAuthed. (except loaderComponent)

Here's my full code:

import React from "react";
import { Col, Row, Container, ModalDialog, Modal } from "react-bootstrap";
import { PersonCheckFill, CloudArrowUpFill } from "react-bootstrap-icons";
import FirebaseAuth from "../components/elements/FirebaseAuth";
import { AuthAction, useAuthUser, withAuthUser } from "next-firebase-auth";

const alreadyLoggedModal = () => {
  const AuthUser = useAuthUser();
  return (
    <Modal show backdrop="static" keyboard={false}>
      <Modal.Header closeButton>
        <Modal.Title>You are already Logged In!</Modal.Title>
      </Modal.Header>
      <Modal.Body>
        <strong>Note:</strong> You already logged in with{" "}
        {AuthUser.email || AuthUser.firebaseUser?.phoneNumber}. To Register or
        Login with new account, you must first Logout. If you want to continue
        with you current account, click on Dashboard to view your Account.{" "}
      </Modal.Body>
      <Modal.Footer>
        <Button variant="info" onClick={AuthUser.signOut}>
          Logout
        </Button>

        <Link href="/dashboard">
          <Button variant="success">Dashboard</Button>
        </Link>
      </Modal.Footer>
    </Modal>
  );
};
const register = () => {
  return (
    <div>
      <Row
        style={{ height: "90vh" }}
        className="d-flex align-items-md-center justify-content-center"
      >
        <Col md={4} className="p-5 text-center">
          <h1> Register as a Host</h1>
          <hr />
          <p className="lead">
            <CloudArrowUpFill /> Host you own Virtaul Queues and Allow people to
            get token for them.
          </p>
        </Col>
        <Col md={4} className="border shadow rounded-3 p-3">
          <h3>
            <PersonCheckFill /> Get Started
          </h3>
          <p>
            Setup your queue within few seconds. Choose one of the following
            methods:{" "}
          </p>
          <hr />
          <FirebaseAuth />
          <hr />
          <p>
            <strong>Note: </strong>Kindly choose only one of the above methods
            and you must use the same method everytime you login.
          </p>
        </Col>
      </Row>
    </div>
  );
};

export default withAuthUser({
  whenAuthed: AuthAction.SHOW_LOADER,
  LoaderComponent: alreadyLoggedModal,
})(register);```

Error message when logging in on example project

Describe the bug
Error Message displayed when trying to login new user with the /example project
Version
^0.13.0-alpha.0 (also tried 0.12.1)

To Reproduce
Steps to reproduce the behavior:

  1. Clone project and run the example folder project (using an existing firebase project and keys)
  2. Open localhost:3000
  3. click login in header
  4. Register new user by filling out email address and password
  5. Signout of new account
  6. Try and login with new account, you get the following error message -

Error: Problem getting a refresh token: {"error":{"code":400,"message":"INVALID_CUSTOM_TOKEN","errors":[{"message":"INVALID_CUSTOM_TOKEN","domain":"global","reason":"invalid"}]}}
at /Users/davidcarter/work/next-firebase-auth/example/node_modules/next-firebase-auth/build/index.node.js:2:10542
at Generator.next ()
at asyncGeneratorStep (/Users/davidcarter/work/next-firebase-auth/example/node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24)
at _next (/Users/davidcarter/work/next-firebase-auth/example/node_modules/@babel/runtime/helpers/asyncToGenerator.js:25:9)
at runMicrotasks ()
at processTicksAndRejections (internal/process/task_queues.js:97:5)

Expected behavior
I expect the user to login without an error.

Additional context
tried on Brave and Chrome

Composed props SSR should merge at the top level and within the props key

Currently, we merge composed server-side props into the props key (see source). Instead, we should merge at the top level as well as within the props key.

This is a breaking change. Composed functions should return { props: { ... }} instead of just { ... }.

We should also document that the AuthUserSerialized prop will take precedence over any merged props.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.