# مستندات کامل سیستم OAuth 2.0 - Hesabix ## 📋 فهرست مطالب 1. [معرفی OAuth 2.0](#معرفی-oauth-20) 2. [معماری سیستم](#معماری-سیستم) 3. [بخش مدیریت](#بخش-مدیریت) 4. [بخش کاربری](#بخش-کاربری) 5. [API Documentation](#api-documentation) 6. [نحوه اتصال](#نحوه-اتصال) 7. [امنیت](#امنیت) 8. [مثال‌های عملی](#مثال‌های-عملی) 9. [عیب‌یابی](#عیب‌یابی) 10. [پشتیبانی](#پشتیبانی) --- ## 🚀 معرفی OAuth 2.0 OAuth 2.0 یک پروتکل استاندارد برای احراز هویت و مجوزدهی است که به برنامه‌های خارجی اجازه می‌دهد بدون نیاز به رمز عبور، به حساب کاربران دسترسی داشته باشند. ### مزایای OAuth 2.0: - ✅ **امنیت بالا:** عدم اشتراک‌گذاری رمز عبور - ✅ **کنترل دسترسی:** محدود کردن دسترسی‌ها با Scope - ✅ **قابلیت لغو:** امکان لغو دسترسی در هر زمان - ✅ **استاندارد:** سازگار با پروتکل‌های جهانی - ✅ **IP Whitelist:** کنترل دسترسی بر اساس IP - ✅ **Rate Limiting:** محدودیت تعداد درخواست --- ## 🏗️ معماری سیستم ### اجزای اصلی: ``` ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Client App │ │ OAuth Server │ │ Resource Owner │ │ (Third Party) │◄──►│ (Hesabix) │◄──►│ (User) │ └─────────────────┘ └─────────────────┘ └─────────────────┘ ``` ### جریان OAuth: 1. **Client Registration:** ثبت برنامه در بخش مدیریت 2. **Authorization Request:** درخواست مجوز از کاربر 3. **User Consent:** تأیید کاربر 4. **Authorization Code:** دریافت کد مجوز 5. **Token Exchange:** تبدیل کد به Access Token 6. **Resource Access:** دسترسی به منابع ### پایگاه داده: ```sql -- جداول OAuth oauth_application -- برنامه‌های ثبت شده oauth_scope -- محدوده‌های دسترسی oauth_authorization_code -- کدهای مجوز موقت oauth_access_token -- توکن‌های دسترسی ``` --- ## ⚙️ بخش مدیریت ### 1. دسترسی به بخش مدیریت ``` مدیریت سیستم → تنظیمات سیستم → تب "برنامه‌های OAuth" ``` ### 2. آمار کلی سیستم چهار کارت آمار نمایش می‌دهد: - **کل برنامه‌ها:** تعداد کل برنامه‌های ثبت شده - **فعال:** تعداد برنامه‌های فعال - **غیرفعال:** تعداد برنامه‌های غیرفعال - **جدید (7 روز):** برنامه‌های ایجاد شده در هفته گذشته ### 3. ایجاد برنامه جدید #### مراحل ایجاد: 1. **کلیک روی "ایجاد برنامه جدید"** 2. **پر کردن فرم:** - **نام برنامه:** نام منحصر به فرد برنامه - **توضیحات:** توضیح کاربرد برنامه - **آدرس وب‌سایت:** URL اصلی برنامه - **آدرس بازگشت:** URL callback برنامه - **محدودیت درخواست:** تعداد درخواست مجاز در ساعت #### تنظیمات امنیتی: ##### IP Whitelist: ``` - در صورت خالی بودن: از هر IP مجاز است - افزودن IP: 192.168.1.1 یا 192.168.1.0/24 - پشتیبانی از CIDR notation - Validation خودکار آدرس‌های IP ``` ##### Scope Management: ``` read_profile - دسترسی به اطلاعات پروفایل write_profile - تغییر اطلاعات پروفایل read_business - دسترسی به اطلاعات کسب و کار write_business - تغییر اطلاعات کسب و کار read_financial - دسترسی به اطلاعات مالی write_financial - تغییر اطلاعات مالی read_contacts - دسترسی به لیست مخاطبین write_contacts - تغییر لیست مخاطبین read_documents - دسترسی به اسناد write_documents - تغییر اسناد admin_access - دسترسی مدیریتی ``` ### 4. مدیریت برنامه‌ها #### کارت برنامه: - **وضعیت:** فعال/غیرفعال با رنگ‌بندی - **Client ID:** شناسه یکتا برنامه - **تاریخ ایجاد:** تاریخ ثبت برنامه - **توضیحات:** شرح برنامه #### عملیات موجود: ##### دکمه‌های اصلی: - **ویرایش:** تغییر اطلاعات برنامه - **آمار:** مشاهده آمار استفاده - **فعال/غیرفعال:** تغییر وضعیت برنامه ##### منوی سه نقطه: - **بازسازی کلید:** تولید Client Secret جدید - **لغو توکن‌ها:** لغو تمام توکن‌های فعال - **حذف:** حذف کامل برنامه ### 5. اطلاعات امنیتی پس از ایجاد برنامه، اطلاعات زیر نمایش داده می‌شود: ``` Client ID: mL0qT1fkIL6MCJfxIPAh7nM2cQ7ykxEy Client Secret: goM7latD9akY83z2O2e9IIEYED3Re6sRMd36f5cUSYHm389PPSqYbFHSX8GtQ9H1 ``` ⚠️ **هشدار:** این اطلاعات را در جای امنی ذخیره کنید! --- ## 👤 بخش کاربری ### 1. صفحه مجوزدهی هنگام اتصال برنامه خارجی، کاربر به صفحه زیر هدایت می‌شود: ``` ┌─────────────────────────────────────┐ │ مجوزدهی OAuth │ ├─────────────────────────────────────┤ │ │ │ [آیکون برنامه] نام برنامه │ │ توضیحات برنامه... │ │ │ │ این برنامه درخواست دسترسی به: │ │ ✓ خواندن اطلاعات پروفایل │ │ ✓ خواندن اطلاعات کسب و کار │ │ │ │ [لغو] [تأیید] │ └─────────────────────────────────────┘ ``` ### 2. اطلاعات نمایش داده شده: - **نام و لوگوی برنامه** - **توضیحات برنامه** - **محدوده‌های دسترسی درخواستی** - **دکمه‌های تأیید/لغو** ### 3. تصمیم کاربر: - **تأیید:** ادامه فرآیند OAuth - **لغو:** بازگشت به برنامه اصلی --- ## 📡 API Documentation ### Base URL ``` https://your-domain.com/oauth ``` ### 1. Authorization Endpoint #### درخواست مجوز: ```http GET /oauth/authorize ``` #### پارامترهای مورد نیاز: ```javascript { "response_type": "code", "client_id": "mL0qT1fkIL6MCJfxIPAh7nM2cQ7ykxEy", "redirect_uri": "https://your-app.com/callback", "scope": "read_profile read_business", "state": "random_string_for_csrf" } ``` #### پاسخ موفق: ```http HTTP/1.1 302 Found Location: https://your-app.com/callback?code=AUTHORIZATION_CODE&state=random_string ``` ### 2. Token Endpoint #### درخواست Access Token: ```http POST /oauth/token Content-Type: application/x-www-form-urlencoded ``` #### پارامترهای مورد نیاز: ```javascript { "grant_type": "authorization_code", "client_id": "mL0qT1fkIL6MCJfxIPAh7nM2cQ7ykxEy", "client_secret": "goM7latD9akY83z2O2e9IIEYED3Re6sRMd36f5cUSYHm389PPSqYbFHSX8GtQ9H1", "code": "AUTHORIZATION_CODE", "redirect_uri": "https://your-app.com/callback" } ``` #### پاسخ موفق: ```json { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "def50200...", "scope": "read_profile read_business" } ``` ### 3. User Info Endpoint #### درخواست اطلاعات کاربر: ```http GET /oauth/userinfo Authorization: Bearer ACCESS_TOKEN ``` #### پاسخ موفق: ```json { "id": 123, "email": "user@example.com", "name": "نام کاربر", "profile": { "phone": "+989123456789", "address": "تهران، ایران" } } ``` ### 4. Refresh Token Endpoint #### تمدید Access Token: ```http POST /oauth/token Content-Type: application/x-www-form-urlencoded ``` #### پارامترهای مورد نیاز: ```javascript { "grant_type": "refresh_token", "client_id": "mL0qT1fkIL6MCJfxIPAh7nM2cQ7ykxEy", "client_secret": "goM7latD9akY83z2O2e9IIEYED3Re6sRMd36f5cUSYHm389PPSqYbFHSX8GtQ9H1", "refresh_token": "def50200..." } ``` ### 5. Revoke Endpoint #### لغو Token: ```http POST /oauth/revoke Authorization: Bearer ACCESS_TOKEN ``` #### پارامترهای مورد نیاز: ```javascript { "token": "ACCESS_TOKEN_OR_REFRESH_TOKEN" } ``` ### 6. Discovery Endpoint #### دریافت اطلاعات OAuth Server: ```http GET /.well-known/oauth-authorization-server ``` #### پاسخ: ```json { "issuer": "https://hesabix.ir", "authorization_endpoint": "https://hesabix.ir/oauth/authorize", "token_endpoint": "https://hesabix.ir/oauth/token", "userinfo_endpoint": "https://hesabix.ir/oauth/userinfo", "revocation_endpoint": "https://hesabix.ir/oauth/revoke", "response_types_supported": ["code"], "grant_types_supported": ["authorization_code", "refresh_token"], "token_endpoint_auth_methods_supported": ["client_secret_post"], "scopes_supported": [ "read_profile", "write_profile", "read_business", "write_business", "read_financial", "write_financial", "read_contacts", "write_contacts", "read_documents", "write_documents", "admin_access" ] } ``` --- ## 🔗 نحوه اتصال ### 1. ثبت برنامه ابتدا برنامه خود را در بخش مدیریت ثبت کنید: ```javascript // اطلاعات مورد نیاز const appInfo = { name: "My Application", description: "توضیح برنامه من", website: "https://myapp.com", redirectUri: "https://myapp.com/oauth/callback", allowedScopes: ["read_profile", "read_business"], ipWhitelist: ["192.168.1.0/24"], // اختیاری rateLimit: 1000 }; ``` ### 2. پیاده‌سازی OAuth Flow #### مرحله 1: درخواست مجوز ```javascript function initiateOAuth() { const params = new URLSearchParams({ response_type: 'code', client_id: 'YOUR_CLIENT_ID', redirect_uri: 'https://myapp.com/oauth/callback', scope: 'read_profile read_business', state: generateRandomString() }); window.location.href = `https://hesabix.com/oauth/authorize?${params}`; } ``` #### مرحله 2: دریافت Authorization Code ```javascript // در callback URL function handleCallback() { const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); const state = urlParams.get('state'); if (code && state) { exchangeCodeForToken(code); } } ``` #### مرحله 3: تبدیل Code به Token ```javascript async function exchangeCodeForToken(code) { const response = await fetch('https://hesabix.com/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'authorization_code', client_id: 'YOUR_CLIENT_ID', client_secret: 'YOUR_CLIENT_SECRET', code: code, redirect_uri: 'https://myapp.com/oauth/callback' }) }); const tokenData = await response.json(); // ذخیره token localStorage.setItem('access_token', tokenData.access_token); localStorage.setItem('refresh_token', tokenData.refresh_token); } ``` #### مرحله 4: استفاده از Access Token ```javascript async function getUserInfo() { const response = await fetch('https://hesabix.com/oauth/userinfo', { headers: { 'Authorization': `Bearer ${localStorage.getItem('access_token')}` } }); const userData = await response.json(); return userData; } ``` ### 3. مدیریت Token #### تمدید Access Token: ```javascript async function refreshAccessToken() { const response = await fetch('https://hesabix.com/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'refresh_token', client_id: 'YOUR_CLIENT_ID', client_secret: 'YOUR_CLIENT_SECRET', refresh_token: localStorage.getItem('refresh_token') }) }); const tokenData = await response.json(); localStorage.setItem('access_token', tokenData.access_token); localStorage.setItem('refresh_token', tokenData.refresh_token); } ``` #### لغو Token: ```javascript async function revokeToken() { await fetch('https://hesabix.com/oauth/revoke', { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('access_token')}` }, body: JSON.stringify({ token: localStorage.getItem('access_token') }) }); localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); } ``` --- ## 🔒 امنیت ### 1. محدودیت‌های IP ```javascript // بررسی IP در backend function checkIpWhitelist($clientIp, $whitelist) { if (empty($whitelist)) { return true; // همه IP ها مجاز } foreach ($whitelist as $allowedIp) { if (ipInRange($clientIp, $allowedIp)) { return true; } } return false; } ``` ### 2. محدودیت‌های Scope ```javascript // بررسی دسترسی در backend function checkScope($requestedScope, $allowedScopes) { $requestedScopes = explode(' ', $requestedScope); foreach ($requestedScopes as $scope) { if (!in_array($scope, $allowedScopes)) { return false; } } return true; } ``` ### 3. Rate Limiting ```javascript // محدودیت درخواست function checkRateLimit($clientId, $limit) { $requests = getRequestCount($clientId, '1 hour'); return $requests < $limit; } ``` ### 4. Token Security - **Access Token:** عمر 1 ساعت - **Refresh Token:** عمر 30 روز - **JWT Signature:** امضای دیجیتال - **Token Revocation:** امکان لغو فوری ### 5. نکات امنیتی مهم 1. **HTTPS اجباری:** تمام ارتباطات باید روی HTTPS باشد 2. **State Parameter:** همیشه از state parameter استفاده کنید 3. **Client Secret:** Client Secret را در کد سمت کلاینت قرار ندهید 4. **Token Storage:** توکن‌ها را در جای امنی ذخیره کنید 5. **Scope Validation:** همیشه scope ها را بررسی کنید --- ## 💻 مثال‌های عملی ### مثال 1: اپلیکیشن وب ```html OAuth Example ``` ### مثال 2: اپلیکیشن موبایل ```javascript // React Native Example import { Linking } from 'react-native'; class OAuthManager { constructor() { this.clientId = 'YOUR_CLIENT_ID'; this.redirectUri = 'myapp://oauth/callback'; } async login() { const authUrl = `https://hesabix.com/oauth/authorize?` + `response_type=code&` + `client_id=${this.clientId}&` + `redirect_uri=${encodeURIComponent(this.redirectUri)}&` + `scope=read_profile&` + `state=${Math.random().toString(36)}`; await Linking.openURL(authUrl); } async handleCallback(url) { const code = url.match(/code=([^&]*)/)?.[1]; if (code) { await this.exchangeCodeForToken(code); } } async exchangeCodeForToken(code) { const response = await fetch('https://hesabix.com/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', client_id: this.clientId, client_secret: 'YOUR_CLIENT_SECRET', code: code, redirect_uri: this.redirectUri }) }); const data = await response.json(); await AsyncStorage.setItem('access_token', data.access_token); } } ``` ### مثال 3: اپلیکیشن سرور ```python # Python Flask Example from flask import Flask, request, redirect, session import requests app = Flask(__name__) app.secret_key = 'your-secret-key' CLIENT_ID = 'YOUR_CLIENT_ID' CLIENT_SECRET = 'YOUR_CLIENT_SECRET' REDIRECT_URI = 'https://myapp.com/oauth/callback' @app.route('/login') def login(): auth_url = f'https://hesabix.com/oauth/authorize?' + \ f'response_type=code&' + \ f'client_id={CLIENT_ID}&' + \ f'redirect_uri={REDIRECT_URI}&' + \ f'scope=read_profile' return redirect(auth_url) @app.route('/oauth/callback') def callback(): code = request.args.get('code') # تبدیل code به token token_response = requests.post('https://hesabix.com/oauth/token', data={ 'grant_type': 'authorization_code', 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, 'code': code, 'redirect_uri': REDIRECT_URI }) token_data = token_response.json() session['access_token'] = token_data['access_token'] return 'Login successful!' @app.route('/user-info') def user_info(): headers = {'Authorization': f"Bearer {session['access_token']}"} response = requests.get('https://hesabix.com/oauth/userinfo', headers=headers) return response.json() ``` ### مثال 4: کلاس کامل JavaScript ```javascript class HesabixOAuth { constructor(clientId, redirectUri) { this.clientId = clientId; this.redirectUri = redirectUri; this.baseUrl = 'https://hesabix.com'; } // شروع فرآیند OAuth authorize(scopes = ['read_profile']) { const state = this.generateState(); const params = new URLSearchParams({ client_id: this.clientId, redirect_uri: this.redirectUri, response_type: 'code', scope: scopes.join(' '), state: state }); localStorage.setItem('oauth_state', state); window.location.href = `${this.baseUrl}/oauth/authorize?${params}`; } // پردازش callback async handleCallback() { const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); const state = urlParams.get('state'); const savedState = localStorage.getItem('oauth_state'); if (state !== savedState) { throw new Error('State mismatch'); } if (!code) { throw new Error('Authorization code not found'); } const tokens = await this.exchangeCode(code); localStorage.setItem('access_token', tokens.access_token); localStorage.setItem('refresh_token', tokens.refresh_token); return tokens; } // مبادله کد با توکن async exchangeCode(code) { const response = await fetch(`${this.baseUrl}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', client_id: this.clientId, client_secret: 'YOUR_CLIENT_SECRET', // در سرور ذخیره شود code: code, redirect_uri: this.redirectUri }) }); if (!response.ok) { throw new Error('Token exchange failed'); } return await response.json(); } // دریافت اطلاعات کاربر async getUserInfo() { const token = localStorage.getItem('access_token'); const response = await fetch(`${this.baseUrl}/oauth/userinfo`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error('Failed to get user info'); } return await response.json(); } // تمدید توکن async refreshToken() { const refreshToken = localStorage.getItem('refresh_token'); const response = await fetch(`${this.baseUrl}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'refresh_token', client_id: this.clientId, client_secret: 'YOUR_CLIENT_SECRET', refresh_token: refreshToken }) }); if (!response.ok) { throw new Error('Token refresh failed'); } const tokens = await response.json(); localStorage.setItem('access_token', tokens.access_token); localStorage.setItem('refresh_token', tokens.refresh_token); return tokens; } generateState() { return Math.random().toString(36).substring(2, 15); } } // استفاده const oauth = new HesabixOAuth('YOUR_CLIENT_ID', 'https://yourapp.com/callback'); // شروع OAuth oauth.authorize(['read_profile', 'read_business']); // در صفحه callback oauth.handleCallback().then(tokens => { console.log('OAuth successful:', tokens); // دریافت اطلاعات کاربر return oauth.getUserInfo(); }).then(userInfo => { console.log('User info:', userInfo); }); ``` --- ## 🔧 عیب‌یابی ### خطاهای رایج: #### 1. invalid_client **علت:** Client ID یا Secret اشتباه **راه حل:** بررسی صحت Client ID و Secret #### 2. invalid_grant **علت:** Authorization Code نامعتبر یا منقضی شده **راه حل:** درخواست مجدد Authorization Code #### 3. invalid_scope **علت:** Scope درخواستی مجاز نیست **راه حل:** بررسی Scope های مجاز در پنل مدیریت #### 4. access_denied **علت:** کاربر دسترسی را لغو کرده **راه حل:** درخواست مجدد از کاربر #### 5. server_error **علت:** خطای سرور **راه حل:** بررسی لاگ‌های سرور ### کدهای خطا: ```javascript const errorCodes = { 'invalid_request': 'درخواست نامعتبر', 'invalid_client': 'Client ID یا Secret اشتباه', 'invalid_grant': 'کد مجوز نامعتبر', 'unauthorized_client': 'برنامه مجاز نیست', 'unsupported_grant_type': 'نوع grant پشتیبانی نمی‌شود', 'invalid_scope': 'Scope نامعتبر', 'access_denied': 'دسترسی رد شد', 'server_error': 'خطای سرور', 'temporarily_unavailable': 'سرویس موقتاً در دسترس نیست' }; ``` ### لاگ‌ها: ```bash # مشاهده لاگ‌های OAuth tail -f /var/log/hesabix/oauth.log # پاک کردن کش php bin/console cache:clear # بررسی وضعیت سرور php bin/console debug:router | grep oauth ``` --- ## 📞 پشتیبانی ### اطلاعات تماس: - **ایمیل:** support@hesabix.com - **تلفن:** 021-12345678 - **ساعات کاری:** شنبه تا چهارشنبه، 9 صبح تا 6 عصر - **تلگرام:** @hesabix_support ### سوالات متداول: #### Q: چگونه Client Secret را تغییر دهم؟ A: در پنل مدیریت، روی منوی سه نقطه برنامه کلیک کرده و "بازسازی کلید" را انتخاب کنید. #### Q: آیا می‌توانم چندین Redirect URI داشته باشم؟ A: خیر، هر برنامه فقط یک Redirect URI می‌تواند داشته باشد. #### Q: توکن‌ها چه مدت اعتبار دارند؟ A: Access Token 1 ساعت و Refresh Token 30 روز اعتبار دارد. #### Q: چگونه IP Whitelist را تنظیم کنم؟ A: در زمان ایجاد یا ویرایش برنامه، IP های مجاز را اضافه کنید. اگر خالی باشد، از هر IP مجاز است. #### Q: آیا می‌توانم Scope ها را بعداً تغییر دهم؟ A: بله، در بخش ویرایش برنامه می‌توانید Scope ها را تغییر دهید. ### گزارش باگ: برای گزارش باگ، لطفاً اطلاعات زیر را ارسال کنید: 1. **نوع خطا:** کد خطا و پیام 2. **مراحل تولید:** مراحل دقیق تولید خطا 3. **اطلاعات برنامه:** Client ID و نام برنامه 4. **لاگ‌ها:** لاگ‌های مربوطه 5. **مرورگر/سیستم عامل:** اطلاعات محیط اجرا --- ## 📚 منابع بیشتر - [RFC 6749 - OAuth 2.0](https://tools.ietf.org/html/rfc6749) - [OAuth 2.0 Security Best Practices](https://tools.ietf.org/html/draft-ietf-oauth-security-topics) - [OpenID Connect](https://openid.net/connect/) - [OAuth 2.0 Authorization Code Flow](https://auth0.com/docs/protocols/oauth2/oauth2-authorization-code-flow) --- ## 📋 چک‌لیست پیاده‌سازی ### قبل از شروع: - [ ] برنامه در پنل مدیریت ثبت شده - [ ] Client ID و Secret دریافت شده - [ ] Redirect URI تنظیم شده - [ ] Scope های مورد نیاز تعیین شده - [ ] IP Whitelist تنظیم شده (در صورت نیاز) ### پیاده‌سازی: - [ ] Authorization Request پیاده‌سازی شده - [ ] Callback Handler پیاده‌سازی شده - [ ] Token Exchange پیاده‌سازی شده - [ ] Error Handling پیاده‌سازی شده - [ ] Token Storage پیاده‌سازی شده ### تست: - [ ] Authorization Flow تست شده - [ ] Token Exchange تست شده - [ ] User Info API تست شده - [ ] Error Scenarios تست شده - [ ] Security Features تست شده --- **نسخه مستندات:** 1.0 **تاریخ آخرین به‌روزرسانی:** 2025-08-16 **وضعیت:** فعال ✅ **توسعه‌دهنده:** Hesabix Team