پیش نوشت: این مقاله بسیار ابتدایی است، بنابراین اگر اطلاعات اولیه درمورد OAuth2 دارید، پیشنهاد میکنم از این مقاله بگذرید و نوشته های بعدی رو مطالعه کنید.
در اکثر سیستم هایی که به عنوان دولوپر پیاده می کنیم، منابع به دو بخش کلی تقسیم میشن.
نکته: منبع (Resource) به بخش های مختلف یه سیستم گفته میشه، که میتونه یک صفحه از یک سایت باشه یا یک API endpoint.
همونطور که گفتیم برای دسترسی به منابعی که عمومی نیستند باید کاربر شناسایی بشه، به طور کلی شناسایی کاربر در دو مرحله انجام میشه:
تا اینجا مشکلی نیست و همه چی داره خوب پیش میره. سیستم شما هم صفحات عمومی داره که نیازی به لاگین شدن کاربر ندارند، هم منابعی داره که کاربر باید شناسایی و اعتبارسنجی بشه.
اما سناریوی دیگه ای رو درنظر بگیرید. شما قصد دارید صفحه ای رو در سایت تون طراحی کنید که ده پست آخر اینستاگرام کاربر رو همراه با آمارهای لایک و کامنت نشون بده. دو نکته مهم رو باید درنظر بگیرید:
بیاید چند راهکاری که به ذهن مون میرسه رو بررسی کنیم:
اطلاعات کاربری (نام کاربری و رمزعبور) که کاربر در سایت ما ثبت کرده، همان اطلاعات کاربری هست که برای اینستاگرام استفاده میکنه. درنتیجه بعد از اینکه که کاربر در سایت ما لاگین شد، ما همین اطلاعات کاربری رو برای API اینستاگرام میفرستیم و توکنی که نشان دهنده مجوز ما هست رو دریافت میکنم. درنهایت از این مرحله به بعد سیستم ما به عنوان کلاینت به تمامی منابعی که کاربر واقعی از Protected Resources دراختیار داره، دسترسی داره.
شرط اصلی استفاده از این روش این است که User Credential در هر دو سرویس یکسان باشد، که حداقل میتونیم مطمئن باشیم در سناریوی ما احتمال وقوع این شرط یک در صد هزاره. اما عیب اصلی این راهکار اینه که کلاینت هویت کاربر را جعل کرده، به این معنی که Protected Resource هیچ راهی نداره که تعیین کنه آیا واقعا کاربر این پروسه Authentication رو انجام داده یا کلاینت؟
و مشکل بعدی اینه که کلاینت بعد از گرفتن توکن، به تمامی دسترسی های تعیین شده برای کاربر دسترسی داره، که این موضوع از همان جعل هویت نشأت می گیره.
همانطور که گفته شد، اگر کاربر Credential متفاوتی در دو سیستم داشته باشد، راه حل اول قابل پیاده سازی نیست. درواقع راه حل دوم برای رفع این مشکل معرفی میشه. در این راهکار کلاینت اطلاعات کاربری اینستاگرام رو از کاربر درخواست میکنه و به اینستاگرام درخواست لاگینش رو میفرسته و توکن رو دریافت میکنه. به نظر راه حل منطقی تری میاد اما مشکل امنیتی بزرگی در این راه حل وجود داره.
کاربر اطلاعات کاربری اینستاگرام خودش رو دراختیار شما قرار میده! و این یعنی احتمال سوء استفاده از اطلاعات محرمانه کاربر 100 درصد افزایش پیدا کرده. کلاینت میتونه اطلاعات کاربری وارد شده رو در جایی ذخیره کنه و بعدها از آنها استفاده کنه. نکته مهم تر اینه که کاربر شما هیچ راهی برای محافطت از اطلاعات اینستاگرامش نداره، مگر اینکه پسورد اینستاگرام رو تغییر بده!
اما هنوز به عمق فاجعه نرسیدیم. موضوع وقتی خطرناک تر میشه که ما برای سیستم های مختلف خودمون از یک پسورد مشترک استفاده می کنیم. درنتیجه در سیستمی که ما داریم طراحی می کنیم نه تنها پسورد اینستاگرام کاربر رو داریم، بلکه میتونیم این پسورد رو در سیستم های دیگه مثل ایمیل، سیستم بانکی و ... هم چک کنیم (شاید کاربر ما جز اون دسته از آدم هاست که یه رمز برای همه سرویس هاشون دارند.) ?
در این روش مشکل جعل هویت راهکار قبلی رو هنوز داریم. به این معنی که Protected Resource نمیتونه متوجه بشه که کاربر واقعی لاگین کرده یا لاگین از طرف کلاینت صورت گرفته.
جالبه بدونید که قبل از معرفی OAuth از این روش استفاده میشده.
در این روش اینستاگرام به کلاینت یه API Key اختصاص میده، هر زمان که کلاینت قصد داشت آخرین پست های یک کاربر رو از اینستاگرام دریافت کنه، این API Key رو همراه درخواست میفرسته. اگر کلید فرستاده شده نشان دهنده یک کلاینت معتبر بود، اینستاگرام درخواست رو پاسخ میده، درغیر این صورت کلاینت اخطار Forbidden دریافت میکنه.
عیب اصلی این روش اینه که کلاینت اعتبارسنجی میشه، نه کاربر! بنابراین کلاینت میتونه حتی اطلاعات کاربرهایی که در سیستم ثبت نام نکرده اند رو از اینستاگرام درخواست کنه.
نکته: درسته که این روش برای سناریوی مدنظر ما کاملا اشتباهه، اما کاربرد خودش رو داره و در سیستم هایی مثل Google map و ... استفاده میشه.
در این روش اینستاگرام توکن رو برای کاربر تولید میکنه، نه کلاینت. درنتیجه هر کاربر که بخواد از سرویس ما استفاده کنه باید از اینستاگرام یه توکن دریافت کنه و این توکن رو جایی در تنظیمات سرویس ما قرار بده. این روش، عیب اصلی روش قبل رو کاملا برطرف کرده. اما هنوز یه مشکلی وجود داره. مدیریت کردن این توکن به عهده کاربره. کاربر باید چندین مرحله پیش بره تا سرویسی رو دریافت کنه. این مراحل به شرح زیره:
حالا فرض کنید توکن های اینستاگرام اعتبار یک ماهه دارند. این یعنی اینکه کاربر هر ماه مجبوره سه مرحله بالا رو تکرار کنه که هم وقت گیره و هم برای بعضی کاربرها پیچیده.
دقیقا تو این مرحله OAuth معرفی شد که نسخه نهایی آن OAuth2 رو با هم بررسی می کنیم.
پروتکل OAuth2 یه پروتکل امنیتی است که به یک سرویس third-party (مثل وب سایت ما) اجازه میده از یک سرویس دیگه (مثل اینستاگرام) دسترسی محدودی رو از جانب یک کاربر دریافت بکنه.
فرآیند زیر فهم OAuth2 رو آسون تر میکنه:
در صفحه ای از وب سایت مون قراره 10 پست آخر کاربر Authenticate شده رو به همراه جزییات نشون بدیم. براساس پروتکل OAuth2 وب سایت ما درخواست دریافت توکن رو برای اینستاگرام ارسال میکنه و بلافاصله کاربر به صفحه لاگین اینستاگرام ریدایرکت میشه. در این بخش بعد از لاگین کردن، اینستاگرام از کاربر اجازه میخواد که مجوزی مبنی بر مشاهده پست ها و جزییات رو به کلاینت اعطا کنه. اگه کاربر در این مرحله دکمه لغو رو فشار بده، پروسه تموم میشه و هیچ مجوزی به کلاینت (وب سایت ما) داده نمیشه. درغیر این صورت اینستاگرام توکنی به نام access_token میسازه که مجوزهای اعطا شده به کلاینت رو درون خودش داره. کلاینت موظفه این توکن رو درجایی ذخیره کنه تا برای درخواست های بعدی به اینستاگرام کاربر از اون استفاده کنه.
این پروتکل از چندین کامپوننت تشکیل شده که هر کدام از این کامپوننت ها یه نقشی رو برعهده دارند، اما در انتها، این ارتباط منجر به واگذاری دسترسی کاربر از یک سرویس به سرویس دیگه میشه که اصطلاحا به آن Delegating access گفته میشه.
در نمودار زیر این دو مرحله رو با جزئیات بیشتری توضیح میدیم.
1. در مرحله اول کاربر به کلاینت اعلام میکنه که قصد داره با سرویس مدنظر که اینجا اینستاگرام هست لاگین کنه.
2. کلاینت کاربر رو به صفحه لاگین سرویس اصلی ریدایرکت میکنه. در اون صفحه کاربر علاوه بر لاگین کردن باید به Authorization Server اعلام کنه که قصد داره یه سری از دسترسی ها رو به کلاینت موردنظر واگذار کنه. (اینجوری کلاینت دسترسی کامل به اطلاعات کاربر نداره و فقط دسترسی هایی رو دریافت میکنه که با هم توافق کرده باشند. برای مثال در سناریوی ما کلاینت تنها دسترسی خواندن پست های کاربر رو نیاز داره، پس زمانی که میخواد درخواست بده نیازی نداره که مجوز بخش های دیگه مثل Setting اینستاگرام رو بخواد.)
3. بعد از تایید کاربر، Authorization Server اطلاعات کاربر که همون نام کاربری و رمزعبور هست رو بررسی میکنه و اگه اطلاعات معتبر بود، access_token رو میسازه و به کلاینت ریدایرکت میکنه.
4. کلاینت باید این access_token رو در جایی ذخیره کنه.
5. از این مرحله به بعد هر زمانی که کلاینت قصد داشت به اینستاگرام درخواست بزنه (مثلا درخواست گرفتن ده پست آخر) باید همراه با درخواستش access_token رو ارسال کنه.
6. اینستاگرام اعتبار access_token دریافتی رو به کمک Authorization Server بررسی میکنه و اگه مشکلی وجود نداشت، درخواست کلاینت رو پردازش میکنه و جواب رو به کلاینت میفرسته.
خلاصه: اگه خاطرتون باشه، هدف اصلی سناریو این بود که سرویس ما آخرین پست های کاربر رو بهش نشون بده. برای این کار ما نیاز داریم که کاربر سرویس ما رو که اینجا نقش Client رو برعهده داره به عنوان یک سرویس معتبر به اینستاگرام که نقش Authorization Server رو برعهده داره معرفی کنه. درنهایت کلاینت به ازای کاربر یک توکن دریافت می کند که نشان دهنده مجوز واگذاری شده به کلاینت توسط کاربر هست. از این جا به بعد هر زمان که سرویس ما (Client) به منبع محافظت شده ای از اینستاگرام (Protected Resource) نیاز داشت با فرستادن این توکن نشون میده که Resource Owner مجوز لازم برای این کار رو به سرویس ما واگذار کرده.
پروسه ای که توضیح داده شد، یکی از ساده ترین Flow ها در پروتکل OAuth2 هست. درکل چهار Flow یا اصطلاحا Grant type در OAuth2 وجود داره که در مقاله بعدی درموردشون توضیح کاملی داده میشه.
درمورد توکن ها این رو بدونید که در پروتکل OAuth2 تنها توکن موجود access_token نیست و توکن دیگه ای به نام refresh_token وجود داره که در مقاله بعدی درمورد اون هم صحبت میشه.
OAuth2 in Action 1st edition. Manning Publications Co.