Next Auth with external authorization service.
Unanswered
Korat posted this in #help-forum
KoratOP
Since i'm already using an external service to create the jwt for me and the refresh token logic, I'm sending the access_token to the client using zustand for better usability (I dont want to send a request to /api/session for taking the access_token everytime I make a fetch in the client).
This is my client.tsx (fetch wrapper)
My AuthProvider which wraps the app in RootLayout
Thing is, how do I make this access_token which is in client to be in sync with the one saved in the server on token rotation (token refresh)
Here is my auth.ts file attached.
This is my client.tsx (fetch wrapper)
import { useAuthenticationStore } from '@/store/authentication.store';
export const client = async (endpoint: string, init?: RequestInit) => {
const access_token = useAuthenticationStore.getState().access_token;
const fetchRequest = await fetch(endpoint, {
...init,
headers: {
...init?.headers,
Authorization: `Bearer ${access_token}`,
},
cache: 'no-cache',
});
return fetchRequest.json();
};My AuthProvider which wraps the app in RootLayout
'use client';
import { useAuthenticationStore } from '@/store/authentication.store';
import { PropsWithChildren } from 'react';
export default function AuthProvider({
access_token,
children,
}: PropsWithChildren<{ access_token: string }>) {
if (access_token) useAuthenticationStore.setState({ access_token });
return children;
}Thing is, how do I make this access_token which is in client to be in sync with the one saved in the server on token rotation (token refresh)
Here is my auth.ts file attached.
28 Replies
KoratOP
Yeah, here it is
/**
*
* @param refreshToken
* @returns
*/
export const refreshTokenAction = async (
refreshToken: string
): Promise<LoginResponseDto> => {
const response = await fetch(
'https://server/api/refresh-token',
{
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken }),
method: 'POST',
}
);
return await response.json();
};I've been working on this for too much now, I would appreciate the most your help
Also this is marked as server fyi
@Korat Yeah, here it is
/**
*
* @param refreshToken
* @returns
*/
export const refreshTokenAction = async (
refreshToken: string
): Promise<LoginResponseDto> => {
const response = await fetch(
'https://server/api/refresh-token',
{
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken }),
method: 'POST',
}
);
return await response.json();
};
I would set
access_token to cookies then do this in refreshTokenAction/**
*
* @param refreshToken
* @returns
*/
export const refreshTokenAction = async (): Promise<LoginResponseDto> => {
const refreshToken = cookies().get('refreshToken')?.value || ''
const response = await fetch(
'https://server/api/refresh-token',
{
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken }),
method: 'POST',
}
);
return await response.json();
};KoratOP
I need a way to rerender AuthProvider so it gets the new access token from the session, maybe use an useEffect and check for session changes, but that didn't work
'use client';
import { useEffect } from 'react';
import { useSession } from 'next-auth/react';
import { useAuthenticationStore } from '@/store/authentication.store';
export default function AuthProvider() {
const { data: session, status } = useSession();
useEffect(() => {
if (status === 'authenticated' && session) {
useAuthenticationStore.setState({
access_token: session.user?.access_token,
});
}
}, [session, status]);
return null;
}This is how my rootlayout looks like
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const session = await auth();
return (
<html lang='en'>
<body className={inter.className}>
<SessionProvider session={session}>
<AuthProvider />
<QueryClientProvider>
<header className='space-x-2'>
<Link href='/users'>Users</Link>
<Link href='/notifications'>Notification</Link>
</header>
{children}
</QueryClientProvider>
</SessionProvider>
</body>
</html>
);
}KoratOP
Interesting thought, lemme try it,
But this only works if the rootlayout rerenders right
But this only works if the rootlayout rerenders right
@Korat Interesting thought, lemme try it,
But this only works if the rootlayout rerenders right
yeah, or move
AuthProvider to app/template.tsxKoratOP
I dunno why but there is no expires inside session
am i overriding it the wrong way
from session callback
async session(params: any) {
console.log('SESSION CALLBACK', params.token);
if (!('token' in params)) return params.session;
return {
id: params.token?.id,
email: params.token?.email,
access_token: params.token?.access_token,
};
},@Korat am i overriding it the wrong way
yes
callbacks: {
async session({ session, token, user }) {
// Send properties to the client, like an access_token and user id from a provider.
session.accessToken = token.accessToken
session.user.id = token.id
return session
}
}KoratOP
Daym, i think the key worked (but I would love to know why, since root layout doesn't rerender or does it)
@Ray yes
ts
callbacks: {
async session({ session, token, user }) {
// Send properties to the client, like an access_token and user id from a provider.
session.accessToken = token.accessToken
session.user.id = token.id
return session
}
}
KoratOP
Yeah thats what I did, do I have to change the jwt callback too ?
I've returned it as object and not replaced the values
@Ray I think this is working only on tab change and not when navigating throught pages
@Korat <@743561772069421169> I think this is working only on tab change and not when navigating throught pages
what do you mean on tab change?
KoratOP
When I defocus and focus the app, the AuthProvider rerenders and takes the new access_token from session
look like react-query?
use
app/template.ts for <AuthProvider />template will re-render on page change
KoratOP
import AuthProvider from '@/providers/auth.provider';
export default function Template({ children }: { children: React.ReactNode }) {
return (
<>
<AuthProvider />
{children}
</>
);
}I guess nothing changed, i still see the different access_token in client,
When changing chrome tabs, it does recieve the new access_token and if reloading the browser too, but not while in the app (navigating to different pages)
When changing chrome tabs, it does recieve the new access_token and if reloading the browser too, but not while in the app (navigating to different pages)