forked from morrning/hesabixCore
957 lines
27 KiB
Markdown
957 lines
27 KiB
Markdown
# مستندات کامل سیستم 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
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>OAuth Example</title>
|
||
</head>
|
||
<body>
|
||
<button onclick="login()">ورود با Hesabix</button>
|
||
|
||
<script>
|
||
const CLIENT_ID = 'YOUR_CLIENT_ID';
|
||
const REDIRECT_URI = 'https://myapp.com/callback';
|
||
|
||
function login() {
|
||
const params = new URLSearchParams({
|
||
response_type: 'code',
|
||
client_id: CLIENT_ID,
|
||
redirect_uri: REDIRECT_URI,
|
||
scope: 'read_profile',
|
||
state: Math.random().toString(36)
|
||
});
|
||
|
||
window.location.href = `https://hesabix.com/oauth/authorize?${params}`;
|
||
}
|
||
|
||
// بررسی callback
|
||
if (window.location.search.includes('code=')) {
|
||
handleCallback();
|
||
}
|
||
|
||
async function handleCallback() {
|
||
const code = new URLSearchParams(window.location.search).get('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: CLIENT_ID,
|
||
client_secret: 'YOUR_CLIENT_SECRET',
|
||
code: code,
|
||
redirect_uri: REDIRECT_URI
|
||
})
|
||
});
|
||
|
||
const data = await response.json();
|
||
console.log('Token received:', data);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
### مثال 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 |