Auth Push آزمایشی — راهنمای توسعه و اتصال
آموزش قدمبهقدم اتصال صفحه ورود به Pushfa.Auth؛ از گرفتن شناسه دستگاه تا انتظار برای تأیید، بررسی کد و رفتن به روش پیامک.
هر بخش از سیستم چه کاری انجام میدهد؟
برای سادهماندن پیادهسازی، کارها را میان مرورگر، SDK پوشفا و سرور سایت جدا کنید. مرورگر فقط رابط ورود را نشان میدهد. SDK وضعیت درخواست را بررسی میکند. سرور سایت کاربر را میشناسد و تصمیم نهایی ورود را میگیرد.
۱. راهنمای کاربر
Auth Push چیست، چگونه فعال میشود و کاربر چطور ورود را تأیید میکند.
۲. راهنمای توسعهدهندگان
روش اتصال صفحه ورود، بررسی وضعیت، کد Push و بازگشت به پیامک.
۳. وبسرویسها
درخواستهای سروری برای ثبت دستگاه، ساخت درخواست ورود و تأیید نهایی.
| بخش | کار اصلی | چه چیزی نباید در آن باشد؟ |
|---|---|---|
| مرورگر یا Frontend | گرفتن شناسه دستگاه، نمایش انتظار و فرستادن نتیجه برای سرور خود سایت | کلید خصوصی یا امکان انتخاب هویت واقعی کاربر |
| Pushfa SDK | بررسی وضعیت، کنترل کد ششرقمی و لغو درخواست | ساخت نشست ورود سایت |
| سرور سایت | پیداکردن کاربر، ساخت درخواست، بررسی مجوز نهایی و ورود کاربر | فرستادن کلید خصوصی برای مرورگر |
چند واژه فنی به زبان ساده
| واژه | معنی ساده |
|---|---|
| Subscriber ID | شناسه پایدار یک مرورگر یا دستگاه در پوشفا |
| Challenge | درخواست ورود کوتاهمدتی که منتظر تأیید است |
| client_token | کلید موقت مرورگر برای دیدن وضعیت یا لغو همان درخواست |
| Grant | مجوز یکبارمصرفی که پس از تأیید کاربر صادر میشود |
| Fallback | رفتن به روش جایگزین، مانند پیامک، وقتی Push قابل استفاده نیست |
۱. گرفتن شناسه دستگاه در مرورگر
بعد از اینکه کاربر به روش عادی وارد سایت شد و اعلانها را فعال کرد، شناسه پایدار دستگاه او را از SDK بگیرید و برای سرور خودتان بفرستید. سرور باید کاربر را از روی نشست معتبر بشناسد؛ مرورگر نباید بتواند شناسه کاربر دیگری را به درخواست اضافه کند.
// این کد فقط بعد از ورود معتبر کاربر اجرا شود.
await window.Pushfa.requestPermission();
const subscriberId = await window.Pushfa.Auth.prepare();
await fetch('/account/trusted-device', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ subscriber_id: subscriberId })
});
مسیرهای داخلی مورد نیاز در سایت شما
مرورگر نباید مستقیماً وبسرویسهایی را صدا بزند که به کلید خصوصی نیاز دارند. سه مسیر داخلی در سرور خودتان بسازید: یکی برای شروع درخواست، یکی برای تکمیل ورود و دیگری برای شروع پیامک. ارتباط هر درخواست با کاربر باید فقط در سرور نگهداری شود.
| مسیر نمونه | چه چیزی از مرورگر میگیرد؟ | چه چیزی برمیگرداند؟ |
|---|---|---|
| POST /auth/push/start | username، requester subscriber و mode | challenge_id، client_token، expires_at یا fallback_required |
| POST /auth/push/complete | challenge_id و Grant | نتیجه ورود و redirect امن |
| POST /auth/sms/start | اطلاعات لازم fallback | شروع OTP پیامکی پس از لغو Push Challenge |
۴. انتظار برای تأیید روی دستگاه دیگر
مرورگر شناسه درخواست، توکن کوتاهمدت و زمان پایان را از سرور سایت میگیرد. SDK هر چند ثانیه وضعیت را بررسی میکند. اگر کاربر درخواست را تأیید کند، یک مجوز یکبارمصرف به نام Grant برگردانده میشود تا سرور ورود را کامل کند.
async function startAuthPushLogin() {
const requesterSubscriberId = await window.Pushfa.Auth.prepare();
const startResponse = await fetch('/auth/push/start', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({
username: document.querySelector('#username').value,
subscriber_id: requesterSubscriberId,
mode: 'approval'
})
});
if (startResponse.status === 422) {
return fallbackToSms(null);
}
const challenge = await startResponse.json();
const result = await window.Pushfa.Auth.wait({
challengeId: challenge.challenge_id,
clientToken: challenge.client_token,
expiresAt: challenge.expires_at,
interval: 2000
});
if (result.status === 'approved' && result.grant) {
return completeLogin(challenge.challenge_id, result.grant);
}
return fallbackToSms(challenge);
}
۵. بررسی کد ششرقمی Push
در روش otp، یک کد ششرقمی در اعلان دستگاه مورد اعتماد نمایش داده میشود. کاربر کد را در صفحه ورود جدید وارد میکند. اگر کد درست باشد، SDK همان مجوز یکبارمصرف لازم برای تکمیل ورود را برمیگرداند.
const challenge = await startPushChallenge({ mode: 'otp' });
try {
const result = await window.Pushfa.Auth.verifyOtp({
challengeId: challenge.challenge_id,
clientToken: challenge.client_token
}, document.querySelector('#push-otp').value);
if (result.status === 'approved' && result.grant) {
await completeLogin(challenge.challenge_id, result.grant);
}
} catch (error) {
// کد نادرست، Challenge بسته یا تعداد تلاش تمام شده است.
showOtpError(error.message);
}
وضعیتهای Challenge
| status | معنا | اقدام پیشنهادی |
|---|---|---|
| pending | منتظر OTP یا پاسخ دستگاه دیگر | Polling را تا expires_at ادامه دهید و گزینه پیامک را در دسترس نگه دارید. |
| approved | تأیید انجام شده و Grant آماده است | Grant را فوراً به Backend خودتان بدهید و مصرف کنید. |
| rejected | کاربر درخواست را رد کرده، ارسال شکست خورده یا تلاش OTP تمام شده است | Challenge را دوباره استفاده نکنید؛ پیامک یا شروع مجدد را پیشنهاد دهید. |
| expired | زمان Challenge پایان یافته است | Challenge جدید بسازید یا fallback را اجرا کنید. |
| cancelled | درخواستکننده، مدیر، سرویس یا دستگاه آن را لغو کرده است | هیچ Grant یا لینک قبلی معتبر نیست. |
| consumed | Grant قبلاً مصرف شده است | مصرف دوباره رد میشود؛ Session قبلی را بررسی کنید. |
رفتن به روش پیامک و لغو درخواست قبلی
وقتی کاربر منتظر تأیید است، دکمه «ورود با پیامک» را در دسترس نگه دارید. پیش از ارسال پیامک، درخواست Push را با Pushfa.Auth.cancel لغو کنید تا تأیید دیرهنگام باعث ورود همزمان و ناخواسته نشود.
async function fallbackToSms(challenge) {
if (challenge) {
try {
await window.Pushfa.Auth.cancel({
challengeId: challenge.challenge_id,
clientToken: challenge.client_token
});
} catch (_) {
// Challenge ممکن است همزمان منقضی یا بسته شده باشد.
}
}
return fetch('/auth/sms/start', { method: 'POST', credentials: 'same-origin' });
}
نکات پیادهسازی رابط ورود
| کنترل | قاعده |
|---|---|
| client_token | در URL، analytics، log یا localStorage دائمی ذخیره نشود. |
| Polling | فقط تا expires_at ادامه یابد و هنگام ترک صفحه یا انتخاب fallback متوقف شود. |
| Fallback | دکمه پیامک در pending مخفی نشود و پیش از شروع آن Challenge قبلی cancel شود. |
| وضعیتها | برای pending، rejected، expired و cancelled پیام و اقدام بعدی متفاوت نمایش داده شود. |
| Grant | فقط به Backend همان Origin ارسال شود و هرگز مستقیماً از Browser مصرف نشود. |