Add OAuth_Complete_Documentation
parent
1243b4d96d
commit
d5340d3e6c
957
OAuth_Complete_Documentation.md
Normal file
957
OAuth_Complete_Documentation.md
Normal file
|
@ -0,0 +1,957 @@
|
|||
# مستندات کامل سیستم 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
|
Loading…
Reference in a new issue