لینک‌های مفید برای شرکت در مسابقه:

در طول مسابقه می‌توانید سؤالات خود را از قسمت «سؤال بپرسید» مطرح کنید.

توکن‌چی


Program چند وقتی است که بحث توکن های JWT برای احراز هویت داغ شده است؛ به دلایل متعدد زیادی که بسیار هم موجه هستند. نیما تصمیم گرفته که مشقی به مهیار بدهد که این شیوه‌ی احراز هویت را در ری‌اکت به واسطه‌ی یک هوک پیاده کند. همچنین نیما زحمت پیاده‌سازی سروری که از این شیوه‌ی احراز هویت پشتیبانی می‌کند را قبول کرده است.

این سرور وظیفه‌ی احراز هویت کاربران را برعهده دارد و همواره توکنی که برمی‌گرداند زمان انقضای مشخصی دارد، به این معنا که پس از زمان تعیین‌شده، دیگر توکن قابل استفاده نیست. از این توکن تحت عنوان Access Token نیز یاد می‌شود. در کنار این توکن، یک توکن برای دریافت مجدد توکن جدید و معتبر نیز در اختیار کاربر قرار می‌دهد. از این توکن تحت عنوان Refresh Token یاد می‌شود. با ارسال این توکن به سرور، در صورت معتبر بودن، یک Access Token و همچنین Refresh Token جدید در اختیار کاربر قرار می‌گیرد.

مهیار نه تنها بسیار در امر زمان‌بندی بسیار نامنظم است، بلکه بسیار تنبل و پر مشغله نیز هست! به همین دلیل، تصمیم گرفته است که از شما در انجام این تمرین کمک بگیرد.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

ساختار فایل‌های پروژه
jwt-auth
├── package.json
├── package-lock.json
├── public
│   └── index.html
├── README.md
├── server
│   ├── index.js
│   └── requests.rest
└── src
    ├── App.css
    ├── App.js
    ├── Components
    │   └── ...
    ├── Hooks
    │   └── useJWT.js
    ├── index.js
    ├── Pages
    │   ├── Login.jsx
    │   └── Profile.jsx
    ├── setupTests.js
    └── __tests__
        └── sample.test.js
Plain text
راه‌اندازی پروژه
  • پروژه‌ی اولیه را دانلود و از حالت فشرده خارج کنید.

  • با اجرای دستور npm i پکیج‌های موردنیاز را نصب کنید.

  • با اجرای دستور node server/index.js سرور را اجرا کنید.

  • با اجرای دستور npm start پروژه را اجرا کنید.

هوک useJWT🔗

نیما از مهیار خواسته است که یک هوک برای این شیوه‌ی احراز هویت طراحی کند که بتوان از آن در پروژه‌های بعدی نیز استفاده کرد. جزییات این هوک که نامش useJWT است به‌شرح زیر است:

این هوک باید چهار تابع با نام‌های زیر برگرداند. در ادامه به جزئیات هر یک از آن‌ها خواهیم پرداخت:

const { login, logout, refreshToken, sendPostRequest } = useJWT()
JavaScript

نکته‌ای که نیما از مهیار خواسته است در پیاده‌سازی این توابع به خاطر داشته باشد، این است که بتوان از همه‌ی آن‌ها به شکل زیر استفاده کرد:

f(...args).then(data => {}}.catch(err => {});
JavaScript

تابع login🔗

این تابع به‌ترتیب دو آرگومان email و password دریافت می‌کند و آن‌ها را در قالب یک شی و متد POST به آدرس http://127.0.0.1:4000/api/login ارسال می‌کند. در صورتی که رمز و ایمیل کاربر درست وارد شده باشد، سرور یک شی به‌صورت زیر برمی‌گرداند:

{
    "data": {
        "access": "ACCESS_TOKEN",
        "refresh": "REFRESH_TOKEN"
    }
}
JSON

همچنین مقادیر access و refresh باید به‌صورت مجزا و با همان نام در فضای محلی مرورگر (localStorage) ذخیره شوند (مقدار access در کلید access و مقدار refresh در کلید refresh ذخیره شود).

همچنین توکن‌های دریافت شده را باید در قالب یک شی به‌صورت زیر برگرداند:

{
    access: "ACCESS_TOKEN",
    refresh: "REFRESH_TOKEN"
}
JavaScript

تابع refreshToken🔗

این تابع هیچ آرگومانی دریافت نمی‌کند. وظیفه‌ی اصلی این تابع، ارسال Refresh Token ذخیره‌شده در مرورگر در قالب بدنه‌ی یک درخواست POST به آدرس http://127.0.0.1:4000/api/token است. در صورتی که توکن معتبر باشد، سرور یک شی به‌صورت زیر برمی‌گرداند:

{
    data: {
        "access": "ACCESS_TOKEN",
        "refresh": "REFRESH_TOKEN"
    }
}
JSON

مقادیر access و refresh به صورت مجزا و با همان نام باید در فضای محلی مرورگر (localStorage) ذخیره شوند. همچنین بدنه درخواست ارسالی باید به صورت زیر باشد:

{
    "token": "REFRESH_TOKEN"
}
JSON

علاوه بر آن، این تابع باید توکن‌های دریافت‌شده را در قالب یک شی به‌صورت زیر برگرداند:

{
    access: "ACCESS_TOKEN",
    refresh: "REFRESH_TOKEN"
}
JavaScript

تابع logout🔗

این تابع هیچ آرگومانی دریافت نمی‌کند. وظیفه‌ی اصلی این تابع، حذف Access Token و Refresh Token از localStorage است. همچنین این تابع باید مقدار Refresh Token ذخیره‌شده در localStorage را در قالب بدنه‌ی یک درخواست از نوع DELETE به آدرس http://127.0.0.1:4000/api/logout (در قالب کلیدی تحت عنوان token) ارسال کند:

const logout = () => { /* TODO: Implement */ }
JavaScript

تضمین می‌شود که در این بخش همواره کد پاسخ برابر با 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/ هدایت شود. پاسخ سرور به‌صورت زیر خواهد بود:
{
    data: {
        "user": {...values}
    }
}
JavaScript

در این‌صورت، شما باید مقدار استیت user را برابر با data دریافت شده قرار دهید. در غیر این‌صورت (یعنی اگر درخواست ناموفق بود)، کاربر باید به آدرس /login هدایت شود

  • همچنین در صورت فشرده شدن دکمه‌ی LogoutButton باید روند خروج کاربر صورت پذیرد و کاربر به آدرس /login هدایت شود.

نکات🔗

  • در این پروژه از React Router با نسخه v6 یعنی جدیدترین نسخه، استفاده شده است.
  • آدرس درخواست‌ها باید دقیقاً مطابق با آدرس‌های ذکرشده در صورت سؤال باشد.
  • داده‌ها باید به‌صورت JSON به سرور ارسال شوند.
  • برای انجام این پروژه میتوانید از axios استفاده کنید.
  • شما تنها مجاز به اعمال تغییرات در فایل‌های useJWT.js، Login.jsx و Profile.jsx هستید.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته‌شده، پوشه‌ی src را زیپ کرده و آپلود کنید.

ارسال پاسخ برای این سؤال
در حال حاضر شما دسترسی ندارید.