![Program](https://quera.org/qbox/view/XTJCkHBtDu/ezgif-1-4ff7a2b34e.gif)
چند وقتی است که بحث توکن های *JWT* برای احراز هویت داغ شده است؛ به دلایل متعدد زیادی که بسیار هم موجه هستند. نیما تصمیم گرفته که مشقی به مهیار بدهد که این شیوهی احراز هویت را در ریاکت به واسطهی یک هوک پیاده کند. همچنین نیما زحمت پیادهسازی سروری که از این شیوهی احراز هویت پشتیبانی میکند را قبول کرده است.
این سرور وظیفهی احراز هویت کاربران را برعهده دارد و همواره توکنی که برمیگرداند زمان انقضای مشخصی دارد، به این معنا که پس از زمان تعیینشده، دیگر توکن قابل استفاده نیست. از این توکن تحت عنوان *Access Token* نیز یاد میشود. در کنار این توکن، یک توکن برای دریافت مجدد توکن جدید و معتبر نیز در اختیار کاربر قرار میدهد. از این توکن تحت عنوان *Refresh Token* یاد میشود. با ارسال این توکن به سرور، در صورت معتبر بودن، یک *Access Token* و همچنین *Refresh Token* جدید در اختیار کاربر قرار میگیرد.
مهیار نه تنها بسیار در امر زمانبندی بسیار نامنظم است، بلکه بسیار تنبل و پر مشغله نیز هست! به همین دلیل، تصمیم گرفته است که از شما در انجام این تمرین کمک بگیرد.
## جزئیات پروژه
پروژهی اولیه را از [این لینک](/contest/assignments/38422/download_problem_initial_project/134193/) دانلود کنید.
<details class="green">
<summary>
ساختار فایلهای پروژه
</summary>
```
jwt-auth
├── package.json
├── package-lock.json
├── public
│ └── index.html
├── README.md
├── server
│ ├── index.js
│ └── requests.rest
└── src
├── App.css
├── App.js
├── Components
│ └── ...
├── Hooks
│ └── <mark>useJWT.js</mark>
├── index.js
├── Pages
│ ├── <mark>Login.jsx</mark>
│ └── <mark>Profile.jsx</mark>
├── setupTests.js
└── __tests__
└── sample.test.js
```
</details>
<details class="brown">
<summary>
راهاندازی پروژه
</summary>
- پروژهی اولیه را دانلود و از حالت فشرده خارج کنید.
- با اجرای دستور `npm i` پکیجهای موردنیاز را نصب کنید.
- با اجرای دستور `node server/index.js` سرور را اجرا کنید.
- با اجرای دستور `npm start` پروژه را اجرا کنید.
</details>
## هوک `useJWT`
نیما از مهیار خواسته است که یک هوک برای این شیوهی احراز هویت طراحی کند که بتوان از آن در پروژههای بعدی نیز استفاده کرد. جزییات این هوک که نامش `useJWT` است بهشرح زیر است:
این هوک باید چهار تابع با نامهای زیر برگرداند. در ادامه به جزئیات هر یک از آنها خواهیم پرداخت:
```js
const { login, logout, refreshToken, sendPostRequest } = useJWT()
```
نکتهای که نیما از مهیار خواسته است در پیادهسازی این توابع به خاطر داشته باشد، این است که بتوان از همهی آنها به شکل زیر استفاده کرد:
```js
f(...args).then(data => {}}.catch(err => {});
```
### تابع `login`
این تابع بهترتیب دو آرگومان `email` و `password` دریافت میکند و آنها را در قالب یک شی و متد *POST* به آدرس `http://127.0.0.1:4000/api/login` ارسال میکند. در صورتی که رمز و ایمیل کاربر درست وارد شده باشد، سرور یک شی بهصورت زیر برمیگرداند:
```json
{
"data": {
"access": "ACCESS_TOKEN",
"refresh": "REFRESH_TOKEN"
}
}
```
همچنین مقادیر `access` و `refresh` باید بهصورت مجزا و **با همان نام** در فضای محلی مرورگر (`localStorage`) ذخیره شوند (مقدار `access` در کلید `access` و مقدار `refresh` در کلید `refresh` ذخیره شود).
همچنین توکنهای دریافت شده را باید در قالب یک شی بهصورت زیر برگرداند:
```js
{
access: "ACCESS_TOKEN",
refresh: "REFRESH_TOKEN"
}
```
### تابع `refreshToken`
این تابع هیچ آرگومانی دریافت نمیکند. وظیفهی اصلی این تابع، ارسال *Refresh Token* ذخیرهشده در مرورگر در قالب بدنهی یک درخواست *POST* به آدرس `http://127.0.0.1:4000/api/token` است. در صورتی که توکن معتبر باشد، سرور یک شی بهصورت زیر برمیگرداند:
```json
{
data: {
"access": "ACCESS_TOKEN",
"refresh": "REFRESH_TOKEN"
}
}
```
مقادیر `access` و `refresh` به صورت مجزا و **با همان نام** باید در فضای محلی مرورگر (`localStorage`) ذخیره شوند. همچنین بدنه درخواست ارسالی باید به صورت زیر باشد:
```json
{
"token": "REFRESH_TOKEN"
}
```
علاوه بر آن، این تابع باید توکنهای دریافتشده را در قالب یک شی بهصورت زیر برگرداند:
```js
{
access: "ACCESS_TOKEN",
refresh: "REFRESH_TOKEN"
}
```
### تابع `logout`
این تابع هیچ آرگومانی دریافت نمیکند. وظیفهی اصلی این تابع، حذف *Access Token* و *Refresh Token* از `localStorage` است. همچنین این تابع باید مقدار *Refresh Token* ذخیرهشده در `localStorage` را در قالب بدنهی یک درخواست از نوع *DELETE* به آدرس `http://127.0.0.1:4000/api/logout` (در قالب کلیدی تحت عنوان `token`) ارسال کند:
```js
const logout = () => { /* TODO: Implement */ }
```
تضمین میشود که در این بخش همواره کد پاسخ برابر با `204` است.
### تابع `sendPostRequest`
وظیفهی اصلی این تابع، ارسال یک درخواست از نوع *POST* به سرور است. این تابع به عنوان ورودی، ابتدا مسیر درخواست که از نوع رشته است را دریافت میکند و به عنوان آرگومان دوم، دادهای که قرار است در بدنه درخواست ارسال شود را دریافت میکند.
از آنجایی که این تابع به منظور راحتتر شدن ارسال درخواست به مسیرهای که نیاز به توکن دارند طراحی شده است، دو هدف اصلی را دنبال میکند:
- در هدرهای ارسالی باید *Access Token* با کلید `jwt` در هدر قرار بگیرد.
- در صورتی که درخواست اول به دلیل منقضی شدن *Access Token* ناموفق شود، از سرور درخواست یک توکن جدید میکند و درخواست اصلی را مجدداً با توکن جدید ارسال میکند. لازم به ذکر است این عمل *تنها یک بار* صورت میگیرد (در صورتی که در دفعهی دوم هم درخواست ناموفق باشد، نباید خطایی پرتاب شود یا کار دیگری صورت گیرد).
- در صورتی که درخواست موفقیتآمیز باشد، نتیجهی درخواست را برمیگرداند.
## کامپوننت `src/Pages/Login.jsx`
این کامپوننت یک صفحهی ورود است. بخش `return` که بیانگر ساختار صفحه است از قبل نوشته شده است، اما موراد زیر باید تکمیل شوند:
- هنگام فشرده شدن `LoginButton`، ایمیل و رمز عبور با استفاده از متد مربوطهی هوک طراحیشده به سرور ارسال شوند.
- درصورت ورود موفق کاربر، هدایت به آدرس `/` صورت گیرد.
- در صورتی که احراز هویت ناموفق باشد، باید دو کامپوننت `NoRobot` و `ErrorMessage` نمایش داده شوند. همچنین مقدار ورودی `password` باید خالی باشد اما مقدار ورودی `email` همان مقدار قبلی باشد.
- دکمهی `LoginButton` باید در صورتی فعال باشد که ایمیل و رمز عبور هر دو وارد شده باشند. همچنین ایمیل واردشده باید طبق فرمت `user@test.com` باشد و تیک مربوط به `NoRobot` نیز باید فعال باشد.
## کامپوننت `src/Pages/Profile.jsx`
- هنگام بارگذاری این کامپوننت، ابتدا باید بررسی شود که کاربر توکنی دارد یا خیر. در صورت داشتن توکن، یک درخواست به آدرس `http://127.0.0.1:4000/api/user` ارسال شود در غیر این صورت کاربر به صفحه `login/` هدایت شود. پاسخ سرور بهصورت زیر خواهد بود:
```js
{
data: {
"user": {...values}
}
}
```
در اینصورت، شما باید مقدار استیت `user` را برابر با `data` دریافت شده قرار دهید. در غیر اینصورت (یعنی اگر درخواست ناموفق بود)، کاربر باید به آدرس `/login` هدایت شود
- همچنین در صورت فشرده شدن دکمهی `LogoutButton` باید روند خروج کاربر صورت پذیرد و کاربر به آدرس `/login` هدایت شود.
## نکات
- در این پروژه از *React Router* با نسخه `v6` یعنی جدیدترین نسخه، استفاده شده است.
- آدرس درخواستها باید دقیقاً مطابق با آدرسهای ذکرشده در صورت سؤال باشد.
- دادهها باید بهصورت *JSON* به سرور ارسال شوند.
- برای انجام این پروژه میتوانید از `axios` استفاده کنید.
- شما تنها مجاز به اعمال تغییرات در فایلهای `useJWT.js`، `Login.jsx` و `Profile.jsx` هستید.
## آنچه باید آپلود کنید
پس از پیادهسازی موارد خواستهشده، پوشهی `src` را زیپ کرده و آپلود کنید.