احتمالا دیدهاید که در خیلی از وبسایتها امکان لاگین با حساب گوگل وجود دارد. در این مطلب میخواهیم بررسی کنیم که وقتی با گوگل در یک وبسایت لاگین میکنید در پشت پرده چه اتفاقی رخ میدهد. این مطلب برای برنامه نویس ها و افرادی که علاقه دارند جزئیات OAuth رو بدونن کاربرد داره. دونستن عملکرد وب و پروتکل http هم لازم هست برای درک مطلب.
در سال ۲۰۱۰، OAuth نسخه یک معرفی شد. دو سال بعد به دلیل مشکلات امنیتی آن، OAuth2 جایگزین OAuth1 شد.
حال سناریویی را بررسی میکنیم تا درک کنیم که قبل از OAuth فرایند به چه صورت بوده و OAuth چگونه مسئله را حل کرده است.
فرض کنید یک سرویس ابری اشتراک عکس وجود دارد که میتوانیم عکسهایمان را بر روی آن ذخیره و مدیریت کنیم. در پروتکل OAuth به این سرویس، Resource Server گفته میشود. منابع، که در اینجا همان عکسها میباشند، بر روی این سرور ذخیره و مدیریت میشوند.
سرویس مذکور ابتدا کاربر را احراز هویت کرده و سپس به او اجازه مدیریت عکسها را میدهد. در OAuth به کاربران مالک منبع یا Resource Owner گفته میشود.
تا اینجا با دو موجودیت پروتکل یعنی resource server و resource owner آشنا شدیم.
حال فرض کنیم یک سرویس پرینت عکس وجود دارد که یک شرکت دیگر آن را تولید کرده است. مسئله این است که کاربر (Resource Owner) می خواهد به سرویس پرینت دسترسی بدهد، به طوری که بتواند عکسهای او را از سرویس اشتراک عکس دریافت کرده و سپس آنها را پرینت بگیرد. این نکته رو هم بگم که در ادبیات OAuth، به سرویس پرینت که مثال زدیم، Client گفته میشود.
پس تا اینجا به سرویس اشتراک عکس، Resource Server گفتیم؛ سرویس پرینت را Client نامیدیم و به کاربر واژه Resource Owner را نسبت دادیم. به طور مکرر از این واژه ها در متن استفاده شده.
اولین راهکاری که برای حل سناریو به ذهن میرسد این است که کاربر، نام کاربری و کلمه عبور خود را در اختیار سرویس پرینت عکس قرار دهد. بنابراین سرویس پرینت خواهد توانست تا از طرف کاربر روی Resource Serverلاگین نموده و عکسها را دریافت کند.
این روش مشکلاتی دارد:
به طور کلی از نظر امنیتی صحیح نیست که نام کاربری و کلمه عبور (credential) در اختیار غیر قرار گیرد. در نتیجه این روش منتفی خواهد بود.
حال به بررسی راهکار OAuth در این مسئله میپردازیم.
همانطور که در شکل قابل ملاحظه است، در OAuth یک موجودیت جدید به نام Authorization Server اضافه شده است. به طور مثال، گوگل میتواند این نقش را داشته باشد. این سرور کار مدیریت و احراز هویت کاربران را انجام میدهد. وظیفه دیگر این سرور این است که پس از کسب اجازه از کاربر، برای کلاینتها توکن دسترسی یا access token صادر نماید.
برای اینکه کلاینت بتواند از Resource Server عکس را دریافت کند، باید access token معتبر ارائه دهد. اکسس توکن را authorization server تولید میکند. اما با چه شرایطی؟ به روشی مالک منبع یا کاربر باید اجازه دهد که توکن برای کلاینت صادر شود.
در شکل فوق جریان یا flow کلی پروتکل OAuth ارائه شده است. در مرحله اول کلاینت میبایست با روشی اجازه Resource Owner را بگیرد. به این اجازه Authorization Grant گفته میشود.
اگر Resource Owner رضایت داشت به کلاینت اجازه یا Grant را میدهد. کلاینت این authorization grant را به authorization server ارائه داده و اکسس توکن دریافت میکند. سپس کلاینت با ارائه اکسس توکن به ریسورس سرور، به منابع دسترسی پیدا خواهد کرد.
همانطور که توضیح داده شد، در OAuth دیگر Resource Owner پسورد خود را در اختیار کلاینت قرار نمیدهد. بلکه به او یک Authorization Grant میدهد.
نکته مثبتی که در اینجا قابل ذکر است، این است که این امکان وجود دارد که مالک منبع در grant مشخص کند که کلاینت به چه چیزهایی دسترسی داشته باشد. در واقع scope اکسس توکن قابل تعیین است. Resource Server تنها اجازه دسترسی به Scope های موجود در اکسس توکن را خواهد داد و نه بیشتر. به طور مثال مالک منبع میتونه فقط دسترسی خواندن (مثلا read scope) به کلاینت بدهد؛ لذا کلاینت با آن اکسس توکن فقط می تواند عکس ها را بخواند و اجازه ویرایش و حذف نخواهد داشت.
گفتیم که کلاینت باید به روشی از مالک منبع اجازه (grant) بگیرد. اما نگفتیم چگونه! در ادامه به انواع مختلف Grant در OAuth میپردازیم. در OAuth چهار نوع Grant تعریف شده است. لذا کلاینت به چهار روش مختلف میتواند از مالک منبع، اجازه کسب نماید.
در این نوع Grant، کلاینت با وساطت Authorization Server از مالک منبع اجازه را میگیرد. به این صورت که ابتدا کلاینت مالک منبع را به authorization server هدایت (redirect) میکند؛ در authorization server، مالک منبع ابتدا احراز هویت شده و سپس اجازه میدهد که کلاینت به ریسورسها دسترسی داشته باشد. پس از آن Authorization Server یک کد (Authorization Code) به کلاینت ارائه میدهد. کلاینت با آن کد میتواند از Authorization Server اکسس توکن را دریافت نماید. این grant امنترین grant در OAuth بوده و توصیه شده است که در صورت محیا بودن شرایط، از آن استفاده شود.
این نوع Grant بسیار مشابه نوع اول است، با این تفاوت که به جای ارسال کد به کلاینت، به طور مستقیم اکسس توکن در اختیار کلاینت قرار داده میشود. امنیت این روش پایین تر بوده و در سناریوهای خاصی استفاده میشود. در ادامه بیشتر در مورد این Grant توضیح داده خواهد شد.
این Grant شبیه روش ارائه شده در ابتدای مطلب (قبل OAuth) است. به این صورت که کلاینت نام کاربری و کلمه عبور مالک منبع را گرفته و با آن از authorization server اکسس توکن دریافت میکند. در واقع مالک منبع با ارائه نام کاربری و کلمه عبور خود به کلاینت، اجازه خود را اعلام مینماید.
در این نوع Grant مالک منبع وجود ندارد و خود کلاینت در واقع نقش مالک منبع را دارد. لذا کلاینت با نام کاربری و کلمه عبور خود، اکسس توکن را دریافت میکند.
لازم به ذکر است که grant ها قابل توسعه یا extend هست. یعنی میتوان OAuthرا مبنا قرار داده و Grant های جدید به آن اضافه نمود. به عنوان مثال پروتکلهای openid connect (OIDC) یا User Managerd Access (UMA)، در واقع یک extension بر روی OAuth میباشند.
در ادامه با جزئیات بیشتر به موضوع Grant ها پرداخته خواهد شد. پیش از آن لازم است انواع کلاینتهای OAuthرا بشناسیم.
کلاینتهای OAuth به دو نوع Confidential و Publicتقسیم میشوند.
امکان ذخیره امن نام کاربری و کلمه عبور خود را دارند. به عنوان مثال اپلیکیشن تحت وب سمت سرور که میتواند Credentials را به صورت امن روی سرور ذخیره کند. به نحوی که امکان دسترسی غیرمجاز به آن وجود نداشته باشد.
امکان ذخیره امن Credentials خود را ندارند. مانند اپلیکیشنهایی که به صورت کامل سمت مرورگر اجرا میشوند (مثل فریمورکهای جاوا اسکریپتی، انگولار، ری اکت و ...) یا اپلیکیشنهای موبایلی. این نوع کلاینتها از آنجایی که در دستگاه کاربر اجرا میشوند، عملاً امکان دسترسی به تمامی دادههای آنها سمت کاربر وجود دارد و قادر به ذخیره امن پسورد نخواهند بود.
مراحل کلی اخذ اجازه از کاربر به روش Authorization Code در شکل زیر نمایش داده شده است.
در ادامه درخواست و پاسخهای HTTP که بین مرورگر کاربر، کلاینت و authorization server مبادله میشود به تفصیل شرح داده شده است. طی 9 مرحله زیر اکسس توکن در اختیار کلاینت قرار داده شده و با آن میتواند به سرویس های resource serverدسترسی پیدا کند.
در درخواستها و پاسخهای HTTP که در ادامه آمده است، رشتههای طولانی و موارد غیر مهم با "..." خلاصه شده است.
مرحله یک: فرض کنید مالک منبع پشت لپ تاپ خود نشسته است و از طریق یک مرورگر مانند گوگل کروم یا فایرفاکس (User Agent) وب سایت print-service.com (کلاینت) را باز مینماید. لذا یک درخواست HTTP مشابه زیر توسط مرورگر ایجاد شده و به کلاینت ارسال میشود.
GET /print/2 HTTP/1.1 Host: print-service.com …
مرحله دو: کلاینت درخواست را بررسی نموده و متوجه میشود که اکسس توکن معتبری از کاربر ندارد. لذا با ارسال یک http response با کد 301، مرورگر کاربر را به Authorization Serviceهدایت میکند.
HTTP/1.1 302 Location:http://auth-server.com/auth?response_type=code&client_id=print-service&scope=read&redirect_uri=http://print-service.com/login/OAuth2/code/test
در این ریدایرکت پارامترهای زیر به Authorization Server ارسال میشود:
برای تعیین نوع Grant به کار می رود. عبارت code به معنای Authorization Code Grant می باشد.
برای این که Authorization Server متوجه شود که درخواست از کدام کلاینت آمده است. لازم به ذکر هست که کلاینت باید از قبل در Authorization Server ثبت نام کرده باشد و client_id منحصر بفرد گرفته باشد.
آدرسی است که بعد از احراز هویت و کسب اجازه از کاربر، Authorization Server مرورگر کاربر را به آن ریدایرکت خواهد نمود. این پارامتر میبایست از قبل در Authorization Server برای کلاینت ثبت شده باشد.
همانطور که پیشتر گفته شد، پارامتر scope به منظور تعیین محدوده دسترسی است. در اینجا کلاینت در درخواست خود عنوان میکند که به چه دسترسیهایی نیاز دارد.
سوال: کلاینت از کجا متوجه میشود که اکسس توکن معتبری از کاربر ندارد؟
پاسخ: پروتکل OAuth در این مورد صحبتی نکرده است و تصمیمگیری بر عهده کلاینت است. معمولاً کلاینتها اطلاعات Session کاربر را چک میکنند و در صورتی که اکسس توکن معتبر در Session کاربر وجود نداشته باشد، ریدایرکت به Authorization Server را انجام میدهند.
مرحله سه: با دریافت 302، مرورگر یک درخواست GET به Location دریافتی ارسال مینماید.
GET /auth?response_type=code&client_id=print-service&scope=read&redirect_uri=http://print-service.com/login/OAuth2/code/test HTTP/1.1 Host: auth-server.com
مرحله چهار: به عنوان پاسخ، Authorization Server یک فرم لاگین به کاربر بر میگرداند. لازم به ذکر است که Authorization Server میتواند به روش دلخواه خود، کاربر یا مالک منبع را احراز هویت کند و از او اجازه بگیرد و پروتکل OAuth در این زمینه سکوت کرده است.
HTTP/1.1 200 OK Content-Type: text/html <html> <body> <form>…</form> </body> </html>
مرحله پنج: مالک منبع، نام کاربری و کلمه عبور را وارد کرده و بر روی دکمه ورود کلیک میکند. لذا درخواست زیر تولید شده و به Authorization Server ارسال میشود.
POST /authenticate HTTP/1.1 Host: auth-server.com Content-Type: application/x-www-form-urlencoded username=user1&password=1234
مرحله شش: Authorization Server نام کاربری و کلمه عبور را بررسی نموده و در صورت تایید مرورگر کاربر را به کلاینت ریدایرکت میکند.
HTTP/1.1 302 Found Location: http://print-service.com/login/OAuth2/code/test?code=493...
در پاسخ ملاحظه میکنید که کد به عنوان یک URL Parameter به مرورگر کاربر ارسال شده است. لازم به ذکر است که کد باید رشتهای باشد که غیر قابل حدس زدن باشد. در اینجا رشته به 493 خلاصه شده است.
مرحله هفت: حال مرورگر کار با دریافت پاسخ ۳۰۱، یک در خواست get تولید نموده و به آدرس درج شده در هدر location ارسال میکند.
GET /login/OAuth2/code/test?code=493... HTTP/1.1 Host: print-service.com
همانطور که ملاحظه نمودید، به این روش Authorization Code به کلاینت منتقل شد. در نتیجه کلاینت با استفاده از این کد میتواند از Authorization Server اکسس توکن را دریافت نماید.
مرحله هشت: برای دریافت اکسس توکن با ارائه کد، درخواست زیر توسط کلاینت تولید شده و به Authorization Server و به Token Endpoint آن ارسال میشود.
POST /token HTTP/1.1 Host: auth-server.com Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=493…&redirect_uri=http://print-service.com/login/OAuth2/code/test&client_id=test&client_secret=pass
لازم به ذکر است که در این مرحله ضروری است کلاینت احراز هویت شود. لذا client_id و client_secret ای را که قبلاً از Authorization Server دریافت کرده است، در درخواست ارسال میکند. لازم به توضیح است که Authorization Code تنها برای کلاینتهای از نوع Confidential قابل استفاده است.
مرحله نه: در صورت معتبر بودن درخواست و نام کاربری و کلمه عبور کلاینت، Authorization Server در پاسخ اکسس توکن را به کلاینت ارائه میدهد.
HTTP/1.1 200 OK Content-Type: application/json { “access_token”: “eyJ...”, “expires_in”: 300, “refresh_expires_in”: 1800, “refresh_token”: “eaZ...”, “token_type”: “Bearer”, “scope”: “profile email” }
این گرنت مشابه Authorization Code هست و تنها یک مرحله از آن حذف شده است. در واقع به جای این که codeبه کلاینت ارسال شده و کلاینت با آن کد، اکسس توکن را دریافت کند، مستقیماً اکسس توکن به کلاینت داده خواهد شد.
این نوع گرنت برای کلاینت هایی طراحی شده که به طور کامل در سمت دستگاه اجرا میشوند. مانندکلاینت های جاوا اسکریپتی مثل Angular، react، vue.js و ….
لازم به ذکر است که استفاده از این نوع Grant به دلیل ضعف امنیتی نباید انجام شود. توصیه شده است به جای آن از Authorization Code همراه با PKCE استفاده شود. برای اطلاعات بیشتر در این خصوص به لینک https://OAuth.net/2/pkce مراجعه نمایید.
در این نوع Grant مالک منبع نام کاربری و کلمه عبور خود را در اختیار کلاینت قرار میدهد. زمانی که مالک منبع به کلاینت اعتماد کامل دارد این روش قابل استفاده است؛ به طور مثال در حالتی که Resource Serverو کلاینت هر دو توسط یک سازنده توسعه داده شده باشند.
در این نوع Grant، کلاینت همان مالک منبع است. لذا کلاینت با استفاده از Credentials خود (مانند client_id و client_secret) از Authorization Server اکسس توکن دریافت میکند.
در بخش زیر نکاتی در خصوص پروتکل OAuth2 ذکر شده است.
امیدوارم مطلب براتون مفید بوده باشه. اگر جایی اشکالی میبینید، مبهم هست یا نکته ای وجود داره ممنون میشم در کامنت ها عنوان کنید. سپاس از این که وقت گذاشتید.