راهنمای جامع و گامبهگام یکپارچهسازی کلاینتها با SSO بر اساس استاندارد OAuth 2
در دنیای توسعه نرمافزار، مدیریت احراز هویت (Authentication) و مجوز دسترسی (Authorization) به طور مستقیم بر تجربه کاربری و امنیت سیستم تأثیر میگذارد. یکی از روشهای مرسوم برای سادهسازی فرآیند ورود کاربران و حفظ امنیت، استفاده از SSO (Single Sign-On) بر اساس استاندارد OAuth 2 است. در این راهنما، با تمرکز ویژه بر “ریدایرکت URL” (Redirect URL)، به صورت گامبهگام با مفاهیم اصلی و نحوه پیادهسازی یکپارچهسازی کلاینتها با SSO مبتنی بر OAuth 2 آشنا میشویم.
1. مقدمهای بر SSO و OAuth 2
SSO (Single Sign-On) روشی است که به کاربران اجازه میدهد تنها با یک بار ورود به سیستم احراز هویت، بتوانند به سرویسها و نرمافزارهای مختلف دسترسی پیدا کنند. در نتیجه نیازی به ورود مجدد اطلاعات احراز هویت در هر سرویس نیست و این امر سبب تجربه کاربری بهتر و امنیت بالاتر میشود.
OAuth 2 یک چارچوب استاندارد برای صدور مجوز (Authorization) است که در آن یک Authorization Server یا Identity Provider وظیفه تأیید هویت کاربر و صدور توکن دسترسی را بر عهده دارد. سرویسهای گیرنده (Client) با استفاده از توکن دسترسی میتوانند به منابع کاربر در سرورهای مختلف دسترسی پیدا کنند.
2. مفاهیم اصلی در OAuth 2
- Resource Owner (مالک منبع): کاربری است که مالک منابع محافظتشده (Protected Resources) به حساب میآید.
- Client (کلاینت): برنامه یا سرویس ثالثی است که قصد دسترسی به منابع محافظتشده را دارد.
- Authorization Server (سرور احراز هویت): نهادی است که هویت کاربر را تأیید و توکن دسترسی صادر میکند.
- Resource Server (سرور منبع): سرویسی که دادهها یا منابع کاربر را در اختیار دارد و تنها با ارائه توکن دسترسی معتبر اجازهی دسترسی میدهد.
- Redirect URL (ریدایرکت URL): آدرسی که کاربر پس از تأیید هویت یا رد آن، توسط سرور احراز هویت به آن بازگردانده میشود. این آدرس نقش کلیدی در احراز هویت دارد و یکی از موضوعات مهم در پیکربندی OAuth 2 است.
3. اهمیت ریدایرکت URL در OAuth 2
“ریدایرکت URL” یکی از اصلیترین قسمتهای مکانیزم OAuth 2 به شمار میرود. چرا که پس از انجام مراحل احراز هویت، سرور احراز هویت نتیجه (مثبت یا منفی) را به کلاینت بازمیگرداند و این بازگشت معمولاً از طریق یک ریدایرکت URL انجام میشود.
- نقش امنیتی: با تعیین دقیق ریدایرکت URL، از حملات فیشینگ و ریدایرکتهای ناخواسته جلوگیری میشود.
- نقش عملکردی: اگر ریدایرکت URL درست پیکربندی نشود، ممکن است کاربر پس از ورود، به صفحهای اشتباه منتقل شود یا مراحل احراز هویت ناتمام بماند.
- منعطف در انواع گرنتها (Grant Types): بسته به نوع گرنت مورداستفاده (Authorization Code، Implicit، Password، Client Credentials)، ریدایرکت URL میتواند متفاوت باشد یا در برخی موارد ضروری نباشد. با این حال، در سناریوهای پرکاربرد نظیر Authorization Code و Implicit، نقش کلیدی دارد.
4. نحوه تنظیم صحیح ریدایرکت URL برای کلاینتها
در یکپارچهسازی SSO با OAuth 2، هر کلاینت باید آدرس (یا آدرسهای) ریدایرکت URL خود را در سرور احراز هویت بهصورت دقیق ثبت کند. این تنظیمات معمولاً در کنسول مدیریت سرور احراز هویت (به عنوان نمونه Keycloak، Auth0 یا Okta) قابل انجام است.
مراحل کلی تنظیم ریدایرکت URL:
-
ثبت آدرس در سرور احراز هویت
- آدرس (یا آدرسهای) ریدایرکت معتبر را در بخش تنظیمات کلاینت ثبت کنید. برای مثال:
https://myapp.com/auth/callback
https://myapp.com/auth/sso-login
-
- برخی از ارائهدهندگان امکان استفاده از Wildcard را میدهند. اما این موضوع باید با احتیاط انجام شود تا ریسک امنیتی به حداقل برسد.
-
تنظیم کلاینت برای ارسال پارامترهای صحیح
- در Authorization Code Flow، کلاینت پس از هدایت کاربر به سرور احراز هویت، باید پارامتر
redirect_uri
را ارسال کند و مقدار آن باید دقیقاً با آدرسی که در سرور احراز هویت ثبت شده است، مطابقت داشته باشد.
- در Authorization Code Flow، کلاینت پس از هدایت کاربر به سرور احراز هویت، باید پارامتر
-
بررسی پروتکل و پورت
- آدرس ریدایرکت (HTTP یا HTTPS) و شماره پورت (80 یا 443 یا هر پورت سفارشی) باید دقیق باشد. هرگونه مغایرت در پروتکل یا پورت، اعتبار نشانی ریدایرکت را زیر سؤال میبرد و ممکن است باعث خطا شود.
5. چالشها و مشکلات رایج مرتبط با ریدایرکت URL
-
عدم تطابق دقیق آدرس (Exact Match)
- اگر حتی یک کاراکتر آدرس ثبتشده با آدرس درخواستی متفاوت باشد، سرور احراز هویت درخواست را رد میکند. برای مثال عدم تطابق
www.example.com
باexample.com
یا/callback/
با/callback
.
- اگر حتی یک کاراکتر آدرس ثبتشده با آدرس درخواستی متفاوت باشد، سرور احراز هویت درخواست را رد میکند. برای مثال عدم تطابق
-
استفاده از Wildcard به شکل ناایمن
- ثبت ریدایرکت URL با Wildcard (مثلاً
https://*.example.com/*
) میتواند خطرناک باشد، زیرا مهاجمان با زیر دامنهی جعلی میتوانند توکنهای کاربر را رهگیری کنند.
- ثبت ریدایرکت URL با Wildcard (مثلاً
-
فقدان HTTPS
- استفاده از HTTP به جای HTTPS میتواند منجر به حملات سرقت توکن شود (Man-in-the-Middle). توصیه میشود همیشه از HTTPS برای ریدایرکت URL استفاده کنید.
-
پیکربندی اشتباه چندین ریدایرکت
- برخی کلاینتها چندین نقطه بازگشت (ریدایرکت) دارند و عدم مدیریت درست این آدرسها ممکن است اختلال ایجاد کند.
-
مدیریت خطا در بازگشت
- در صورت عدم موفقیت در احراز هویت (مثلاً اگر کاربر رد شود یا خطایی رخ دهد)، سرور احراز هویت به آدرس خطای ثبتشده در ریدایرکت URL برمیگردد. عدم مدیریت درست این وضعیت باعث سردرگمی کاربر میشود.
6. نمونههای کدنویسی و کاربردی برای درک بهتر
برای درک بهتر نقش ریدایرکت URL، مثالی ساده در فریمورک Node.js با استفاده از کتابخانه passport را ارائه میدهیم. فرض کنیم در سرور احراز هویت، آدرس https://myapp.com/auth/callback
بهعنوان ریدایرکت URL ثبت شده است:
// app.js
const express = require('express');
const passport = require('passport');
const session = require('express-session');
const app = express();
app.use(session({ secret: 'YOUR_SECRET_KEY', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());
// مسیر لاگین با OAuth
app.get('/auth/oauth2', passport.authenticate('oauth2'));
// مسیر بازگشت (ریدایرکت URL)
app.get('/auth/callback', passport.authenticate('oauth2', {
failureRedirect: '/auth/failure'
}), (req, res) => {
// موفقیت در احراز هویت
res.redirect('/profile');
});
// صفحه پروفایل
app.get('/profile', (req, res) => {
if (!req.isAuthenticated()) {
return res.redirect('/auth/oauth2');
}
res.send(`خوش آمدید ${req.user.displayName}`);
});
// صفحه خطا
app.get('/auth/failure', (req, res) => {
res.send('احراز هویت ناموفق بود!');
});
app.listen(3000, () => {
console.log('Server started on http://localhost:3000');
});
8. جمعبندی
یکپارچهسازی کلاینتها با SSO بر اساس OAuth 2 به دلیل افزایش امنیت، تجربه کاربری بهتر و کاهش هزینههای نگهداری سیستم، در حال گسترش است. “ریدایرکت URL” بهعنوان بخش حیاتی در فرآیند احراز هویت، میتواند عملکرد صحیح سیستم را تضمین یا بهدلیل پیکربندی نامناسب، آن را با چالش مواجه کند. در این مقاله، با مفاهیم اصلی OAuth 2، ساختار ریدایرکت URL، نحوه پیکربندی آن در کلاینت و سرور احراز هویت، چالشهای رایج و روشهای پیشگیری از مشکلات احتمالی آشنا شدیم.
در ادامه یک نمونه کدنویسی ساده با استفاده از Node.js و فریمورک Express ارائه شده که در آن:
- کد (Authorization Code) از سرور احراز هویت دریافت میشود.
- کد دریافتشده با فراخوانی سرویس Token Endpoint به توکن دسترسی (Access Token) تبدیل میشود.
- سپس با موفقیتآمیز بودن دریافت توکن، کاربر به یک مسیر (ریدایرکت URL) مشخص هدایت میگردد.
نکته: مقادیر سرور احراز هویت (نظیر
https://AUTH-SERVER/token
)،YOUR_CLIENT_ID
،YOUR_CLIENT_SECRET
، وYOUR_REDIRECT_URI
باید مطابق با تنظیمات واقعی شما جایگزین شوند.
/**
* یک نمونه ساده از پیادهسازی OAuth 2 Authorization Code Flow
* جهت دریافت کد و تبدیل آن به توکن، سپس ریدایرکت کاربر
*/
const express = require('express');
const axios = require('axios');
const session = require('express-session');
const app = express();
// تنظیمات سشن برای ذخیره توکنها
app.use(
session({
secret: 'YOUR_SESSION_SECRET',
resave: false,
saveUninitialized: false,
})
);
/**
* مسیر شروع احراز هویت:
* هدایت کاربر به صفحه لاگین سرور احراز هویت (Authorization Server)
* اینجا معمولا از یک URL شبیه به:
* https://AUTH-SERVER/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI
* استفاده میشود.
*/
app.get('/auth/login', (req, res) => {
const authURL = `https://AUTH-SERVER/authorize?` +
`response_type=code&` +
`client_id=YOUR_CLIENT_ID&` +
`redirect_uri=${encodeURIComponent('https://YOUR-REDIRECT-URI/auth/callback')}&` +
`scope=openid%20profile`;
res.redirect(authURL);
});
/**
* مسیر بازگشت (Callback) از سرور احراز هویت:
* در این مرحله، سرور احراز هویت یک کد (Authorization Code) را به این مسیر ارسال میکند.
* ما این کد را دریافت و با استفاده از متد POST به /token برای دریافت Access Token درخواست میزنیم.
*/
app.get('/auth/callback', async (req, res) => {
const { code } = req.query;
if (!code) {
return res.redirect('/auth/failure');
}
try {
// درخواست به سرور احراز هویت برای تبدیل Code به Access Token
const tokenResponse = await axios.post('https://AUTH-SERVER/token', null, {
params: {
grant_type: 'authorization_code',
code: code,
redirect_uri: 'https://YOUR-REDIRECT-URI/auth/callback',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
},
});
// در اینجا میتوانید توکنها را (access_token و refresh_token و ...) در سشن یا دیتابیس ذخیره کنید
req.session.accessToken = tokenResponse.data.access_token;
req.session.refreshToken = tokenResponse.data.refresh_token;
// ریدایرکت کاربر به صفحه پروفایل (یا هر مسیر دلخواه)
res.redirect('/profile');
} catch (error) {
console.error('Error exchanging code for token:', error);
res.redirect('/auth/failure');
}
});
/**
* مسیر نمایشی (مثلا پروفایل) که نیازمند لاگین است
* در اینجا صرفا بررسی میکنیم که آیا توکن داریم یا خیر
*/
app.get('/profile', (req, res) => {
if (!req.session.accessToken) {
return res.redirect('/auth/login');
}
// در اینجا میتوانید با توکن از سرور Resource Server اطلاعات کاربر را بگیرید
res.send('شما با موفقیت احراز هویت شدهاید و در این صفحه قرار دارید!');
});
/**
* مسیر شکست احراز هویت
*/
app.get('/auth/failure', (req, res) => {
res.send('فرآیند احراز هویت با خطا مواجه شد!');
});
// راهاندازی سرور
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
نکات کلیدی:
- درخواست دریافت کد
- ابتدا کاربر را به مسیر
/auth/login
هدایت میکنیم. این مسیر کاربر را به آدرس سرور احراز هویت (آدرس/authorize
) ریدایرکت کرده و در پارامترهاclient_id
،redirect_uri
، وscope
را ارسال میکند.
- ابتدا کاربر را به مسیر
- دریافت کد در Callback
- سرور احراز هویت پس از تأیید کاربر (لاگین موفق) یک Authorization Code را به مسیر
redirect_uri
که قبلاً تعیین کردهایم (مثلاً/auth/callback
) ارسال میکند.
- سرور احراز هویت پس از تأیید کاربر (لاگین موفق) یک Authorization Code را به مسیر
- تبدیل کد به توکن
- در این مرحله با فراخوانی آدرس
/token
سرور احراز هویت (متدPOST
) و ارسالgrant_type=authorization_code
,client_id
,client_secret
, وredirect_uri
، توکنهای لازم (Access Token و در صورت نیاز Refresh Token) را دریافت میکنیم.
- در این مرحله با فراخوانی آدرس
- ذخیره و استفاده از توکن
- میتوانید توکن دریافت شده را در session، کوکی یا دیتابیس ذخیره کنید تا در درخواستهای بعدی برای دسترسی به منابع محافظتشده استفاده شود.
- ریدایرکت به مسیر دلخواه
- در نهایت برای تجربه کاربری بهتر، کاربر را به صفحهای مانند
/profile
هدایت میکنیم.
- در نهایت برای تجربه کاربری بهتر، کاربر را به صفحهای مانند
این ساختار، قالب اصلی Authorization Code Flow را نشان میدهد. با سفارشیسازی و افزودن بخشهای امنیتی (نظیر بررسی استیت، PKCE برای اپلیکیشنهای عمومی و …)، میتوانید فرآیند احراز هویت را قدرتمندتر و امنتر کنید.