2 - jwt final

This commit is contained in:
safronman
2024-06-14 16:18:40 +03:00
parent ce9d06057e
commit 86c56f2f03
6 changed files with 119 additions and 19 deletions

View File

@@ -24,8 +24,8 @@ type Props = {
export const SignIn = (props: Props) => {
const { control, handleSubmit } = useForm<FormType>({
defaultValues: {
email: '',
password: '',
email: 'test@test.com',
password: 'test',
rememberMe: false,
},
mode: 'onSubmit',

View File

@@ -34,7 +34,7 @@ const privateRoutes: RouteObject[] = [
},
]
const router = createBrowserRouter([
export const router = createBrowserRouter([
{
children: [
{

View File

@@ -1,15 +1,33 @@
import { flashcardsApi } from '..'
import { LoginArgs, User } from './auth.types'
import { LoginArgs, LoginResponse, User } from './auth.types'
export const authService = flashcardsApi.injectEndpoints({
endpoints: builder => ({
login: builder.mutation<void, LoginArgs>({
invalidatesTags: ['Me'],
query: body => ({
body,
method: 'POST',
url: '/v1/auth/login',
}),
login: builder.mutation<LoginResponse, LoginArgs>({
async onQueryStarted(
// 1 параметр: QueryArg - аргументы, которые приходят в query
_,
// 2 параметр: MutationLifecycleApi - dispatch, queryFulfilled, getState и пр.
// queryFulfilled - это промис, возвращаемый RTK Query, который разрешается,
// когда запрос успешно завершен
{ queryFulfilled }
) {
const { data } = await queryFulfilled
if (!data) {
return
}
localStorage.setItem('accessToken', data.accessToken)
localStorage.setItem('refreshToken', data.refreshToken)
},
query: body => {
return {
body,
method: 'POST',
url: '/v1/auth/login',
}
},
}),
me: builder.query<User, void>({
providesTags: ['Me'],

View File

@@ -3,6 +3,12 @@ export type LoginArgs = {
password: string
rememberMe?: boolean
}
export type LoginResponse = {
accessToken: string
refreshToken: string
}
export type User = {
avatar: null | string
created: string

View File

@@ -0,0 +1,81 @@
import { router } from '@/router'
import {
BaseQueryFn,
FetchArgs,
FetchBaseQueryError,
fetchBaseQuery,
} from '@reduxjs/toolkit/query/react'
import { Mutex } from 'async-mutex'
// create a new mutex
const mutex = new Mutex()
const baseQuery = fetchBaseQuery({
baseUrl: 'https://api.flashcards.andrii.es',
prepareHeaders: headers => {
const token = localStorage.getItem('accessToken')
if (headers.get('Authorization')) {
return headers
}
if (token) {
headers.set('Authorization', `Bearer ${token}`)
}
return headers
},
})
export const baseQueryWithReauth: BaseQueryFn<
FetchArgs | string,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions) => {
// wait until the mutex is available without locking it
await mutex.waitForUnlock()
let result = await baseQuery(args, api, extraOptions)
if (result.error && result.error.status === 401) {
// checking whether the mutex is locked
if (!mutex.isLocked()) {
const release = await mutex.acquire()
try {
const refreshToken = localStorage.getItem('refreshToken')
const refreshResult = (await baseQuery(
{
headers: {
Authorization: `Bearer ${refreshToken}`,
},
method: 'POST',
url: '/v2/auth/refresh-token',
},
api,
extraOptions
)) as any
console.log('refreshResult', refreshResult)
if (refreshResult.data) {
localStorage.setItem('accessToken', refreshResult.data.accessToken)
localStorage.setItem('refreshToken', refreshResult.data.refreshToken)
// retry the initial query
result = await baseQuery(args, api, extraOptions)
} else {
router.navigate('/login')
}
} finally {
// release must be called once the mutex should be released again.
release()
}
} else {
// wait until the mutex is available without locking it
await mutex.waitForUnlock()
result = await baseQuery(args, api, extraOptions)
}
}
return result
}

View File

@@ -1,13 +1,8 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { baseQueryWithReauth } from '@/services/flashCardsBaseQuery'
import { createApi } from '@reduxjs/toolkit/query/react'
export const flashcardsApi = createApi({
baseQuery: fetchBaseQuery({
baseUrl: 'https://api.flashcards.andrii.es',
credentials: 'include',
prepareHeaders: headers => {
headers.append('x-auth-skip', 'true')
},
}),
baseQuery: baseQueryWithReauth,
endpoints: () => ({}),
reducerPath: 'flashcardsApi',
tagTypes: ['Decks', 'Me'],