-
Notifications
You must be signed in to change notification settings - Fork 397
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
Client side API query example/explanation #132
Comments
Also, usage of auth credentials only works on the backend, I don't understand why there is a need to have all env variables filled during build time('npm run build' command). |
Don't quite understand that myself, but this post I wrote the other day might be of use if you're building for Docker: #86 (comment). Basically, just create dummy values that get you through the build, and then set env vars as part of your deployment which will overwrite whatever was in your dummy file anyway. |
I need a solution to this problem as well. We are actually using GraphQL as backend and trying to handle Auth0 authentication and a separate Apollo Server. Since the access token is not exposed on the client side, I am not able to use Apollo client to direct query to GraphQL server. |
We'll need to write a page explaining your options. This SDK follows the Regular Web Application model, meaning:
If your need is to have access tokens on the client and calling APIs from the client I would suggest moving to our new React SDK: https://auth0.com/docs/quickstart/spa/react/01-login |
@sandrinodimattia can the two solutions be used together? We need both server-side and client-side authentication. |
@sandrinodimattia Not to pile on, I think I understand what you're saying based on reading your invaluable ultimate guide article. That article was written a while back and the React SDK is just coming out now with their own Next JS integration example. But my guess is that if you were to update that article today (not a bad idea?) you would say to use the React SDK in order to take the first option, Thank you again, your explanation in that article is so incredibly thorough and helpful, it's really a godsend! 🙏 |
I also had this need and wanted to share my solution to this. For my use-case, I wanted to be able to:
If this is useful, I'd be happy to turn it into a PR. Credit: this was adapted from an old with-redux implementation Code
pages/dashboard.jsimport React from 'react'
import { withSession, useSession } from '../utils/session-provider'
function DashboardPage(props) {
const session = useSession()
console.log(props.hello) // 'hello from getInitialProps'
// You can use `session.accessToken` to make an authenticated request on the client
// Could be with useEffect, or your own useApi, useQuery, or whatever
const data = useApi('/dashboard', session.accessToken)
return (
<Dashboard data={data} />
)
}
DashboardPage.getInitialProps = async function (context) {
const { session } = context;
// You can use `session.accessToken` to make an authenticated request on first page load
// This works for both server and client requests
// Here, you'd have to use `fetch` or `axios`, or whatever else directly.
const data = await fetch('/api/dashboard', session.accessToken)
return {
message: 'hello from getInitialProps',
}
}
export default withSession(DashboardPage) utils/auth0.jsimport { initAuth0 } from '@auth0/nextjs-auth0'
export default initAuth0({
// ... your config
session: {
// ... your session config
storeAccessToken: true, // required for this example to work
storeRefreshToken: true, // required for this example to work
}
}) utils/session-provider.jsimport React from 'react'
import App from 'next/app'
import Router from 'next/router'
import auth0 from './auth0'
const SessionContext = React.createContext()
SessionContext.displayName = 'SessionContext'
export function useSession() {
const context = React.useContext(SessionContext)
if (context === undefined) {
throw new Error(
'useSession must be used within a SessionProvider. Did you wrap the page component with `withSession`?'
)
}
return context
}
export const withSession = (PageComponent) => {
const WithSession = ({ session, ...props }) => {
return (
<SessionContext.Provider value={session}>
<PageComponent {...props} />
</SessionContext.Provider>
)
}
WithSession.getInitialProps = async (context) => {
const session = await getSession(context)
// Provide the session to getInitialProps of pages
// In order to make authenticated requests
context.session = session
// Run getInitialProps from HOCed PageComponent
const pageProps =
typeof PageComponent.getInitialProps === 'function'
? await PageComponent.getInitialProps(context)
: {}
// Pass props to PageComponent
return {
session,
...pageProps,
}
}
return WithSession
}
async function getSession(context) {
const { req, res, pathname } = context
// On the server, we can ask auth0 for the session
if (req) {
const session = await auth0.getSession(req)
if (!session || !session.user) {
res.writeHead(302, {
Location: '/api/login',
})
res.end()
return {}
} else {
const tokenCache = auth0.tokenCache(req, res)
const { accessToken } = await tokenCache.getAccessToken()
return {
...session,
accessToken,
}
}
}
// On the client
const { session } = __NEXT_DATA__.props.pageProps
if (session) {
// If we go from authenticated to authenticated page (both using withSession),
// We can pass around the session from pageProps
// This crucially avoids an un-needed network request when a page is requested from a Link component
return session
} else {
// If we go from unauthenticated (without withSession) to authenticated page (with withSession),
// we have to redirect through login first
Router.replace(`/api/login?redirectTo=${pathname}`)
}
} |
@danthareja Couple of things with your example code: first, it's a little more straightforward to destructure the user from the session and provide that directly as a prop. The frontend doesn't need and probably should not get the entire session object with all of its cookie information. Second, I'm pretty sure getInitialProps is being outmoded by next and you should use getServerSideProps. Finally I'm not sure the use of The way I solved what you're talking about is this:
This system works great so far, in that it keeps all the cookie and session logic on the backend (including secrets obv.) and then automates providing |
Thanks for the reply, @mosesoak! I appreciate the feedback. Good call on destructuring the user, I like that suggestion. The original intention of the whole session was to include any additional properties added by the I actually chose to use Your setup sounds interesting. Where are you caching the logged-in session that you lookup in Could you share an example of the custom hook? Edit: I 100% agree using |
@danthareja It's already cached in the cookie. Lookup of the session doesn't necessarily call the server. My hook is really similar to what you'd find in Sandrino's example code, except that I add the logged-out state which can be boolean or undefined (initial/unset). Here's a simplified example version. // for use in _app only
export const useFetchUser = (ssrUser?: User): User => {
if (isServerSide) {
return ssrUser;
}
const [user, setUser] = useState(ssrUser);
const [isFetchingUser, setIsFetching] = useState(false);
const [isLoggedOut, setIsLoggedOut] = useState(ssrUser ? false : undefined);
useEffect(() => {
if (isLoggedOut === undefined && !isFetchingUser && !user) {
setIsFetching(true);
fetchUser().then((res) => {
setIsFetching(false);
if (res) {
setUser(processUser(res));
} else {
setIsLoggedOut(true);
}
});
}
}, []);
return { user, isFetchingUser };
}; |
Isn't this implied by using
How would you avoid this extra request in your example? Maybe it's not necessary and I'm overthinking this whole thing... I'm admittedly new to the Next.js patterns. |
Oh, you mean the nextjs api route? Yes it would call that, I was referring to not calling auth0's server. I don't think there's much of a penalty for using api routes in next, they're screamingly fast and a great way to handle stuff like this. This nextjs-auth0 library is really authored to be used as much within api routes as possible. |
Yeah, I was thinking of a way to avoid that request. I only SSR a UI skeleton (including a user profile), and most of my data fetching is done in the client as an SPA, so I wanted to keep those transitions snappy. I haven't profiled the performance of using Thanks for sharing your thoughts @mosesoak, it's always nice to hear perspectives from others. |
I been also wondering if there isn't a way of reading the But from my understanding the only way would be to get the Also with the data available on the client side, there is no way a user could sign a request and send it signed as an alternative to using the Also i'm a bit unsure about how the If the token expires the the API should notify the user with an error and then the user must call next.js api to get a new TLDR: Please correct me if i'm wrong, but from my understanding on this thread, there is no way of :
|
Right, however this library is very much designed as a serverless-driven alternative to their other frontend-centric libs. Since you want to use frontend I'd suggest using their js client (or the react one), which are able to fetch access tokens and have the advantage of being able to silently refresh user login using a hidden iframe. |
Hi everyone, please check out the examples page of the new v1.0.0-beta.0 release for guidance on this: Also, we added some information in the README of the beta wrt this library vs auth0-react. |
Thank you for the library first of all! I'm trying to understand some logic that goes behind the scene and for me, it is not clear why the library forces users to have only API query proxied over next.js API.
Auth0 by default allows you to maintain and refresh tokens just using client-side only, however, in the current implementation everything should go over the backend since all functions are available only from the server-side.
So I'm curious how do you propose to handle situations where there is a need to do a query from the client side that requires access token?
The text was updated successfully, but these errors were encountered: