i have my server running with Node.js & Express framework, react+redux+rtk query – client side, PostgreSQL for db.
and when you click at “/api/qr” route through the client-side, rtk baseQueryWithReauth should refresh your access-token:
server refresh tokens:
app.post('/api/refresh-token', async (req, res) => {
const { refreshToken } = req.cookies;
if (!refreshToken) {
return res.status(401).json({ error: 'Refresh token not found' });
}
try {
const decoded = jwt.verify(refreshToken, REFRESH_TOKEN_SECRET);
const user = await User.findOne({ where: { id: decoded.userId, refresh_token: refreshToken } });
if (!user) {
return res.status(401).json({ error: 'Invalid refresh token or user not found' });
}
const newAccessToken = jwt.sign({ userId: user.id }, ACCESS_TOKEN_SECRET, { expiresIn: '10s' });
return res.json({ accessToken: newAccessToken });
} catch (err) {
console.error(err);
return res.status(401).json({ error: 'Invalid refresh token' });
}
});
client RTK baseQuery with headers and baseQueryWithReauth logic:
const baseQuery = fetchBaseQuery({
baseUrl: 'http://localhost:3001',
credentials: 'include',
prepareHeaders: (headers, { getState }) => {
const token = (getState() as RootState).auth.accessToken;
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
return headers;
},
});
const baseQueryWithReauth: BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError> = async (args, api, extraOptions) => {
let result = await baseQuery(args, api, extraOptions);
if (result.error && result.error.status === 401) {
console.log('SENDING NEW ACCESS-TOKEN');
try {
const refreshResult = await baseQuery({
url: `/api/refresh-token`,
method: "POST",
credentials: 'include',
}, api, extraOptions);
if (refreshResult.data) {
api.dispatch(setToken( { accessToken: refreshResult.data as string} ));
result = await baseQuery(args, api, extraOptions);
}else{
api.dispatch(logout())
}
} catch(err) {
console.error('Ошибка доступа', err)
}
}
return result;
};
export const apiSlice = createApi({
reducerPath: 'api',
baseQuery: baseQueryWithReauth,
endpoints: (builder: any) => ({
// add QR-code
addQRcode: builder.mutation({
query: (body: AddQRCodeRequest) => ({
url: `/api/qr`,
method: 'POST',
body,
}),
}),
Redux store authSlice for user:
const authSlice = createSlice({
name: 'auth',
initialState: {
accessToken: '',
isAuthenticated: false,
login: null,
loginStatus: 'idle',
role: false,
} as InitialState,
reducers: {
logout(state: InitialState) {
state.isAuthenticated = false;
state.login = null;
state.loginStatus = 'idle';
state.accessToken = '';
},
setToken(state: InitialState, action: { payload: { accessToken: string } }) {
state.accessToken = action.payload.accessToken;
},
},
“setToken()” reducer dispatches payload from “refreshResult.data” and store this data in authSlice
i expected to see my new access token (expiresIn: 10s) refresh through the console.log() or devtools network tab getting 401 error and refresh it, but never get one.
i would realy appreciate if you point me at my mistakes, if its because of RTK cashe or something else.
ValentineFrolov is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.