-
Notifications
You must be signed in to change notification settings - Fork 93
feat: Next.js SSR Improvements #877
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
base: master
Are you sure you want to change the base?
Conversation
944a484
to
e278f9f
Compare
size-limit report 📦
|
b306f85
to
ad89bbe
Compare
8231792
to
7c23d35
Compare
ebadbed
to
3f9b519
Compare
Update the function and add types Add more info about the proposal Account for pages directory in the function implementation Update types and clean things up Add comments Add nextjs init function Add the jose library Fix refreshing and add example Fix build and token config Fix payload processing Load config fron env variables Add a redirect location during refresh Refresh token from middleware Move everything in the library Rename files and update function singatures Use the correct refresh path Use normal responses instead of nextresponse Use normal request instead of nextrequest Cleanup implementation Revoke session inside the middleware Code review fixes Add tests for getSSRSession Add tests for middleware
3f9b519
to
0797deb
Compare
578dc07
to
22365e9
Compare
22365e9
to
5bfbbd1
Compare
accessTokenPayload, | ||
doesSessionExist: true, | ||
loading: false, | ||
invalidClaims: [], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should somehow communicate that this doesn't do claim validation, but maybe docs are enough.
cookies: CookiesObject | CookiesStore | ||
): Promise<{ state: SSRSessionState; session?: LoadedSessionContext }> { | ||
const frontToken = | ||
getCookieValue(cookies, FRONT_TOKEN_COOKIE_NAME) || getCookieValue(cookies, FRONT_TOKEN_HEADER_NAME); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FRONT_TOKEN_HEADER_NAME
doesn't appear as a cookie I think.
function defaultIsApiRequest(request: Request): boolean { | ||
const requestUrl = new URL(request.url); | ||
const refreshPath = getRefreshAPIPath(); | ||
return requestUrl.pathname.startsWith(refreshPath); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this isn't right, or the name is misleading.
* @returns The server action return value | ||
**/ | ||
static async authenticateServerAction<T extends (session: LoadedSessionContext) => Promise<K>, K>(action: T) { | ||
let loadedSessionContext: LoadedSessionContext | undefined = undefined; | ||
static async confirmAuthenticationAndCallServerAction<T extends () => Promise<K>, K>(action: T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a shorter name would be better.
static async confirmAuthenticationAndCallServerAction<T extends () => Promise<K>, K>(action: T) { | |
static async ensureSessionAndCall<T extends () => Promise<K>, K>(action: T) { |
function getRefreshAPIPath(): string { | ||
const apiPath = SuperTokensNextjsSSRAPIWrapper.getConfigOrThrow().appInfo.apiBasePath || DEFAULT_API_PATH; | ||
return `${apiPath}/session/refresh`; | ||
} | ||
|
||
function getRefreshLocation(redirectPath: string): string { | ||
return `${SESSION_REFRESH_API_PATH}?${REDIRECT_PATH_PARAM_NAME}=${redirectPath}`; | ||
const refreshAPIPath = getRefreshAPIPath(); | ||
return `${refreshAPIPath}?${REDIRECT_PATH_PARAM_NAME}=${redirectPath}`; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the first name is also present in the middleware file, you could indicate in the name that one path is meant for the frontend/as a full page redirect.
throw new Error("JWT header missing alg (Algorithm)"); | ||
} | ||
|
||
const jwksResponse = await fetch(jwksUrl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will reload the jwks on every action call, could we cache this somehow? Also, I'm not necessarily opposed to using jose if it doesn't mess with our bundle sizes.
Summary of change
The following PR improves SSR support in Next.js applications.
To get the session data in the context of server side rendering you now have to call
getSSRSession
either inside aServer Component
, or ingetServerSideProps
(for older next applications).There's two parts to this change:
Fetching the session during SSR
The logic for this is kept in the
ssr.ts
file. The function reads the active tokens and has 3 possible outcomes:Refreshing a session
The
getSSRSession
function redirects to arefresh
endpoint that we do not expose from our existing API.Hence, this change adds logic inside the next.js middleware to handle such a request. The custom middleware calls the backend API using a fetch and updates the response with the new tokens.
This part is now kept mostly in the
middleware.ts
file, in the react library. It's there just to make it easy to review during this stage. It should be moved in the node SDK package as a separate, Nextjs specific API.Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos!)
Documentation changes
(If relevant, please create a PR in our docs repo, or create a checklist here highlighting the necessary changes)
Checklist for important updates
frontendDriverInterfaceSupported.json
file has been updated (if needed)package.json
package-lock.json
lib/ts/version.ts
npm run build-pretty
git tag
) in the formatvX.Y.Z
, and then find the latest branch (git branch --all
) whoseX.Y
is greater than the latest released tag.someFunc: function () {..}
).size-limit
section ofpackage.json
with the size limit set to the current size rounded up.rollup.config.mjs
lib/ts/types.ts
lib/ts/recipe/multifactorauth/types.ts
Remaining TODOs for this PR