rsnext/examples/with-firebase-authentication/utils/pageWrappers/withAuthUser.js
2020-05-18 17:44:18 -04:00

99 lines
3.5 KiB
JavaScript

/* eslint react/jsx-props-no-spreading: 0 */
import React from 'react'
import PropTypes from 'prop-types'
import { get, set } from 'lodash/object'
import { AuthUserInfoContext, useFirebaseAuth } from '../auth/hooks'
import { createAuthUser, createAuthUserInfo } from '../auth/user'
// Gets the authenticated user from the Firebase JS SDK, when client-side,
// or from the request object, when server-side. Add the AuthUserInfo to
// context.
export default function withAuthUser(ComposedComponent) {
const WithAuthUserComp = (props) => {
const { AuthUserInfo, ...otherProps } = props
// We'll use the authed user from client-side auth (Firebase JS SDK)
// when available. On the server side, we'll use the authed user from
// the session. This allows us to server-render while also using Firebase's
// client-side auth functionality.
const { user: firebaseUser } = useFirebaseAuth()
const AuthUserFromClient = createAuthUser(firebaseUser)
const { AuthUser: AuthUserFromSession, token } = AuthUserInfo
const AuthUser = AuthUserFromClient || AuthUserFromSession || null
return (
<AuthUserInfoContext.Provider value={{ AuthUser, token }}>
<ComposedComponent {...otherProps} />
</AuthUserInfoContext.Provider>
)
}
WithAuthUserComp.getInitialProps = async (ctx) => {
const { req, res } = ctx
// Get the AuthUserInfo object.
let AuthUserInfo
if (typeof window === 'undefined') {
// If server-side, get AuthUserInfo from the session in the request.
// Don't include server middleware in the client JS bundle. See:
// https://arunoda.me/blog/ssr-and-server-only-modules
const { addSession } = require('../middleware/cookieSession')
addSession(req, res)
AuthUserInfo = createAuthUserInfo({
firebaseUser: get(req, 'session.decodedToken', null),
token: get(req, 'session.token', null),
})
} else {
// If client-side, get AuthUserInfo from stored data. We store it
// in _document.js. See:
// https://github.com/zeit/next.js/issues/2252#issuecomment-353992669
try {
const jsonData = JSON.parse(
window.document.getElementById('__MY_AUTH_USER_INFO').textContent
)
if (jsonData) {
AuthUserInfo = jsonData
} else {
// Use the default (unauthed) user info if there's no data.
AuthUserInfo = createAuthUserInfo()
}
} catch (e) {
// If there's some error, use the default (unauthed) user info.
AuthUserInfo = createAuthUserInfo()
}
}
// Explicitly add the user to a custom prop in the getInitialProps
// context for ease of use in child components.
set(ctx, 'myCustomData.AuthUserInfo', AuthUserInfo)
// Evaluate the composed component's getInitialProps().
let composedInitialProps = {}
if (ComposedComponent.getInitialProps) {
composedInitialProps = await ComposedComponent.getInitialProps(ctx)
}
return {
...composedInitialProps,
AuthUserInfo,
}
}
WithAuthUserComp.displayName = `WithAuthUser(${ComposedComponent.displayName})`
WithAuthUserComp.propTypes = {
AuthUserInfo: PropTypes.shape({
AuthUser: PropTypes.shape({
id: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
emailVerified: PropTypes.bool.isRequired,
}),
token: PropTypes.string,
}).isRequired,
}
WithAuthUserComp.defaultProps = {}
return WithAuthUserComp
}