-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Example with getSession() in _app.js? #345
Comments
I agree, I think this a thing it would be helpful to have a tutorial for. This is tricky as the Provider isn't initialised yet so doesn't help if you want to call There is an undocumented work around, where you can export and call import { Provider } from 'react-redux'
import { setOptions, getSession, Provider as AuthProvider } from 'next-auth/client'
setOptions({ site: 'http://localhost:3000 })
const WrappedApp: FC<CustomProps> & StaticComponents = (props) => {
const { Component, pageProps, session } = props
return (
<Provider store={store}>
<AuthProvider session={session} options={{ site: 'http://localhost:3000' }}>
<HeaderNav {...props} />
<Component {...pageProps} />
</AuthProvider>
</Provider>
)
}
WrappedApp.getInitialProps = async (context) => {
const appProps = await App.getInitialProps(context)
const session = await getSession(context)
return {
...appProps,
session
}
}
export default WrappedApp Note: You can pass a regular environment variable (e.g. |
Thanks for the response. I no longer get the fetch error, but the Another question - how do folks generally implement |
@iaincollins I think I see where the problem is, and I believe it's a bug in the client code under this particular situation. I put some log statements in var getSession = function () {
var _ref = _asyncToGenerator(function* () {
var {
req
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var baseUrl = _baseUrl();
var options = req ? {
headers: {
cookie: req.headers.cookie
}
} : {};
console.log(arguments)
console.log('req', req)
console.log('opts', options)
console.log(baseUrl, "/session") The output looks like this:
If I do this, the flow works, but it feels quite hacky: WrappedApp.getInitialProps = async (context) => {
context['req'] = context.ctx.req Not sure what the appropriate fix would be for |
The best approach is to use the If you need server side rendering then yes, you need to call You can do that by creating an 'AuthPage' class and extending it (or a HOC and using that) to apply it only to pages that require authentication, or add it to We plan to do some tutorial for folks in future to help with this. Thanks for you PR will leave a note there! |
I went with this route because useSession would have this brief flash where the page renders in a logout state then immediately flashes the layout to the login one, which was pretty jarring. |
The example project doesn't have that problem and was created to show how to avoid that, including on sites that also need to support server side rendering on some pages. If you go this route, do aware your website will be slower to access as a result and Next.js will generate a warning if you do this because it prevents automatic static optimization of your site. The performance impact isn't usually noticeable running locally as everything runs very quickly, but can be significant when the site is deployed to Vercel / AWS Lambda. Related: I've added some examples of alternative approaches to securing routes in #347 that contain approaches that are somewhat relevant. |
Thank you so much for your time and providing guidance. I'll check out your updated examples. |
I'm happy to leave this issue open until we have a more satisfying answer to your question. :-) |
I know this issue is closed, but would it be possible to pass crsfToken on _app too? (i'm asking because I have a few pages where I check if a user is logged in or not after a certain action, e.g. click on a button). For this I'm fetching the session and csrfToken on pageload or on the server. It would be nice if we could pass those down, so we don't need to check on those pages.. (this means I'd be able to have those pages be SSG instead of SSR). |
As a side note, I'm fetching those since after the click. I'm showing a login modal, where I need to pass the csrf for email login. |
Requested in #345 getSession() already does this so seems reasonable to support it in getCsrfToken too.
Requested in #345 getSession() already does this so seems reasonable to support it in getCsrfToken too.
Requested in #345 getSession() already does this so seems reasonable to support it in getCsrfToken too.
Requested in #345 getSession() already does this so seems reasonable to support it in getCsrfToken too.
Requested in #345 getSession() already does this so seems reasonable to support it in getCsrfToken too.
Similar problem here trying to So, it works well when I work with in local development, but when I deploy it to
|
For anyone looking for an example, here's mine: import type { AppContext } from 'next/app'
import App from 'next/app'
import Layout from '../components/layout'
import { getSession, SessionProvider } from "next-auth/react"
import SSRProvider from 'react-bootstrap/SSRProvider'
// Not necessary if you aren't using SWR
import { SWRConfig } from 'swr'
function MyApp({ Component, pageProps, session }) {
return (
<SSRProvider>
<SessionProvider session={session}>
<SWRConfig
value={{
refreshInterval: 3000,
fetcher: async (resource, init) => fetch(resource, init).then(async res => res.json())
}}
>
<Layout>
<Component {...pageProps} />
</Layout>
</SWRConfig>
</SessionProvider>
</SSRProvider>
)
}
MyApp.getInitialProps = async (appContext: AppContext) => {
// perhaps getSession(appContext.ctx) would also work
const session = await getSession({ req: appContext.ctx.req })
const appProps = await App.getInitialProps(appContext)
return { ...appProps, session }
}
export default MyApp Now I can use |
Keep in mind that export default function App({Component, pageProps: {session, ...pageProps}}) {
return (
<SessionProvider session={session}>
<Layout>
<Component {...pageProps} />
</Layout
</SessionProvider>
)
}
function Layout({ children }) {
const session = useSession()
if (session.status === "loading") {
return "skeleton UI"
} else if(session.status === "unauthenticated") {
return "not logged in"
}
return "UI using session.data"
} With a nicely implemented skeleton UI, you can minimize/eliminate the most undesired flashes/jumps. This will have a better perceived performance in my opinion. |
There's a button in the header that says "log in" or "log out". I'm not sure what you mean by skeleton UI but off the top of my head I don't know how the flash there could be hidden. FWIW my site is a note-taking app and the only page that could even in theory be pre-rendered is the landing page, everything else is generated from user content. Or maybe I've also misunderstood what you mean by deoptimizing. |
I see. Dashboards/user-focused sites are generally a good exception for this. |
I think I've finally come up with the right way to use Here is my current version: function MyApp(props) {
const { Component, pageProps } = props
return (
<SSRProvider>
{/* If props.session isn't provided, MyApp is being rendered client-side. In this case
we make sure to not provide any session at all so that the session cache maintained
by the SessionProvider would be reused. */}
<SessionProvider {...(props.session !== undefined) ? { session: props.session } : {}}>
<Layout>
<Component {...pageProps} />
</Layout>
</SessionProvider>
</SSRProvider>
)
}
MyApp.getInitialProps = async (appContext: AppContext) => {
let session: Session | null | undefined = undefined
// getSession works both server-side and client-side but we want to avoid any calls to /api/auth/session
// on page load, so we only call it server-side.
if (typeof window === 'undefined')
session = await getSession(appContext.ctx)
const appProps = await App.getInitialProps(appContext)
return { ...appProps, ...((session !== undefined) ? { session } : {}) }
} |
For those that are experiencing a flash of content when using a client side redirect this may help: const AuthRedirect = ({ children }: { children?: React.ReactNode }) => {
const { status } = useSession();
const router = useRouter();
React.useEffect(() => {
if (status === "unauthenticated" && router.asPath !== "/auth/signIn") {
signIn();
}
}, [status, router]);
// Make sure that you show a loading state for BOTH loading and unauthenticated.
// This is because when status becomes `unathenticated` the component renders,
// returns children and then the useEffect redirect is fired afterwards,
// hence the temporary flash of the child content.
if (status === "loading" || status === "unauthenticated") {
return <div>Loading...</div>
}
return <>{children}</>;
}; |
Requested in nextauthjs#345 getSession() already does this so seems reasonable to support it in getCsrfToken too.
Your question
All the examples I've seen for
getSession()
is for a specific page, but I'd like to pass thesession
prop at the layout level, via_app.js
.What is the proper way of using it in
_app.js
for both client/server side application?What are you trying to do
Apply the
session
prop to all page props without duplicating logic by setting it through_app.js
.With the example, I also get despite having the proper
site
value:Documentation feedback
Documentation refers to searching through online documentation, code comments and issue history. The example project refers to next-auth-example.
The text was updated successfully, but these errors were encountered: